static void FreeInfos(ParamInfo* a, Py_ssize_t count) { for (Py_ssize_t i = 0; i < count; i++) { if (a[i].allocated) pyodbc_free(a[i].ParameterValuePtr); Py_XDECREF(a[i].pParam); } pyodbc_free(a); }
static PyObject* GetDataUser(Cursor* cur, Py_ssize_t iCol, int conv) { // conv // The index into the connection's user-defined conversions `conv_types`. bool isNull = false; byte* pbData = 0; Py_ssize_t cbData = 0; if (!ReadVarColumn(cur, iCol, SQL_C_BINARY, isNull, pbData, cbData)) return 0; if (isNull) { I(pbData == 0 && cbData == 0); Py_RETURN_NONE; } PyObject* value = PyBytes_FromStringAndSize((char*)pbData, cbData); pyodbc_free(pbData); if (!value) return 0; PyObject* result = PyObject_CallFunction(cur->cnxn->conv_funcs[conv], "(O)", value); Py_DECREF(value); if (!result) return 0; return result; }
static PyObject* GetText(Cursor* cur, Py_ssize_t iCol) { // We are reading one of the SQL_WCHAR, SQL_WVARCHAR, etc., and will return // a string. // // If there is no configuration we would expect this to be UTF-16 encoded data. (If no // byte-order-mark, we would expect it to be big-endian.) // // Now, just because the driver is telling us it is wide data doesn't mean it is true. // psqlodbc with UTF-8 will tell us it is wide data but you must ask for single-byte. // (Otherwise it is just UTF-8 with each character stored as 2 bytes.) That's why we allow // the user to configure. ColumnInfo* pinfo = &cur->colinfos[iCol]; const TextEnc& enc = IsWideType(pinfo->sql_type) ? cur->cnxn->sqlwchar_enc : cur->cnxn->sqlchar_enc; bool isNull = false; byte* pbData = 0; Py_ssize_t cbData = 0; if (!ReadVarColumn(cur, iCol, enc.ctype, isNull, pbData, cbData)) return 0; if (isNull) { I(pbData == 0 && cbData == 0); Py_RETURN_NONE; } PyObject* result = TextBufferToObject(enc, pbData, cbData); pyodbc_free(pbData); return result; }
static PyObject* GetBinary(Cursor* cur, Py_ssize_t iCol) { // Reads SQL_BINARY. bool isNull = false; byte* pbData = 0; Py_ssize_t cbData = 0; if (!ReadVarColumn(cur, iCol, SQL_C_BINARY, isNull, pbData, cbData)) return 0; if (isNull) { I(pbData == 0 && cbData == 0); Py_RETURN_NONE; } PyObject* obj; #if PY_MAJOR_VERSION >= 3 obj = PyBytes_FromStringAndSize((char*)pbData, cbData); #else obj = PyByteArray_FromStringAndSize((char*)pbData, cbData); #endif pyodbc_free(pbData); return obj; }
void SQLWChar::Free() { if (pch && owns_memory) pyodbc_free(pch); pch = 0; len = 0; owns_memory = false; }
void FreeRowValues(Py_ssize_t cValues, PyObject** apValues) { if (apValues) { for (Py_ssize_t i = 0; i < cValues; i++) Py_XDECREF(apValues[i]); pyodbc_free(apValues); } }
~DataBuffer() { if (!usingStack) { if (bufferOwner) { Py_DECREF(bufferOwner); } else { pyodbc_free(buffer); } } }
static byte* ReallocOrFreeBuffer(byte* pb, Py_ssize_t cbNeed) { // Attempts to reallocate `pb` to size `cbNeed`. If the realloc fails, the original memory // is freed, a memory exception is set, and 0 is returned. Otherwise the new pointer is // returned. byte* pbNew = (byte*)realloc(pb, (size_t)cbNeed); if (pbNew == 0) { pyodbc_free(pb); PyErr_NoMemory(); return 0; } return pbNew; }
bool SQLWChar::Convert(PyObject* o) { Free(); if (!PyUnicode_Check(o)) { PyErr_SetString(PyExc_TypeError, "Unicode required"); return false; } Py_UNICODE* pU = (Py_UNICODE*)PyUnicode_AS_UNICODE(o); Py_ssize_t lenT = PyUnicode_GET_SIZE(o); if (SQLWCHAR_SIZE == Py_UNICODE_SIZE) { // The ideal case - SQLWCHAR and Py_UNICODE are the same, so we point into the Unicode object. pch = (SQLWCHAR*)pU; len = lenT; owns_memory = false; return true; } else { SQLWCHAR* pchT = (SQLWCHAR*)pyodbc_malloc(sizeof(SQLWCHAR) * (lenT + 1)); if (pchT == 0) { PyErr_NoMemory(); return false; } if (!sqlwchar_copy(pchT, pU, lenT)) { pyodbc_free(pchT); return false; } pch = pchT; len = lenT; owns_memory = true; return true; } }
static PyObject* new_check(PyObject* args) { // We don't support a normal constructor, so only allow this for unpickling. There should be a single arg that was // returned by Row_reduce. Make sure the sizes match. The desc and map should have one entry per column, which // should equal the number of remaining items. if (PyTuple_GET_SIZE(args) < 3) return 0; PyObject* desc = PyTuple_GET_ITEM(args, 0); PyObject* map = PyTuple_GET_ITEM(args, 1); if (!PyTuple_CheckExact(desc) || !PyDict_CheckExact(map)) return 0; Py_ssize_t cols = PyTuple_GET_SIZE(desc); if (PyDict_Size(map) != cols || PyTuple_GET_SIZE(args) - 2 != cols) return 0; PyObject** apValues = (PyObject**)pyodbc_malloc(sizeof(PyObject*) * cols); if (!apValues) return 0; for (int i = 0; i < cols; i++) { apValues[i] = PyTuple_GET_ITEM(args, i+2); Py_INCREF(apValues[i]); } // Row_Internal will incref desc and map. PyObject* self = (PyObject*)Row_InternalNew(desc, map, cols, apValues); if (!self) pyodbc_free(apValues); return self; }
static bool ReadVarColumn(Cursor* cur, Py_ssize_t iCol, SQLSMALLINT ctype, bool& isNull, byte*& pbResult, Py_ssize_t& cbResult) { // Called to read a variable-length column and return its data in a newly-allocated heap // buffer. // // Returns true if the read was successful and false if the read failed. If the read // failed a Python exception will have been set. // // If a non-null and non-empty value was read, pbResult will be set to a buffer containing // the data and cbResult will be set to the byte length. This length does *not* include a // null terminator. In this case the data *must* be freed using pyodbc_free. // // If a null value was read, isNull is set to true and pbResult and cbResult will be set to // 0. // // If a zero-length value was read, isNull is set to false and pbResult and cbResult will // be set to 0. isNull = false; pbResult = 0; cbResult = 0; const Py_ssize_t cbElement = (Py_ssize_t)(IsWideType(ctype) ? sizeof(ODBCCHAR) : 1); const Py_ssize_t cbNullTerminator = IsBinaryType(ctype) ? 0 : cbElement; // TODO: Make the initial allocation size configurable? Py_ssize_t cbAllocated = 4096; Py_ssize_t cbUsed = 0; byte* pb = (byte*)malloc((size_t)cbAllocated); if (!pb) { PyErr_NoMemory(); return false; } SQLRETURN ret = SQL_SUCCESS_WITH_INFO; do { // Call SQLGetData in a loop as long as it keeps returning partial data (ret == // SQL_SUCCESS_WITH_INFO). Each time through, update the buffer pb, cbAllocated, and // cbUsed. Py_ssize_t cbAvailable = cbAllocated - cbUsed; SQLLEN cbData; Py_BEGIN_ALLOW_THREADS ret = SQLGetData(cur->hstmt, (SQLUSMALLINT)(iCol+1), ctype, &pb[cbUsed], (SQLLEN)cbAvailable, &cbData); Py_END_ALLOW_THREADS; TRACE("ReadVarColumn: SQLGetData avail=%d --> ret=%d cbData=%d\n", (int)cbAvailable, (int)ret, (int)cbData); if (!SQL_SUCCEEDED(ret) && ret != SQL_NO_DATA) { RaiseErrorFromHandle("SQLGetData", cur->cnxn->hdbc, cur->hstmt); return false; } if (ret == SQL_SUCCESS && cbData < 0) { // HACK: FreeTDS 0.91 on OS/X returns -4 for NULL data instead of SQL_NULL_DATA // (-1). I've traced into the code and it appears to be the result of assigning -1 // to a SQLLEN. We are going to treat all negative values as NULL. ret = SQL_NULL_DATA; cbData = 0; } // SQLGetData behavior is incredibly quirky: It doesn't tell us the total, the total // we've read, or even the amount just read. It returns the amount just read, plus any // remaining. Unfortunately, the only way to pick them apart is to subtract out the // amount of buffer we supplied. if (ret == SQL_SUCCESS_WITH_INFO) { // This means we read some data, but there is more. SQLGetData is very weird - it // sets cbRead to the number of bytes we read *plus* the amount remaining. Py_ssize_t cbRemaining = 0; // How many more bytes do we need to allocate, not including null? Py_ssize_t cbRead = 0; // How much did we just read, not including null? if (cbData == SQL_NO_TOTAL) { // This special value indicates there is more data but the driver can't tell us // how much more, so we'll just add whatever we want and try again. It also // tells us, however, that the buffer is full, so the amount we read equals the // amount we offered. Remember that if the type requires a null terminator, it // will be added *every* time, not just at the end, so we need to subtract it. cbRead = (cbAvailable - cbNullTerminator); cbRemaining = 1024 * 1024; } else if ((Py_ssize_t)cbData >= cbAvailable) { // We offered cbAvailable space, but there was cbData data. The driver filled // the buffer with what it could. Remember that if the type requires a null // terminator, the driver is going to append one on *every* read, so we need to // subtract them out. At least we know the exact data amount now and we can // allocate a precise amount. cbRead = (cbAvailable - cbNullTerminator); cbRemaining = cbData - cbRead; } else { // I would not expect to get here - we apparently read all of the data but the // driver did not return SQL_SUCCESS? cbRead = (cbData - cbNullTerminator); cbRemaining = 0; } cbUsed += cbRead; if (cbRemaining > 0) { // This is a tiny bit complicated by the fact that the data is null terminated, // meaning we haven't actually used up the entire buffer (cbAllocated), only // cbUsed (which should be cbAllocated - cbNullTerminator). Py_ssize_t cbNeed = cbUsed + cbRemaining + cbNullTerminator; pb = ReallocOrFreeBuffer(pb, cbNeed); if (!pb) return false; cbAllocated = cbNeed; } } else if (ret == SQL_SUCCESS) { // We read some data and this is the last batch (so we'll drop out of the // loop). // // If I'm reading the documentation correctly, SQLGetData is not going to // include the null terminator in cbRead. cbUsed += cbData; } } while (ret == SQL_SUCCESS_WITH_INFO); isNull = (ret == SQL_NULL_DATA); if (!isNull && cbUsed > 0) { pbResult = pb; cbResult = cbUsed; } else { pyodbc_free(pb); } return true; }
static PyObject* GetDataDecimal(Cursor* cur, Py_ssize_t iCol) { // The SQL_NUMERIC_STRUCT support is hopeless (SQL Server ignores scale on input parameters and output columns, // Oracle does something else weird, and many drivers don't support it at all), so we'll rely on the Decimal's // string parsing. Unfortunately, the Decimal author does not pay attention to the locale, so we have to modify // the string ourselves. // // Oracle inserts group separators (commas in US, periods in some countries), so leave room for that too. // // Some databases support a 'money' type which also inserts currency symbols. Since we don't want to keep track of // all these, we'll ignore all characters we don't recognize. We will look for digits, negative sign (which I hope // is universal), and a decimal point ('.' or ',' usually). We'll do everything as Unicode in case currencies, // etc. are too far out. const TextEnc& enc = cur->cnxn->sqlwchar_enc; // I'm going to request the data as Unicode in case there is a weird currency symbol. If // this is a performance problems we may want a flag on this. bool isNull = false; byte* pbData = 0; Py_ssize_t cbData = 0; if (!ReadVarColumn(cur, iCol, enc.ctype, isNull, pbData, cbData)) return 0; if (isNull) { I(pbData == 0 && cbData == 0); Py_RETURN_NONE; } Object result(TextBufferToObject(enc, pbData, cbData)); pyodbc_free(pbData); if (!result) return 0; // Remove non-digits and convert the databases decimal to a '.' (required by decimal ctor). // // We are assuming that the decimal point and digits fit within the size of ODBCCHAR. // If Unicode, convert to UTF-8 and copy the digits and punctuation out. Since these are // all ASCII characters, we can ignore any multiple-byte characters. Fortunately, if a // character is multi-byte all bytes will have the high bit set. char* pch; Py_ssize_t cch; #if PY_MAJOR_VERSION >= 3 if (PyUnicode_Check(result)) { pch = PyUnicode_AsUTF8AndSize(result, &cch); } else { int n = PyBytes_AsStringAndSize(result, &pch, &cch); if (n < 0) pch = 0; } #else Object encoded; if (PyUnicode_Check(result)) { encoded = PyUnicode_AsUTF8String(result); if (!encoded) return 0; result = encoded.Detach(); } int n = PyString_AsStringAndSize(result, &pch, &cch); if (n < 0) pch = 0; #endif if (!pch) return 0; // TODO: Why is this limited to 100? Also, can we perform a check on the original and use // it as-is? char ascii[100]; size_t asciilen = 0; const char* pchMax = pch + cch; while (pch < pchMax) { if ((*pch & 0x80) == 0) { if (*pch == chDecimal) { // Must force it to use '.' since the Decimal class doesn't pay attention to the locale. ascii[asciilen++] = '.'; } else if ((*pch >= '0' && *pch <= '9') || *pch == '-') { ascii[asciilen++] = (char)(*pch); } } pch++; } ascii[asciilen] = 0; Object str(PyString_FromStringAndSize(ascii, (Py_ssize_t)asciilen)); if (!str) return 0; PyObject* decimal_type = GetClassForThread("decimal", "Decimal"); if (!decimal_type) return 0; PyObject* decimal = PyObject_CallFunction(decimal_type, "O", str.Get()); Py_DECREF(decimal_type); return decimal; }
PyObject* DetachValue() { // At this point, Trim should have been called by PostRead. if (bytesUsed == SQL_NULL_DATA || buffer == 0) Py_RETURN_NONE; if (usingStack) { if (dataType == SQL_C_CHAR) return PyBytes_FromStringAndSize(buffer, bytesUsed); if (dataType == SQL_C_BINARY) { #if PY_VERSION_HEX >= 0x02060000 return PyByteArray_FromStringAndSize(buffer, bytesUsed); #else return PyBytes_FromStringAndSize(buffer, bytesUsed); #endif } if (sizeof(SQLWCHAR) == Py_UNICODE_SIZE) return PyUnicode_FromUnicode((const Py_UNICODE*)buffer, bytesUsed / element_size); return PyUnicode_FromSQLWCHAR((const SQLWCHAR*)buffer, bytesUsed / element_size); } if (bufferOwner && PyUnicode_CheckExact(bufferOwner)) { if (PyUnicode_Resize(&bufferOwner, bytesUsed / element_size) == -1) return 0; PyObject* tmp = bufferOwner; bufferOwner = 0; buffer = 0; return tmp; } if (bufferOwner && PyBytes_CheckExact(bufferOwner)) { if (_PyBytes_Resize(&bufferOwner, bytesUsed) == -1) return 0; PyObject* tmp = bufferOwner; bufferOwner = 0; buffer = 0; return tmp; } #if PY_VERSION_HEX >= 0x02060000 if (bufferOwner && PyByteArray_CheckExact(bufferOwner)) { if (PyByteArray_Resize(bufferOwner, bytesUsed) == -1) return 0; PyObject* tmp = bufferOwner; bufferOwner = 0; buffer = 0; return tmp; } #endif // We have allocated our own SQLWCHAR buffer and must now copy it to a Unicode object. I(bufferOwner == 0); PyObject* result = PyUnicode_FromSQLWCHAR((const SQLWCHAR*)buffer, bytesUsed / element_size); if (result == 0) return false; pyodbc_free(buffer); buffer = 0; return result; }