try:
from rdflib.graph import Graph
from rdflib.term import URIRef, BNode, Literal, Variable
except ImportError:
from rdflib.Graph import Graph
from rdflib import URIRef, BNode, Literal, Variable
from rdflib.store import Store, VALID_STORE, NO_STORE
from traceback import format_exc
import pyodbc
__all__ = ['Virtuoso', 'resolve']
log = __import__("logging").getLogger(__name__)
[docs]class Virtuoso(Store):
context_aware = True
transaction_aware = True
def open(self, dsn):
try:
self._connection = pyodbc.connect(dsn)
self._cursor = self._connection.cursor()
log.info("Virtuoso Store Connected: %s" % dsn)
return VALID_STORE
except:
log.error("Virtuoso Connection Failed:\n%s" % format_exc())
return NO_STORE
def close(self, commit_pending_transaction=False):
if commit_pending_transaction:
self.commit()
else:
self.rollback()
self._connection.close()
[docs] def query(self, q):
return self._cursor.execute(str(q))
[docs] def sparql_query(self, q):
return self.query(u"SPARQL " + q)
[docs] def commit(self):
self.query("COMMIT WORK")
[docs] def rollback(self):
self.query("ROLLBACK WORK")
def contexts(self, statement=None):
if statement is None:
q = (u'SELECT DISTINCT __ro2sq(G) FROM RDF_QUAD')
else:
q = (u'SELECT DISTINCT ?g WHERE '
u'{ GRAPH ?g { %(S)s %(P)s %(O)s } }')
q = _query_bindings(statement)
for uri, in self.query(q):
yield Graph(self, identifier=URIRef(uri))
def triples(self, statement, context=None):
query_bindings = _query_bindings(statement, context)
for k,v in query_bindings.items():
if v.startswith('?') or v.startswith('$'):
query_bindings[k+"v"]=v
else:
query_bindings[k+"v"]="(%s) AS %s" % (v, Variable(k).n3())
q = (u'SPARQL define output:valmode "LONG" '
u'SELECT DISTINCT %(Sv)s %(Pv)s %(Ov)s %(Gv)s '
u'WHERE { GRAPH %(G)s { %(S)s %(P)s %(O)s } }')
q = q % query_bindings
log.debug(q)
resolver = self._connection.cursor()
for result in self.query(q):
s,p,o,g = [resolve(resolver, x) for x in result]
yield (s,p,o), g
resolver.close()
def add(self, statement, context=None, quoted=False):
assert not quoted, "No quoted graph support in Virtuoso store yet, sorry"
query_bindings = _query_bindings(statement, context)
q = u'SPARQL INSERT '
if context is not None:
q += u'INTO GRAPH %(G)s ' % query_bindings
q += u'{ %(S)s %(P)s %(O)s }' % query_bindings
log.debug(q)
self.query(q)
def remove(self, statement, context=None, quoted=False):
assert not quoted, "No quoted graph support in Virtuoso store yet, sorry"
query_bindings = _query_bindings(statement, context)
q = u'SPARQL DELETE '
if context is not None:
q += u'FROM GRAPH %(G)s ' % query_bindings
q += u'{ %(S)s %(P)s %(O)s }' % query_bindings
log.debug(q)
self.query(q)
def _bnode_to_nodeid(bnode):
from string import ascii_letters
iri = bnode
for c in bnode[1:]:
if c in ascii_letters:
# from rdflib
iri = "b" + "".join(str(ord(x)-38) for x in bnode)
return URIRef("nodeID://%s" % iri)
def _nodeid_to_bnode(iri):
from string import digits
iri = iri[9:] # strip off "nodeID://"
bnode = iri
if len(iri) == 19:
# assume we made it...
ones, tens = iri[1::2], iri[2::2]
chars = [x+y for x,y in zip(ones, tens)]
bnode = "".join(str(chr(int(x)+38)) for x in chars)
return BNode(bnode)
[docs]def resolve(resolver, (value, dvtype, flag, lang, dtype)):
"""
Takes the Virtuoso representation of an RDF node and returns
an appropriate instance of :class:`rdflib.term.Node`.
:param resolver: an cursor that can be used for database
queries necessary to resolve the value
:param (value, dvtype, flag, lang, dtype): the tuple returned
by :module:`pyodbc` in case of a SPASQL query.
"""
# if dvtype in (129, 211):
# print "XXX", dtype, value, dtype
if dvtype == pyodbc.VIRTUOSO_DV_IRI_ID:
q = (u'SELECT __ro2sq(%s)' % value)
resolver.execute(str(q))
iri, = resolver.fetchone()
if iri.startswith("nodeID://"):
return _nodeid_to_bnode(iri)
return URIRef(iri)
if dvtype == pyodbc.VIRTUOSO_DV_RDF:
return Literal(value, lang=lang or None, datatype=dtype or None)
if dvtype == pyodbc.VIRTUOSO_DV_STRING:
return Literal(value)
if dvtype == pyodbc.VIRTUOSO_DV_LONG_INT:
return Literal(int(value))
if dvtype == pyodbc.VIRTUOSO_DV_DATETIME:
return Literal(value.replace(" ", "T"),
datatype=URIRef("http://www.w3.org/2001/XMLSchema#dateTime"))
if dvtype == pyodbc.VIRTUOSO_DV_DATE:
return Literal(value, datatype=URIRef("http://www.w3.org/2001/XMLSchema#date"))
log.warn("Unhandled SPASQL DV type: %d for %s" % (dvtype, value))
return Literal(value)
def _query_bindings((s,p,o), g=None):
if isinstance(g, Graph):
g = g.identifier
if s is None: s = Variable("S")
if p is None: p = Variable("P")
if o is None: o = Variable("O")
if g is None: g = Variable("G")
if isinstance(s, BNode):
s = _bnode_to_nodeid(s)
if isinstance(p, BNode):
p = _bnode_to_nodeid(p)
if isinstance(o, BNode):
o = _bnode_to_nodeid(o)
if isinstance(g, BNode):
g = _bnode_to_nodeid(g)
return dict(
zip("SPOG", [x.n3() for x in (s,p,o,g)])
)