Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Virtuoso SPASQL support #3

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
include src/*.h
include src/*.cpp
include tests/*
include README.rst

# Include this file, needed for bdist_rpm
include MANIFEST.in
include src/*.h
include src/*.cpp
include tests/*
include README.rst
recursive-include utils *.cpp

# Include this file, needed for bdist_rpm
include MANIFEST.in
18 changes: 15 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ def get_compiler_settings(version_str):
# OS/X now ships with iODBC.
settings['libraries'].append('iodbc')

elif sys.platform.startswith('freebsd'):
try:
include = '-I'+os.environ['PREFIX']+'/include'
lib = '-L'+os.environ['PREFIX']+'/lib'
except:
include = '-I/usr/local/include'
lib = '-L/usr/local/lib'

settings['extra_compile_args'] = ['-Wno-write-strings', include, lib]
settings['extra_link_args'] = [ lib ]
settings['libraries'].append('odbc')

else:
# Other posix-like: Linux, Solaris, etc.

Expand Down Expand Up @@ -263,13 +275,13 @@ def get_version():
def _get_version_pkginfo():
filename = join(dirname(abspath(__file__)), 'PKG-INFO')
if exists(filename):
re_ver = re.compile(r'^Version: \s+ (\d+)\.(\d+)\.(\d+) (?: -beta(\d+))?', re.VERBOSE)
re_ver = re.compile(r'^Version: \s+ (?: (.+)-) (\d+)\.(\d+)\.(\d+) (?: -beta(\d+))?', re.VERBOSE)
for line in open(filename):
match = re_ver.search(line)
if match:
name = line.split(':', 1)[1].strip()
numbers = [int(n or 0) for n in match.groups()[:3]]
numbers.append(int(match.group(4) or OFFICIAL_BUILD)) # don't use 0 as a default for build
numbers = [int(n or 0) for n in match.groups()[1:4]]
numbers.append(int(match.group(5) or OFFICIAL_BUILD)) # don't use 0 as a default for build
return name, numbers

return None, None
Expand Down
3 changes: 3 additions & 0 deletions src/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "wrapper.h"
#include "cnxninfo.h"
#include "sqlwchar.h"
#include "virtuoso.h"

static char connection_doc[] =
"Connection objects manage connections to the database.\n"
Expand Down Expand Up @@ -190,6 +191,8 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi,
cnxn->conv_types = 0;
cnxn->conv_funcs = 0;

cnxn->virtuoso = isVirtuoso(hdbc);

//
// Initialize autocommit mode.
//
Expand Down
2 changes: 2 additions & 0 deletions src/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ struct Connection
int wvarchar_maxlength;
int binary_maxlength;

bool virtuoso;

// Output conversions. Maps from SQL type in conv_types to the converter function in conv_funcs.
//
// If conv_count is zero, conv_types and conv_funcs will also be zero.
Expand Down
5 changes: 4 additions & 1 deletion src/cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "getdata.h"
#include "dbspecific.h"
#include "sqlwchar.h"
#include "virtuoso.h"

enum
{
Expand Down Expand Up @@ -778,6 +779,8 @@ execute(Cursor* cur, PyObject* pSql, PyObject* params, bool skip_first)

FreeParameterData(cur);

cur->spasql = (cur->cnxn->virtuoso && isSPASQL(pSql));

if (ret == SQL_NO_DATA)
{
// Example: A delete statement that did not delete anything.
Expand Down Expand Up @@ -1002,7 +1005,7 @@ Cursor_fetch(Cursor* cur)
return 0;
}

apValues[i] = value;
apValues[i] = value;
}

return (PyObject*)Row_New(cur->description, cur->map_name_to_index, field_count, apValues);
Expand Down
3 changes: 3 additions & 0 deletions src/cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ struct Cursor
// 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.
Expand Down
86 changes: 86 additions & 0 deletions src/getdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "errors.h"
#include "dbspecific.h"
#include "sqlwchar.h"
#include "virtuoso.h"

void GetData_init()
{
Expand Down Expand Up @@ -594,6 +595,87 @@ int GetUserConvIndex(Cursor* cur, SQLSMALLINT sql_type)
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;
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)
Expand All @@ -610,6 +692,10 @@ GetData(Cursor* cur, Py_ssize_t iCol)
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:
Expand Down
17 changes: 17 additions & 0 deletions src/pyodbcmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "getdata.h"
#include "cnxninfo.h"
#include "dbspecific.h"
#include "virtuoso.h"

#include <time.h>
#include <stdarg.h>
Expand Down Expand Up @@ -890,6 +891,22 @@ static const ConstantDef aConstants[] = {
MAKECONST(SQL_UNION),
MAKECONST(SQL_USER_NAME),
MAKECONST(SQL_XOPEN_CLI_YEAR),
// Virtuoso Extensions
MAKECONST(VIRTUOSO_DV_DATE),
MAKECONST(VIRTUOSO_DV_DATETIME),
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_DV_TIMESTAMP_OBJ),
MAKECONST(VIRTUOSO_DT_TYPE_DATETIME),
MAKECONST(VIRTUOSO_DT_TYPE_DATE),
MAKECONST(VIRTUOSO_DT_TYPE_TIME)
};


Expand Down
35 changes: 35 additions & 0 deletions src/virtuoso.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "pyodbc.h"
#include "virtuoso.h"

bool
isVirtuoso(HDBC hdbc)
{
char buf[0x1000];
SQLSMALLINT len;
SQLRETURN ret;

ret = SQLGetInfo(hdbc, (SQLUSMALLINT)SQL_DBMS_NAME, buf, sizeof(buf), &len);
if (!SQL_SUCCEEDED(ret))
return false;
if (!strncasecmp(buf, "OpenLink Virtuoso", sizeof(buf))) {
return true;
}

return false;
}

bool
isSPASQL(PyObject *pSql)
{
char *query = PyString_AS_STRING(pSql);

if (!query)
return false;
while (*query && isspace(*query))
query++;

if (!strncasecmp(query, "SPARQL", 6))
return true;
return false;
}

51 changes: 51 additions & 0 deletions src/virtuoso.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#ifndef VIRTUOSO_H
#define VIRTUOSO_H

#ifdef HAVE_IODBC
#include <iodbcext.h>
#endif

/*
* Include Virtuoso ODBC extensions for SPASQL result set
*/
#if !defined (SQL_DESC_COL_DV_TYPE)

