Permalink
Browse files

Use SQL_DATA_AT_EXEC instead of SQL_DATA_LEN_AT_EXEC when possible.

Some drivers have problems with the LEN version, so eliminating use when possible.

There is an ODBC GetInfo query to determine if LEN is required.  Upon connection, this is
cached and LEN is used if this is 'y'.  FreeTDS, for example, requires LEN in some cases.
  • Loading branch information...
1 parent 6e873d8 commit f4856c4d900a6c10aca41ec5c449c9eb24abb769 @mkleehammer committed Nov 1, 2012
Showing with 25 additions and 31 deletions.
  1. +13 −26 src/cnxninfo.cpp
  2. +4 −0 src/cnxninfo.h
  3. +1 −0 src/connection.cpp
  4. +1 −0 src/connection.h
  5. +6 −5 src/params.cpp
View
@@ -51,17 +51,17 @@ static PyObject* GetHash(PyObject* p)
Object hash(PyObject_CallMethod(hashlib, "new", "s", "sha1"));
if (!hash.IsValid())
return 0;
-
+
PyObject_CallMethodObjArgs(hash, update, p, 0);
return PyObject_CallMethod(hash, "hexdigest", 0);
}
-
+
if (sha)
{
Object hash(PyObject_CallMethod(sha, "new", 0));
if (!hash.IsValid())
return 0;
-
+
PyObject_CallMethodObjArgs(hash, update, p, 0);
return PyObject_CallMethod(hash, "hexdigest", 0);
}
@@ -85,6 +85,7 @@ static PyObject* CnxnInfo_New(Connection* cnxn)
p->odbc_minor = 50;
p->supports_describeparam = false;
p->datetime_precision = 19; // default: "yyyy-mm-dd hh:mm:ss"
+ p->need_long_data_len = false;
// WARNING: The GIL lock is released for the *entire* function here. Do not touch any objects, call Python APIs,
// etc. We are simply making ODBC calls and setting atomic values (ints & chars). Also, make sure the lock gets
@@ -108,11 +109,11 @@ static PyObject* CnxnInfo_New(Connection* cnxn)
}
char szYN[2];
- ret = SQLGetInfo(cnxn->hdbc, SQL_DESCRIBE_PARAMETER, szYN, _countof(szYN), &cch);
- if (SQL_SUCCEEDED(ret))
- {
+ if (SQL_SUCCEEDED(SQLGetInfo(cnxn->hdbc, SQL_DESCRIBE_PARAMETER, szYN, _countof(szYN), &cch)))
p->supports_describeparam = szYN[0] == 'Y';
- }
+
+ if (SQL_SUCCEEDED(SQLGetInfo(cnxn->hdbc, SQL_NEED_LONG_DATA_LEN, szYN, _countof(szYN), &cch)))
+ p->need_long_data_len = (szYN[0] == 'Y');
// These defaults are tiny, but are necessary for Access.
p->varchar_maxlength = 255;
@@ -124,42 +125,28 @@ static PyObject* CnxnInfo_New(Connection* cnxn)
{
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)))
p->datetime_precision = (int)columnsize;
- SQLFreeStmt(hstmt, SQL_CLOSE);
- }
-
if (SQL_SUCCEEDED(SQLGetTypeInfo(hstmt, SQL_VARCHAR)) && SQL_SUCCEEDED(SQLFetch(hstmt)))
- {
if (SQL_SUCCEEDED(SQLGetData(hstmt, 3, SQL_INTEGER, &columnsize, sizeof(columnsize), 0)))
p->varchar_maxlength = (int)columnsize;
-
- SQLFreeStmt(hstmt, SQL_CLOSE);
- }
-
+
if (SQL_SUCCEEDED(SQLGetTypeInfo(hstmt, SQL_WVARCHAR)) && SQL_SUCCEEDED(SQLFetch(hstmt)))
- {
if (SQL_SUCCEEDED(SQLGetData(hstmt, 3, SQL_INTEGER, &columnsize, sizeof(columnsize), 0)))
p->wvarchar_maxlength = (int)columnsize;
-
- SQLFreeStmt(hstmt, SQL_CLOSE);
- }
-
+
if (SQL_SUCCEEDED(SQLGetTypeInfo(hstmt, SQL_BINARY)) && SQL_SUCCEEDED(SQLFetch(hstmt)))
- {
if (SQL_SUCCEEDED(SQLGetData(hstmt, 3, SQL_INTEGER, &columnsize, sizeof(columnsize), 0)))
p->binary_maxlength = (int)columnsize;
-
- SQLFreeStmt(hstmt, SQL_CLOSE);
- }
+
+ SQLFreeStmt(hstmt, SQL_CLOSE);
}
Py_END_ALLOW_THREADS
// WARNING: Released the lock now.
-
+
return info.Detach();
}
View
@@ -27,6 +27,10 @@ struct CnxnInfo
bool supports_describeparam;
int datetime_precision;
+ // Do we need to use SQL_LEN_DATA_AT_EXEC? Some drivers (e.g. FreeTDS 0.91) have problems with long values, so
+ // we'll use SQL_DATA_AT_EXEC when possible. If this is true, however, we'll need to pass the length.
+ bool need_long_data_len;
+
// These are from SQLGetTypeInfo.column_size, so the char ones are in characters, not bytes.
int varchar_maxlength;
int wvarchar_maxlength;
View
@@ -263,6 +263,7 @@ PyObject* Connection_New(PyObject* pConnectString, bool fAutoCommit, bool fAnsi,
cnxn->varchar_maxlength = p->varchar_maxlength;
cnxn->wvarchar_maxlength = p->wvarchar_maxlength;
cnxn->binary_maxlength = p->binary_maxlength;
+ cnxn->need_long_data_len = p->need_long_data_len;
return reinterpret_cast<PyObject*>(cnxn);
}
View
@@ -51,6 +51,7 @@ struct Connection
int varchar_maxlength;
int wvarchar_maxlength;
int binary_maxlength;
+ bool need_long_data_len;
// Output conversions. Maps from SQL type in conv_types to the converter function in conv_funcs.
//
View
@@ -1,4 +1,5 @@
+
#include "pyodbc.h"
#include "pyodbcmodule.h"
#include "params.h"
@@ -151,7 +152,7 @@ static bool GetBytesInfo(Cursor* cur, Py_ssize_t index, PyObject* param, ParamIn
{
// Too long to pass all at once, so we'll provide the data at execute.
info.ParameterType = SQL_LONGVARBINARY;
- info.StrLen_or_Ind = SQL_LEN_DATA_AT_EXEC((SQLLEN)len);
+ info.StrLen_or_Ind = cur->cnxn->need_long_data_len ? SQL_LEN_DATA_AT_EXEC((SQLLEN)len) : SQL_DATA_AT_EXEC;
info.ParameterValuePtr = param;
}
@@ -169,7 +170,7 @@ static bool GetBytesInfo(Cursor* cur, Py_ssize_t index, PyObject* param, ParamIn
{
// Too long to pass all at once, so we'll provide the data at execute.
info.ParameterType = SQL_LONGVARCHAR;
- info.StrLen_or_Ind = SQL_LEN_DATA_AT_EXEC((SQLLEN)len);
+ info.StrLen_or_Ind = cur->cnxn->need_long_data_len ? SQL_LEN_DATA_AT_EXEC((SQLLEN)len) : SQL_DATA_AT_EXEC;
info.ParameterValuePtr = param;
}
#endif
@@ -215,7 +216,7 @@ static bool GetUnicodeInfo(Cursor* cur, Py_ssize_t index, PyObject* param, Param
// Too long to pass all at once, so we'll provide the data at execute.
info.ParameterType = SQL_WLONGVARCHAR;
- info.StrLen_or_Ind = SQL_LEN_DATA_AT_EXEC((SQLLEN)(len * sizeof(SQLWCHAR)));
+ info.StrLen_or_Ind = cur->cnxn->need_long_data_len ? SQL_LEN_DATA_AT_EXEC((SQLLEN)len * sizeof(SQLWCHAR)) : SQL_DATA_AT_EXEC;
info.ParameterValuePtr = param;
}
@@ -495,7 +496,7 @@ static bool GetBufferInfo(Cursor* cur, Py_ssize_t index, PyObject* param, ParamI
info.ParameterValuePtr = param;
info.ColumnSize = (SQLUINTEGER)PyBuffer_Size(param);
info.BufferLength = sizeof(PyObject*); // How big is ParameterValuePtr; ODBC copies it and gives it back in SQLParamData
- info.StrLen_or_Ind = SQL_LEN_DATA_AT_EXEC(PyBuffer_Size(param));
+ info.StrLen_or_Ind = cur->cnxn->need_long_data_len ? SQL_LEN_DATA_AT_EXEC((SQLLEN)PyBuffer_Size(param)) : SQL_DATA_AT_EXEC;
}
return true;
@@ -522,7 +523,7 @@ static bool GetByteArrayInfo(Cursor* cur, Py_ssize_t index, PyObject* param, Par
info.ParameterValuePtr = param;
info.ColumnSize = (SQLUINTEGER)cb;
info.BufferLength = sizeof(PyObject*); // How big is ParameterValuePtr; ODBC copies it and gives it back in SQLParamData
- info.StrLen_or_Ind = SQL_LEN_DATA_AT_EXEC(cb);
+ info.StrLen_or_Ind = cur->cnxn->need_long_data_len ? SQL_LEN_DATA_AT_EXEC((SQLLEN)cb) : SQL_DATA_AT_EXEC;
}
return true;
}

0 comments on commit f4856c4

Please sign in to comment.