Skip to content

Commit

Permalink
Implemented support for the SQL Server sql_variant data type
Browse files Browse the repository at this point in the history
  • Loading branch information
will-hinson authored and mkleehammer committed Sep 8, 2024
1 parent c913dc8 commit ee51907
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/dbspecific.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// SQL Server


#define SQL_SS_VARIANT -150 // SQL Server 2008 SQL_VARIANT type
#define SQL_SS_XML -152 // SQL Server 2005 XML type
#define SQL_DB2_DECFLOAT -360 // IBM DB/2 DECFLOAT type
#define SQL_DB2_XML -370 // IBM DB/2 XML type
Expand Down
32 changes: 32 additions & 0 deletions src/getdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ void GetData_init()
}

static byte* ReallocOrFreeBuffer(byte* pb, Py_ssize_t cbNeed);
PyObject *GetData_SqlVariant(Cursor *cur, Py_ssize_t iCol);

inline bool IsBinaryType(SQLSMALLINT sqltype)
{
Expand Down Expand Up @@ -751,8 +752,39 @@ PyObject* GetData(Cursor* cur, Py_ssize_t iCol)

case SQL_SS_TIME2:
return GetSqlServerTime(cur, iCol);

case SQL_SS_VARIANT:
return GetData_SqlVariant(cur, iCol);
}

return RaiseErrorV("HY106", ProgrammingError, "ODBC SQL type %d is not yet supported. column-index=%zd type=%d",
(int)pinfo->sql_type, iCol, (int)pinfo->sql_type);
}

PyObject *GetData_SqlVariant(Cursor *cur, Py_ssize_t iCol) {
char pBuff;

SQLLEN indicator, variantType;
SQLRETURN retcode;

// Call SQLGetData on the current column with a data length of 0. According to MS, this makes
// the ODBC driver read the sql_variant header which contains the underlying data type
pBuff = 0;
indicator = 0;
retcode = SQLGetData(cur->hstmt, static_cast<SQLSMALLINT>(iCol + 1), SQL_C_BINARY,
&pBuff, 0, &indicator);
if (!SQL_SUCCEEDED(retcode))
return RaiseErrorFromHandle(cur->cnxn, "SQLGetData", cur->cnxn->hdbc, cur->hstmt);

// Get the SQL_CA_SS_VARIANT_TYPE field for the column which will contain the underlying data type
variantType = 0;
retcode = SQLColAttribute(cur->hstmt, iCol + 1, SQL_CA_SS_VARIANT_TYPE, NULL, 0, NULL, &variantType);
if (!SQL_SUCCEEDED(retcode))
return RaiseErrorFromHandle(cur->cnxn, "SQLColAttribute", cur->cnxn->hdbc, cur->hstmt);

// Replace the original SQL_VARIANT data type with the underlying data type then call GetData() again
cur->colinfos[iCol].sql_type = static_cast<SQLSMALLINT>(variantType);
return GetData(cur, iCol);

// NOTE: we don't free the hstmt here as it's managed by the cursor
}
4 changes: 4 additions & 0 deletions src/pyodbc.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ typedef unsigned long long UINT64;
#define SQL_CA_SS_CATALOG_NAME 1225
#endif

#ifndef SQL_CA_SS_VARIANT_TYPE
#define SQL_CA_SS_VARIANT_TYPE 1215
#endif

inline bool IsSet(DWORD grf, DWORD flags)
{
return (grf & flags) == flags;
Expand Down

0 comments on commit ee51907

Please sign in to comment.