Skip to content

Commit

Permalink
heap message if stack message is too small
Browse files Browse the repository at this point in the history
  • Loading branch information
juliuszsompolski committed Apr 21, 2021
1 parent 8b4a795 commit 27cf95b
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ static int GetDiagRecs(Cursor* cur)

ODBCCHAR cSQLState[6]; // five-character SQLSTATE code (plus terminating NULL)
SQLINTEGER iNativeError;
ODBCCHAR cMessageText[SHRT_MAX]; // PRINT statements can be large
ODBCCHAR cMessageText[10240]; // PRINT statements can be large
SQLSMALLINT iTextLength;

SQLRETURN ret;
Expand Down
37 changes: 34 additions & 3 deletions src/errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ PyObject* GetErrorFromHandle(Connection *conn, const char* szFunction, HDBC hdbc
SQLSMALLINT cchMsg;

ODBCCHAR sqlstateT[6];
ODBCCHAR szMsg[SHRT_MAX];
ODBCCHAR stackMsg[1024];
ODBCCHAR *szMsg;


if (hstmt != SQL_NULL_HANDLE)
{
Expand Down Expand Up @@ -251,11 +253,28 @@ PyObject* GetErrorFromHandle(Connection *conn, const char* szFunction, HDBC hdbc

SQLRETURN ret;
Py_BEGIN_ALLOW_THREADS
ret = SQLGetDiagRecW(nHandleType, h, iRecord, (SQLWCHAR*)sqlstateT, &nNativeError, (SQLWCHAR*)szMsg, (short)(_countof(szMsg)-1), &cchMsg);
ret = SQLGetDiagRecW(nHandleType, h, iRecord, (SQLWCHAR*)sqlstateT, &nNativeError, (SQLWCHAR*)szMsg, (short)(_countof(stackMsg)-1), &cchMsg);
Py_END_ALLOW_THREADS
if (!SQL_SUCCEEDED(ret))
break;

// If needed, allocate a bigger error message buffer and retry.
if (cchMsg > msgLen - 1) {
SQLSMALLINT msgLen = cchMsg + 1;
szMsg = pyodbc_malloc(msgLen * sizeof(ODBCCHAR));
if (!heapMsg) {
PyErr_NoMemory();
return 0;
}
Py_BEGIN_ALLOW_THREADS
ret = SQLGetDiagRecW(nHandleType, h, iRecord, (SQLWCHAR*)sqlstateT, &nNativeError, (SQLWCHAR*)szMsg, msgLen, &cchMsg);
Py_END_ALLOW_THREADS
if (!SQL_SUCCEEDED(ret)) {
pyodbc_free(szMsg);
break;
}
}

// Not always NULL terminated (MS Access)
sqlstateT[5] = 0;

Expand All @@ -264,6 +283,13 @@ PyObject* GetErrorFromHandle(Connection *conn, const char* szFunction, HDBC hdbc
const char *unicode_enc = conn ? conn->metadata_enc.name : ENCSTR_UTF16NE;
Object msgStr(PyUnicode_Decode((char*)szMsg, cchMsg * sizeof(ODBCCHAR), unicode_enc, "strict"));

if (szMsg != stackMsg) {
// Free and revert back to stackMsg here, reduces the complexity of handling keeping
// track of the heap message and freeing it in other places.
pyodbc_free(szMsg);
stackMsg = szMsg;
}

if (cchMsg != 0 && msgStr.Get())
{
if (iRecord == 1)
Expand All @@ -272,8 +298,10 @@ PyObject* GetErrorFromHandle(Connection *conn, const char* szFunction, HDBC hdbc
// exception class and append the calling function name.
CopySqlState(sqlstateT, sqlstate);
msg = PyUnicode_FromFormat("[%s] %V (%ld) (%s)", sqlstate, msgStr.Get(), "(null)", (long)nNativeError, szFunction);
if (!msg)
if (!msg) {
PyErr_NoMemory();
return 0;
}
}
else
{
Expand All @@ -298,6 +326,9 @@ PyObject* GetErrorFromHandle(Connection *conn, const char* szFunction, HDBC hdbc
#endif
}

// Free raw message buffer, if allocated on heap.
if (szMsg != stackMsg) pyodbc_free(szMsg);

if (!msg || PyUnicode_GetSize(msg.Get()) == 0)
{
// This only happens using unixODBC. (Haven't tried iODBC yet.) Either the driver or the driver manager is
Expand Down

0 comments on commit 27cf95b

Please sign in to comment.