Commits

Marc-Antoine Parent committed 2341dde

adapt to librdf 3.2, virtuoso tip as of today, pyodbc 2.1.12 with patch

Comments (0)

Files changed (6)

pyodbc-2.1.12.patch

+Only in pyodbc-2.1.12v: PKG-INFO
+Only in pyodbc-2.1.12v: build
+Only in pyodbc-2.1.12v: dist
+Only in pyodbc-2.1.12v: pyodbc.conf
+Only in pyodbc-2.1.12v: pyodbc.egg-info
+diff -c -r pyodbc-2.1.12/src/cnxninfo.cpp pyodbc-2.1.12v/src/cnxninfo.cpp
+*** pyodbc-2.1.12/src/cnxninfo.cpp	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/src/cnxninfo.cpp	2011-12-19 15:52:04.000000000 -0500
+***************
+*** 112,118 ****
+      HSTMT hstmt = 0;
+      if (SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_STMT, cnxn->hdbc, &hstmt)))
+      {
+!         SQLINTEGER columnsize;
+          if (SQL_SUCCEEDED(SQLGetTypeInfo(hstmt, SQL_TYPE_TIMESTAMP)) && SQL_SUCCEEDED(SQLFetch(hstmt)))
+          {
+              if (SQL_SUCCEEDED(SQLGetData(hstmt, 3, SQL_INTEGER, &columnsize, sizeof(columnsize), 0)))
+--- 112,118 ----
+      HSTMT hstmt = 0;
+      if (SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_STMT, cnxn->hdbc, &hstmt)))
+      {
+!         ULONG columnsize;
+          if (SQL_SUCCEEDED(SQLGetTypeInfo(hstmt, SQL_TYPE_TIMESTAMP)) && SQL_SUCCEEDED(SQLFetch(hstmt)))
+          {
+              if (SQL_SUCCEEDED(SQLGetData(hstmt, 3, SQL_INTEGER, &columnsize, sizeof(columnsize), 0)))
+diff -c -r pyodbc-2.1.12/src/connection.cpp pyodbc-2.1.12v/src/connection.cpp
+*** pyodbc-2.1.12/src/connection.cpp	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/src/connection.cpp	2011-12-19 13:45:06.000000000 -0500
+***************
+*** 17,22 ****
+--- 17,23 ----
+  #include "wrapper.h"
+  #include "cnxninfo.h"
+  #include "sqlwchar.h"
++ #include "virtuoso.h"
+  
+  static char connection_doc[] =
+      "Connection objects manage connections to the database.\n"
+***************
+*** 189,194 ****
+--- 190,196 ----
+      cnxn->conv_count      = 0;
+      cnxn->conv_types      = 0;
+      cnxn->conv_funcs      = 0;
++     cnxn->virtuoso        = isVirtuoso(hdbc);
+  
+      //
+      // Initialize autocommit mode.
+***************
+*** 747,753 ****
+          PyErr_SetString(PyExc_TypeError, "Cannot delete the timeout attribute.");
+          return -1;
+      }
+!     int timeout = PyInt_AsLong(value);
+      if (timeout == -1 && PyErr_Occurred())
+          return -1;
+      if (timeout < 0)
+--- 749,755 ----
+          PyErr_SetString(PyExc_TypeError, "Cannot delete the timeout attribute.");
+          return -1;
+      }
+!     long timeout = PyInt_AsLong(value);
+      if (timeout == -1 && PyErr_Occurred())
+          return -1;
+      if (timeout < 0)
+diff -c -r pyodbc-2.1.12/src/connection.h pyodbc-2.1.12v/src/connection.h
+*** pyodbc-2.1.12/src/connection.h	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/src/connection.h	2011-12-19 13:45:11.000000000 -0500
+***************
+*** 44,56 ****
+      bool unicode_results;
+  
+      // The connection timeout in seconds.
+!     int timeout;
+  
+      // These are copied from cnxn info for performance and convenience.
+  
+      int varchar_maxlength;
+      int wvarchar_maxlength;
+      int binary_maxlength;
+  
+      // Output conversions.  Maps from SQL type in conv_types to the converter function in conv_funcs.
+      //
+--- 44,57 ----
+      bool unicode_results;
+  
+      // The connection timeout in seconds.
+!     long timeout;
+  
+      // These are copied from cnxn info for performance and convenience.
+  
+      int varchar_maxlength;
+      int wvarchar_maxlength;
+      int binary_maxlength;
++     bool virtuoso;
+  
+      // Output conversions.  Maps from SQL type in conv_types to the converter function in conv_funcs.
+      //
+diff -c -r pyodbc-2.1.12/src/cursor.cpp pyodbc-2.1.12v/src/cursor.cpp
+*** pyodbc-2.1.12/src/cursor.cpp	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/src/cursor.cpp	2011-12-19 13:40:30.000000000 -0500
+***************
+*** 24,29 ****
+--- 24,30 ----
+  #include "getdata.h"
+  #include "dbspecific.h"
+  #include "sqlwchar.h"
++ #include "virtuoso.h"
+  
+  enum
+  {
+***************
+*** 793,798 ****
+--- 794,801 ----
+  
+      FreeParameterData(cur);
+  
++     cur->spasql = (cur->cnxn->virtuoso && isSPASQL(pSql));
++ 
+      if (ret == SQL_NO_DATA)
+      {
+          // Example: A delete statement that did not delete anything.
+***************
+*** 1918,1927 ****
+      if (!cursor)
+          return 0;
+  
+!     SQLUINTEGER noscan = SQL_NOSCAN_OFF;
+      SQLRETURN ret;
+      Py_BEGIN_ALLOW_THREADS
+!     ret = SQLGetStmtAttr(cursor->hstmt, SQL_ATTR_NOSCAN, (SQLPOINTER)&noscan, sizeof(SQLUINTEGER), 0);
+      Py_END_ALLOW_THREADS
+  
+      if (!SQL_SUCCEEDED(ret))
+--- 1921,1930 ----
+      if (!cursor)
+          return 0;
+  
+!     ULONG noscan = SQL_NOSCAN_OFF;
+      SQLRETURN ret;
+      Py_BEGIN_ALLOW_THREADS
+!     ret = SQLGetStmtAttr(cursor->hstmt, SQL_ATTR_NOSCAN, (SQLPOINTER)&noscan, sizeof(ULONG), 0);
+      Py_END_ALLOW_THREADS
+  
+      if (!SQL_SUCCEEDED(ret))
+***************
+*** 1950,1956 ****
+          return 0;
+      }
+  
+!     SQLUINTEGER noscan = PyObject_IsTrue(value) ? SQL_NOSCAN_ON : SQL_NOSCAN_OFF;
+      SQLRETURN ret;
+      Py_BEGIN_ALLOW_THREADS
+      ret = SQLSetStmtAttr(cursor->hstmt, SQL_ATTR_NOSCAN, (SQLPOINTER)noscan, 0);
+--- 1953,1959 ----
+          return 0;
+      }
+  
+!     ULONG noscan = PyObject_IsTrue(value) ? SQL_NOSCAN_ON : SQL_NOSCAN_OFF;
+      SQLRETURN ret;
+      Py_BEGIN_ALLOW_THREADS
+      ret = SQLSetStmtAttr(cursor->hstmt, SQL_ATTR_NOSCAN, (SQLPOINTER)noscan, 0);
+diff -c -r pyodbc-2.1.12/src/cursor.h pyodbc-2.1.12v/src/cursor.h
+*** pyodbc-2.1.12/src/cursor.h	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/src/cursor.h	2011-12-19 11:52:50.000000000 -0500
+***************
+*** 119,124 ****
+--- 119,127 ----
+      // The Cursor.rowcount attribute from the DB API specification.
+      int rowcount;
+  
++     // is a SPASQL query on a virtuoso server, requires special datatype handling
++     bool spasql;
++ 
+      // A dictionary that maps from column name (PyString) to index into the result columns (PyInteger).  This is
+      // constructued during an execute and shared with each row (reference counted) to implement accessing results by
+      // column name.
+Only in pyodbc-2.1.12v/src: frag.h
+diff -c -r pyodbc-2.1.12/src/getdata.cpp pyodbc-2.1.12v/src/getdata.cpp
+*** pyodbc-2.1.12/src/getdata.cpp	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/src/getdata.cpp	2012-05-14 23:29:31.000000000 -0400
+***************
+*** 9,14 ****
+--- 9,15 ----
+  #include "errors.h"
+  #include "dbspecific.h"
+  #include "sqlwchar.h"
++ #include "virtuoso.h"
+  
+  void GetData_init()
+  {
+***************
+*** 214,220 ****
+          // We have allocated our own SQLWCHAR buffer and must now copy it to a Unicode object.
+          PyObject* result = PyUnicode_FromSQLWCHAR((const SQLWCHAR*)buffer, bytesUsed / element_size);
+          if (result == 0)
+!             return false;
+          pyodbc_free(buffer);
+          buffer = 0;
+          return result;
+--- 215,221 ----
+          // We have allocated our own SQLWCHAR buffer and must now copy it to a Unicode object.
+          PyObject* result = PyUnicode_FromSQLWCHAR((const SQLWCHAR*)buffer, bytesUsed / element_size);
+          if (result == 0)
+!             return NULL;
+          pyodbc_free(buffer);
+          buffer = 0;
+          return result;
+***************
+*** 594,599 ****
+--- 595,681 ----
+      return -1;
+  }
+  
++ static
++ PyObject *GetDataSPASQL(Cursor *cur, Py_ssize_t column)
++ {
++     // Return a tuple of information sufficient to glean the
++     // real underlying type in case of a Virtuoso SPASQL query
++     int dvtype, flag;
++     SQLHANDLE hdesc = SQL_NULL_HANDLE;
++     SQLRETURN ret;
++     SQLCHAR lang[0x100], dtype[0x100];
++     SQLINTEGER len, dv_dt_type = 0;
++     PyObject *value, *colinfo;
++ 
++     memset(lang, 0, sizeof(lang));
++     memset(dtype, 0, sizeof(dtype));
++ 
++     value = GetDataString(cur, column);
++     if (!value)
++    return Py_None;
++ 
++     // why do the virtuoso extensions number the columns from 1???
++     column += 1;
++ 
++     Py_BEGIN_ALLOW_THREADS
++    ret = SQLGetStmtAttr(cur->hstmt, SQL_ATTR_IMP_ROW_DESC, &hdesc, SQL_IS_POINTER, NULL);
++     Py_END_ALLOW_THREADS;
++     if (!SQL_SUCCEEDED(ret)) {
++    return Py_None;
++     }
++     Py_BEGIN_ALLOW_THREADS
++    ret = SQLGetDescField(hdesc, column, SQL_DESC_COL_DV_TYPE, &dvtype, SQL_IS_INTEGER, NULL);
++     Py_END_ALLOW_THREADS;
++     if (!SQL_SUCCEEDED(ret)) {
++    return Py_None;
++     }    
++     Py_BEGIN_ALLOW_THREADS
++    ret = SQLGetDescField(hdesc, column, SQL_DESC_COL_BOX_FLAGS, &flag, SQL_IS_INTEGER, NULL);
++     Py_END_ALLOW_THREADS;
++     if (!SQL_SUCCEEDED(ret)) {
++    return Py_None;
++     }
++ 
++     switch (dvtype) {
++         case VIRTUOSO_DV_RDF:
++        Py_BEGIN_ALLOW_THREADS
++        ret = SQLGetDescField(hdesc, column, SQL_DESC_COL_LITERAL_LANG, lang, sizeof(lang), &len);
++        Py_END_ALLOW_THREADS;
++        if (!SQL_SUCCEEDED(ret))
++        return Py_None;
++        Py_BEGIN_ALLOW_THREADS
++        ret = SQLGetDescField(hdesc, column, SQL_DESC_COL_LITERAL_TYPE, dtype, sizeof(dtype), &len);
++        Py_END_ALLOW_THREADS;
++        if (!SQL_SUCCEEDED(ret))
++        return Py_None;
++        break;
++         case VIRTUOSO_DV_TIMESTAMP:
++         case VIRTUOSO_DV_DATE:
++         case VIRTUOSO_DV_TIME:
++         case VIRTUOSO_DV_DATETIME:
++        Py_BEGIN_ALLOW_THREADS
++        ret = SQLGetDescField (hdesc, column, SQL_DESC_COL_DT_DT_TYPE, &dv_dt_type, SQL_IS_INTEGER, NULL);
++        Py_END_ALLOW_THREADS;
++        if (!SQL_SUCCEEDED(ret))
++        return Py_None;
++        break;
++         default:
++        break;
++     }
++ 
++     colinfo = Py_BuildValue("(Oiiiss)",
++                value,
++                dvtype,
++                dv_dt_type,
++                flag,
++                (char *)lang,
++                (char *)dtype);
++     if (!colinfo)
++    return Py_None;
++ 
++     return colinfo;
++ }
++ 
+  
+  PyObject*
+  GetData(Cursor* cur, Py_ssize_t iCol)
+***************
+*** 610,615 ****
+--- 692,701 ----
+      if (conv_index != -1)
+          return GetDataUser(cur, iCol, conv_index);
+  
++     // Check if we have to apply SPASQL processing
++     if (cur->spasql)
++         return GetDataSPASQL(cur, iCol);
++ 
+      switch (pinfo->sql_type)
+      {
+      case SQL_WCHAR:
+diff -c -r pyodbc-2.1.12/src/params.cpp pyodbc-2.1.12v/src/params.cpp
+*** pyodbc-2.1.12/src/params.cpp	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/src/params.cpp	2011-12-19 12:56:03.000000000 -0500
+***************
+*** 295,301 ****
+      long count = (long)PyTuple_GET_SIZE(digits);
+  
+      char* pch;
+!     int len;
+  
+      if (exp >= 0)
+      {
+--- 295,301 ----
+      long count = (long)PyTuple_GET_SIZE(digits);
+  
+      char* pch;
+!     long len;
+  
+      if (exp >= 0)
+      {
+diff -c -r pyodbc-2.1.12/src/pyodbcmodule.cpp pyodbc-2.1.12v/src/pyodbcmodule.cpp
+*** pyodbc-2.1.12/src/pyodbcmodule.cpp	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/src/pyodbcmodule.cpp	2012-05-16 10:23:18.000000000 -0400
+***************
+*** 19,24 ****
+--- 19,25 ----
+  #include "getdata.h"
+  #include "cnxninfo.h"
+  #include "dbspecific.h"
++ #include "virtuoso.h"
+  
+  #include <time.h>
+  #include <stdarg.h>
+***************
+*** 890,895 ****
+--- 891,914 ----
+      MAKECONST(SQL_UNION),
+      MAKECONST(SQL_USER_NAME),
+      MAKECONST(SQL_XOPEN_CLI_YEAR),
++ 
++     // Virtuoso Extensions
++     MAKECONST(VIRTUOSO_DV_ANY),
++     MAKECONST(VIRTUOSO_DV_DATE),
++     MAKECONST(VIRTUOSO_DV_DATETIME),
++     MAKECONST(VIRTUOSO_DV_DB_NULL),
++     MAKECONST(VIRTUOSO_DV_DOUBLE_FLOAT),
++     MAKECONST(VIRTUOSO_DV_IRI_ID ),
++     MAKECONST(VIRTUOSO_DV_LONG_INT),
++     MAKECONST(VIRTUOSO_DV_NUMERIC),
++     MAKECONST(VIRTUOSO_DV_RDF),
++     MAKECONST(VIRTUOSO_DV_SINGLE_FLOAT),
++     MAKECONST(VIRTUOSO_DV_STRING ),
++     MAKECONST(VIRTUOSO_DV_TIME),
++     MAKECONST(VIRTUOSO_DV_TIMESTAMP),
++     MAKECONST(VIRTUOSO_DT_TYPE_DATETIME),
++     MAKECONST(VIRTUOSO_DT_TYPE_DATE),
++     MAKECONST(VIRTUOSO_DT_TYPE_TIME)
+  };
+  
+  
+Only in pyodbc-2.1.12v/src: virtuoso.cpp
+Only in pyodbc-2.1.12v/src: virtuoso.h
+Only in pyodbc-2.1.12v: temp
+Only in pyodbc-2.1.12v/tests: dbapi20.pyc
+diff -c -r pyodbc-2.1.12/tests/mysqltests.py pyodbc-2.1.12v/tests/mysqltests.py
+*** pyodbc-2.1.12/tests/mysqltests.py	2011-10-17 08:45:30.000000000 -0400
+--- pyodbc-2.1.12v/tests/mysqltests.py	2011-12-19 15:08:39.000000000 -0500
+***************
+*** 420,425 ****
+--- 420,426 ----
+          self.cursor.execute("create table t1(d bigint)")
+          self.cursor.execute("insert into t1 values (?)", input)
+          result = self.cursor.execute("select d from t1").fetchone()[0]
++         print input, result
+          self.assertEqual(result, input)
+  
+      def test_float(self):
+***************
+*** 430,436 ****
+          self.assertEquals(result, value)
+  
+      def test_negative_float(self):
+!         value = -200
+          self.cursor.execute("create table t1(n float)")
+          self.cursor.execute("insert into t1 values (?)", value)
+          result  = self.cursor.execute("select n from t1").fetchone()[0]
+--- 431,437 ----
+          self.assertEquals(result, value)
+  
+      def test_negative_float(self):
+!         value = -200.0
+          self.cursor.execute("create table t1(n float)")
+          self.cursor.execute("insert into t1 values (?)", value)
+          result  = self.cursor.execute("select n from t1").fetchone()[0]
+Only in pyodbc-2.1.12v/tests: setup.cfg
+Only in pyodbc-2.1.12v/tests: testutils.pyc
     version = version + ".%s.%s" % (tip.rev(), tip.hex()[:12])
 except error.RepoError:
     pass
-                            
+
+
 def readme():
     dirname = os.path.dirname(os.path.abspath(__file__))
     filename = os.path.join(dirname, "README.txt")
       zip_safe=False,
       install_requires=[
           # -*- Extra requirements: -*-
-          "pyodbc==virtuoso-2.1.9-beta14",
+          # Apply the included patch to pyodbc 2.1.12
+          #"pyodbc==virtuoso-2.1.12",
       ],
       entry_points="""
           [sqlalchemy.dialects]

virtuoso/alchemy.py

-assert __import__("pkg_resources").get_distribution("sqlalchemy").version.startswith("0.6"), \
+assert __import__("pkg_resources").get_distribution("sqlalchemy").version.split('.') >= ['0', '6'], \
     "requires sqlalchemy version 0.6 or greater"
 
 from sqlalchemy.connectors.pyodbc import PyODBCConnector
 from sqlalchemy.dialects.sybase.base import SybaseDialect
-from sqlalchemy.sql import text, expression, bindparam
-from sqlalchemy import types as sqltypes
-from sqlalchemy.engine import default, reflection
+from sqlalchemy.sql import text, bindparam
+from sqlalchemy.engine import default
+
 
 class VirtuosoExecutionContext(default.DefaultExecutionContext):
     def get_lastrowid(self):
         self.cursor.execute("SELECT identity_value() AS lastrowid")
         lastrowid = self.cursor.fetchone()[0]
         return lastrowid
-    
+
+
 class VirtuosoDialect(PyODBCConnector, SybaseDialect):
     execution_ctx_cls = VirtuosoExecutionContext
-    
+
     def initialize(self, connection):
         self.supports_unicode_statements = False
         self.supports_unicode_binds = False
         SybaseDialect.initialize(self, connection)
-        
+
     def _get_default_schema_name(self, connection):
         return 'DBA'
 
                      ])
             )
         return result.scalar() is not None
-    
+
     def get_table_names(self, connection, schema=None, **kw):
         if schema is None:
             schema = self.default_schema_name
                  bindparams=[bindparam("schemaname", schema)])
             )
         return [r[0] for r in result]
-

virtuoso/tests/test_rdflib3.py

 from rdflib.term import URIRef, Literal, BNode
 from datetime import datetime
 from virtuoso.vstore import Virtuoso
+from virtuoso.vsparql import Result
 import os
 import unittest
 
 
 float_test = (URIRef("http://example.org/"), RDFS["label"], Literal(pi))
 
+
 class Test01Store(unittest.TestCase):
     @classmethod
     def setUp(cls):
         cls.store = Virtuoso("DSN=VOS;UID=dba;PWD=dba;WideAsUTF16=Y")
-        cls.graph = Graph(cls.store, identifier=URIRef("http://example.org/"))
+        cls.identifier = URIRef("http://example.org/")
+        cls.graph = Graph(cls.store, identifier=cls.identifier)
         cls.graph.remove((None, None, None))
-        
+
     @classmethod
     def tearDown(cls):
         cls.graph.remove((None, None, None))
         cls.store.close()
-        
+
     def test_01_query(self):
         g = ConjunctiveGraph(self.store)
         count = 0
         for c in g.contexts():
             assert isinstance(c, Graph)
             break
-           
+
     def test_03_construct(self):
         self.graph.add(test_statements[0])
-        q = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH %s { ?s ?p ?o } }" % self.graph.identifier.n3()
-        result = self.store.query(q)
-        assert isinstance(result, Graph)
+        q = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH %s { ?s ?p ?o } }" % (self.graph.identifier.n3(),)
+        result = self.store.query(None, q)
+        assert isinstance(result, Graph) or isinstance(result, Result)
+        result = list(result)
+        print result
         assert test_statements[0] in result
         self.graph.remove(test_statements[0])
 
     def test_04_ask(self):
-        assert not self.graph.query("ASK WHERE { ?s ?p ?o }")
+        arg = (self.graph.identifier.n3(),)
+        assert not self.graph.query("ASK FROM %s WHERE { ?s ?p ?o }" % arg)
         self.graph.add(test_statements[0])
-        assert self.graph.query("ASK WHERE { ?s ?p ?o }")
+        assert self.graph.query("ASK FROM %s WHERE { ?s ?p ?o }" % arg)
         self.graph.remove(test_statements[0])
-        assert not self.graph.query("ASK WHERE { ?s ?p ?o }")
+        assert not self.graph.query("ASK FROM %s WHERE { ?s ?p ?o }" % arg)
 
     def test_05_select(self):
         for statement in test_statements:
             self.graph.add(statement)
-        q = "SELECT DISTINCT ?s WHERE { ?s %(t)s ?o }" % { "t": RDF["type"].n3() }
+        q = "SELECT DISTINCT ?s FROM %(g)s WHERE { ?s %(t)s ?o }" % {
+            "t": RDF["type"].n3(), "g": self.graph.identifier.n3()}
         results = list(self.graph.query(q))
         assert len(results) == 2, results
         self.graph.remove((None, None, None))
-        
+
     def test_06_construct(self):
         for statement in test_statements:
             self.graph.add(statement)
-        q = "CONSTRUCT { ?s %(t)s ?o } WHERE { ?s %(t)s ?o }" % { "t": RDF["type"].n3() }
+        q = "CONSTRUCT { ?s %(t)s ?o } FROM %(g)s WHERE { ?s %(t)s ?o }" % {
+            "t": RDF["type"].n3(), "g": self.graph.identifier.n3()}
         result = self.graph.query(q)
         assert result.construct is True
         assert isinstance(result.result, Graph)
         self.graph.remove((None, None, None))
 
     def test_07_float(self):
-        raise SkipTest()
         self.add_remove(float_test)
-        print 
+        print
         print repr(float_test[2])
         for x in self.graph.triples((None, None, None)):
             print repr(x[2])
         for statement in self.graph.triples((None, None, None)):
             pass
         self.graph.remove((None, None, None))
-        
+
     def add_remove(self, statement):
         # add and check presence
         self.graph.add(statement)
         self.store.commit()
-        
+
         assert statement in self.graph, "%s not found" % (statement,)
 
         # check that we really got back what we asked for
         # delete and check absence
         self.graph.remove(statement)
         self.store.commit()
-        
+
         assert statement not in self.graph, "%s found" % (statement,)
 
 # make separate tests for each of the test statements so that we don't

virtuoso/vsparql.py

         for pfx, ns in initNs.items():
             preamble += u"PREFIX %s: <%s>\n" % (pfx, ns)
 
-        return self.graph.store.query(preamble + query)
+        return self.graph.store.query(None, preamble + query)
 
 class Result(rdflib.query.Result):
     def __init__(self, qResult):
         elif isinstance(qResult, Graph):
             self.construct = True
         self.result = qResult
+
     def __iter__(self):
-        return self.result
+        return self.result.__iter__()
+
+    def __len__(self):
+        return len(self.result)
+
     def __nonzero__(self):
         if self.askAnswer: return self.askAnswer[0]
         else: return False
-        

virtuoso/vstore.py

 except ImportError:
     from rdflib.Graph import Graph
     from rdflib import URIRef, BNode, Literal, Variable
-from rdflib.store import Store, VALID_STORE, NO_STORE
+from rdflib.store import Store, VALID_STORE
 
 if __dist__.version.startswith('3'):
     import vsparql
 
 __bnode_old_new__ = BNode.__new__
 
+
 @staticmethod
 def __bnode_new__(cls, value=None, *av, **kw):
     if value is None:
         value = choice(ascii_letters) + \
-            "".join(choice(ascii_letters+digits) for x in range(7))
+            "".join(choice(ascii_letters + digits) for x in range(7))
     return __bnode_old_new__(cls, value, *av, **kw)
 BNode.__new__ = __bnode_new__
 ## end hack
 
 import re
-_ask_re = re.compile(u'^SPARQL ([ \t\r\n]*DEFINE[ \t]+.*)*([ \t\r\n]*PREFIX[ \t]+[^ \t]*: <[^>]*>)*[ \t\r\n]*(ASK)[ \t\r\n]+WHERE', re.IGNORECASE)
+_ask_re = re.compile(u'^SPARQL ([ \t\r\n]*DEFINE[ \t]+.*)*([ \t\r\n]*PREFIX[ \t]+[^ \t]*: <[^>]*>)*[ \t\r\n]*(ASK)[ \t\r\n]+(FROM|WHERE)', re.IGNORECASE)
 _construct_re = re.compile(u'^SPARQL ([ \t\r\n]*DEFINE[ \t]+.*)*([ \t\r\n]*PREFIX[ \t]+[^ \t]*: <[^>]*>)*[ \t\r\n]*(CONSTRUCT|DESCRIBE)', re.IGNORECASE)
 _select_re = re.compile(u'^SPARQL ([ \t\r\n]*DEFINE[ \t]+.*)*([ \t\r\n]*PREFIX[ \t]+[^ \t]*: <[^>]*>)*[ \t\r\n]*SELECT', re.IGNORECASE)
 
+
 class OperationalError(Exception):
     """
     Raised when transactions are mis-managed
 
 import threading
 
+
 class Cursor(object):
     def __init__(self, connection, isolation=READ_COMMITTED):
         self.__cursor__ = connection.cursor()
         if "VSTORE_DEBUG" in os.environ:
             print u"INIT Cursor(%X) Thread(%X)" % (id(self.__cursor__), threading.currentThread().ident)
         self.execute("SET TRANSACTION ISOLATION LEVEL %s" % isolation)
+
     def __enter__(self):
         self.__refcount__ += 1
         return self
+
     def __exit__(self, type, value, traceback):
         self.__refcount__ -= 1
         if self.__refcount__ == 0:
             self.close()
+
     def __getattr__(self, attr):
         return getattr(self.__cursor__, attr)
+
     def commit(self):
         if self.__cursor__ is None:
-            raise OperationaError("No transaction in progress")
+            raise OperationalError("No transaction in progress")
         self.execute("COMMIT WORK")
+
     def rollback(self):
         if self.__cursor__ is None:
-            raise OperationaError("No transaction in progress")
+            raise OperationalError("No transaction in progress")
         self.execute("ROLLBACK WORK")
+
     def execute(self, q, *av, **kw):
         if "VSTORE_DEBUG" in os.environ:
             print u"EXEC Cursor(%X) Thread(%X)" % (id(self.__cursor__), threading.currentThread().ident), q
         return self.__cursor__.execute(q)
+
     def close(self):
         if "VSTORE_DEBUG" in os.environ:
             print u"CLOSE Cursor(%X) Thread(%X)" % (id(self.__cursor__), threading.currentThread().ident)
             self.__cursor__ = None
         else:
             self.log.warn("already closed. set VSTORE_DEBUG in the environment to enable debugging")
+
     def isOpen(self):
         return self.__cursor__ is not None
-    
+
+
+def contextIfGraph(context):
+    if isinstance(context, Graph):
+        return context
+    return None
+
+
 class Virtuoso(Store):
     """
     RDFLib Storage backed by Virtuoso
     """
     context_aware = True
     transaction_aware = True
+
     def __init__(self, *av, **kw):
         super(Virtuoso, self).__init__(*av, **kw)
         self._transaction = None
-        
+
     def open(self, dsn):
         self.__dsn = dsn
         return VALID_STORE
                 log.error("Virtuoso Connection Failed")
                 raise
         return self._connection
-    
+
     def cursor(self, *av, **kw):
         """
         Acquire a cursor, setting the isolation level.
     def clone(self):
         return Virtuoso(self.__dsn)
 
-    def query(self, q, cursor=None, commit=False):
+    def query(self, graph, q, initNs={}, initBindings={},
+                    DEBUG=False, cursor=None, commit=False):
         """
-        Run a SPARQL query on the connection. Returns a Graph in case of 
+        Run a SPARQL query on the connection. Returns a Graph in case of
         DESCRIBE or CONSTRUCT, a bool in case of Ask and a generator over
         the results otherwise.
         """
                 cursor = self.cursor()
         try:
             if _construct_re.match(q):
+                print "co"
                 return self._sparql_construct(q, cursor)
             elif _ask_re.match(q):
+                print "ask"
                 return self._sparql_ask(q, cursor)
             elif _select_re.match(q):
+                print "sel"
                 return self._sparql_select(q, cursor)
             else:
+                print "els", q
                 return self._sparql_ul(q, cursor, commit=commit)
         except:
             log.error(u"Exception running: %s" % q.decode("utf-8"))
             raise
-        
+
     def _sparql_construct(self, q, cursor):
+        g = Graph()
         with cursor:
             results = cursor.execute(q.encode("utf-8"))
-            # virtuoso handles construct by returning turtle
-            for result, in results:
-                g = Graph()
-                turtle = result[0]
-                g.parse(StringIO(turtle + "\n"), format="n3")
-                return g
+            with self.cursor() as resolver:
+                for result in results:
+                    print result
+                    g.add(resolve(resolver, x) for x in result)
+        return vsparql.Result(g)
 
     def _sparql_ask(self, q, cursor):
         with cursor:
             # seems like ask -> false returns an empty result set
             # and ask -> true returns an single row
             results = cursor.execute(q.encode("utf-8"))
-            if list(results):
-                return True
-            return False
+            result = results.next()
+            print result
+            result = resolve(None, result[0])
+            return result != 0
 
     def _sparql_select(self, q, cursor):
         with cursor:
             results = cursor.execute(q.encode("utf-8"))
             with self.cursor() as resolver:
                 for result in results:
+                    print result
                     yield [resolve(resolver, x) for x in result]
 
     def _sparql_ul(self, q, cursor, commit):
             cursor.execute(q.encode("utf-8"))
             if commit:
                 cursor.commit()
-                
+
     def transaction(self):
         """
         Return a long(er) life cursor associated with this store for
             return self._transaction
         self._transaction = self.cursor()
         return self._transaction
-    
+
     def commit(self):
         """
         Commit any pending work. Also releases the cached cursor.
         if statement is None:
             q = (u'SELECT DISTINCT __ro2sq(G) FROM RDF_QUAD')
         else:
-            q = (u'SELECT DISTINCT ?g WHERE '            
+            q = (u'SELECT DISTINCT ?g WHERE '
                  u'{ GRAPH ?g { %(S)s %(P)s %(O)s } }')
             q = _query_bindings(statement)
         with self.cursor() as cursor:
             for uri, in cursor.execute(q):
                 yield Graph(self, identifier=URIRef(uri))
+
     def triples(self, statement, context=None):
-        s,p,o = statement
+        s, p, o = statement
         if s is not None and p is not None and o is not None:
             # really we have an ASK query
             if self._triples_ask(statement, context):
     def _triples_ask(self, statement, context=None):
         query_bindings = _query_bindings(statement, context)
         q = (u'ASK WHERE { GRAPH %(G)s { %(S)s %(P)s %(O)s } }' % query_bindings)
-        return self.query(q)
-    
+        return self.query(contextIfGraph(context), q)
+
+    def __contains__(self, statement, context=None):
+        return self._triples_ask(statement, context)
+
     def _triples_pattern(self, statement, context=None):
         query_bindings = _query_bindings(statement, context)
         query_constants = {}
-        for k,v in query_bindings.items():
+        for k, v in query_bindings.items():
             if v.startswith('?') or v.startswith('$'):
-                query_bindings[k+"v"]=v
+                query_bindings[k + "v"] = v
             else:
                 query_constants[k] = v
                 query_bindings[k + "v"] = ""
              u'WHERE { GRAPH %(G)s { %(S)s %(P)s %(O)s } }')
         q = q % query_bindings
 
-        for row in self.query(q):
+        for row in self.query(contextIfGraph(context), q):
             result, i = [], 0
             for column in "SPOG":
                 if column in query_constants:
         if context is not None:
             q += u'INTO GRAPH %(G)s ' % query_bindings
         q += u'{ %(S)s %(P)s %(O)s }' % query_bindings
-        self.query(q, commit=self._transaction is None)
+        self.query(None, q, commit=self._transaction is None)
         super(Virtuoso, self).add(statement, context, quoted)
 
     def remove(self, statement, context=None):
             if context is not None:
                 q += u'FROM GRAPH %(G)s ' % query_bindings
             q += u'{ %(S)s %(P)s %(O)s } WHERE { %(S)s %(P)s %(O)s }' % query_bindings
-        self.query(q, commit=self._transaction is None)
+        self.query(contextIfGraph(context), q, commit=self._transaction is None)
         super(Virtuoso, self).remove(statement, context)
 
     def __len__(self, context=None):
+        graph = None
         if isinstance(context, Graph):
+            graph = context
             context = context.identifier
         if isinstance(context, BNode):
             context = _bnode_to_nodeid(context)
         q = u"SELECT COUNT (*) WHERE { "
-        if context: q += "GRAPH %s { " % context.n3()
+        if context:
+            q += "GRAPH %s { " % context.n3()
         q += "?s ?p ?o"
-        if context: q += " }"
+        if context:
+            q += " }"
         q += " }"
-        for count, in self.query(q):
+        for count, in self.query(graph, q):
             return count
-            
+
     def bind(self, prefix, namespace, flags=1):
         q = u"DB.DBA.XML_SET_NS_DECL ('%s', '%s', %s)" % (prefix, namespace, flags)
         with self.cursor() as cursor:
         for prefix, namespace in self.__namespace.iteritems():
             yield prefix, namespace
 
+
 def _bnode_to_nodeid(bnode):
     from string import ascii_letters
     iri = bnode
     for c in bnode[1:]:
         if c in ascii_letters:
             # from rdflib not virtuoso
-            iri = "b" + "".join(str(ord(x)-38) for x in bnode[:8])
+            iri = "b" + "".join(str(ord(x) - 38) for x in bnode[:8])
             break
     return URIRef("nodeID://%s" % iri)
 
+
 def _nodeid_to_bnode(iri):
-    from string import digits
-    iri = iri[9:] # strip off "nodeID://"
+    #from string import digits
+    iri = iri[9:]  # strip off "nodeID://"
     bnode = iri
     if len(iri) == 17:
         # 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)
+        chars = [x + y for x, y in zip(ones, tens)]
+        bnode = "".join(str(chr(int(x) + 38)) for x in chars)
     return BNode(bnode)
 
+
 def resolve(resolver, args):
     """
     Takes the Virtuoso representation of an RDF node and returns
         return URIRef(iri)
     if dvtype == pyodbc.VIRTUOSO_DV_RDF:
         if dtype == XSD["gYear"].encode("ascii"):
-             value = value[:4]
+            value = value[:4]
         elif dtype == XSD["gMonth"].encode("ascii"):
             value = value[:7]
         return Literal(value, lang=lang or None, datatype=dtype or None)
     if dvtype == pyodbc.VIRTUOSO_DV_STRING:
-        return Literal(value)
+        if flag == 1:
+            return URIRef(value)
+        else:
+            if dtype == XSD["gYear"].encode("ascii"):
+                value = value[:4]
+            elif dtype == XSD["gMonth"].encode("ascii"):
+                value = value[:7]
+            return Literal(value, lang=lang or None, datatype=dtype or None)
     if dvtype == pyodbc.VIRTUOSO_DV_LONG_INT:
         return Literal(int(value))
-    if dvtype == pyodbc.VIRTUOSO_DV_SINGLE_FLOAT:
+    if dvtype == pyodbc.VIRTUOSO_DV_SINGLE_FLOAT or dvtype == pyodbc.VIRTUOSO_DV_DOUBLE_FLOAT:
         return Literal(value, datatype=XSD["float"])
-    if dvtype == pyodbc.VIRTUOSO_DV_DATETIME:
+    if dvtype == pyodbc.VIRTUOSO_DV_NUMERIC:
+        return Literal(value, datatype=XSD["decimal"])
+    if dvtype == pyodbc.VIRTUOSO_DV_DATETIME or dvtype == pyodbc.VIRTUOSO_DV_TIMESTAMP:
         value = value.replace(" ", "T")
-        if dttype == pyodbc.VIRTUOSO_DT_TYPE_DATETIME:
-            return Literal(value, datatype=XSD["dateTime"])
         if dttype == pyodbc.VIRTUOSO_DT_TYPE_DATE:
             return Literal(value[:10], datatype=XSD["date"])
-        if dttype == pyodbc.VIRTUOSO_DT_TYPE_TIME:
+        elif dttype == pyodbc.VIRTUOSO_DT_TYPE_TIME:
             return Literal(value, datatype=XSD["time"])
+        else:
+            return Literal(value, datatype=XSD["dateTime"])
         log.warn("Unknown SPASQL DV DT type: %d for %s" % (dttype, value))
         return Literal(value)
     if dvtype == pyodbc.VIRTUOSO_DV_DATE:
-        return Literal(value, datatype=URIRef("http://www.w3.org/2001/XMLSchema#date"))
-    if dvtype == 204: ## XXX where is this const!?
+        return Literal(value, datatype=XSD["date"])
+    if dvtype == pyodbc.VIRTUOSO_DV_TIME:
+        return Literal(value, datatype=XSD["time"])
+    if dvtype == pyodbc.VIRTUOSO_DV_DB_NULL:
         return None
     log.warn("Unhandled SPASQL DV type: %d for %s" % (dvtype, value))
     return Literal(value)
 
-def _query_bindings((s,p,o), g=None):
+
+def _query_bindings((s, p, o), g=None):
     if isinstance(g, Graph):
         g = g.identifier
     if s is None: s = Variable("S")
     if isinstance(g, BNode):
         g = _bnode_to_nodeid(g)
     return dict(
-        zip("SPOG", [x.n3() for x in (s,p,o,g)])
+        zip("SPOG", [x.n3() for x in (s, p, o, g)])
         )