/*
* ODBC extensions for SQLGetDescField
*/
# define SQL_DESC_COL_DV_TYPE 1057L
# define SQL_DESC_COL_DT_DT_TYPE 1058L
# define SQL_DESC_COL_LITERAL_ATTR 1059L
# define SQL_DESC_COL_BOX_FLAGS 1060L
# define SQL_DESC_COL_LITERAL_LANG 1061L
# define SQL_DESC_COL_LITERAL_TYPE 1062L

/*
* Virtuoso - ODBC SQL_DESC_COL_DV_TYPE
*/
# define VIRTUOSO_DV_DATE 129
# define VIRTUOSO_DV_DATETIME 211
# define VIRTUOSO_DV_DOUBLE_FLOAT 191
# define VIRTUOSO_DV_IRI_ID 243
# define VIRTUOSO_DV_LONG_INT 189
# define VIRTUOSO_DV_NUMERIC 219
# define VIRTUOSO_DV_RDF 246
# define VIRTUOSO_DV_SINGLE_FLOAT 190
# define VIRTUOSO_DV_STRING 182
# define VIRTUOSO_DV_TIME 210
# define VIRTUOSO_DV_TIMESTAMP 128
# define VIRTUOSO_DV_TIMESTAMP_OBJ 208

/*
* Virtuoso - ODBC SQL_DESC_COL_DT_DT_TYPE
*/
# define VIRTUOSO_DT_TYPE_DATETIME 1
# define VIRTUOSO_DT_TYPE_DATE 2
# define VIRTUOSO_DT_TYPE_TIME 3

#endif /* SQL_DESC_COL_DV_TYPE */

bool isVirtuoso(HDBC);
bool isSPASQL(PyObject *);

#endif /* VIRTUOSO_H */