static PyObject* GetUUID(Cursor* cur, Py_ssize_t iCol) { // REVIEW: Since GUID is a fixed size, do we need to pass the size or cbFetched? PYSQLGUID guid; SQLLEN cbFetched = 0; SQLRETURN ret; Py_BEGIN_ALLOW_THREADS ret = SQLGetData(cur->hstmt, (SQLUSMALLINT)(iCol+1), SQL_GUID, &guid, sizeof(guid), &cbFetched); Py_END_ALLOW_THREADS if (!SQL_SUCCEEDED(ret)) return RaiseErrorFromHandle("SQLGetData", cur->cnxn->hdbc, cur->hstmt); if (cbFetched == SQL_NULL_DATA) Py_RETURN_NONE; #if PY_MAJOR_VERSION >= 3 const char* szFmt = "(yyy#)"; #else const char* szFmt = "(sss#)"; #endif Object args(Py_BuildValue(szFmt, NULL, NULL, &guid, (int)sizeof(guid))); if (!args) return 0; PyObject* uuid_type = GetClassForThread("uuid", "UUID"); if (!uuid_type) return 0; PyObject* uuid = PyObject_CallObject(uuid_type, args.Get()); Py_DECREF(uuid_type); return uuid; }
bool IsInstanceForThread(PyObject* param, const char* szModule, const char* szClass, PyObject** pcls) { // Like PyObject_IsInstance but compares against a class specific to the current thread's // interpreter, for proper subinterpreter support. Uses GetClassForThread. // // If `param` is an instance of the given class, true is returned and a new reference to // the class, specific to the current thread, is returned via pcls. The caller is // responsible for decrementing the class. // // If `param` is not an instance, true is still returned (!) but *pcls will be zero. // // False is only returned when an exception has been raised. (That is, the return value is // not used to indicate whether the instance check matched or not.) if (param == 0) { *pcls = 0; return true; } PyObject* cls = GetClassForThread(szModule, szClass); if (!cls) { *pcls = 0; return false; } int n = PyObject_IsInstance(param, cls); // (The checks below can be compressed into just a few lines, but I was concerned it // wouldn't be clear.) if (n == 1) { // We have a match. *pcls = cls; return true; } Py_DECREF(cls); *pcls = 0; if (n == 0) { // No exception, but not a match. return true; } // n == -1; an exception occurred return false; }
PyObject* PythonTypeFromSqlType(Cursor* cur, SQLSMALLINT type) { // Returns a type object ('int', 'str', etc.) for the given ODBC C type. This is used to populate // Cursor.description with the type of Python object that will be returned for each column. // // type // The ODBC C type (SQL_C_CHAR, etc.) of the column. // // The returned object does not have its reference count incremented (is a borrowed // reference). // // Keep this in sync with GetData below. int conv_index = GetUserConvIndex(cur, type); if (conv_index != -1) return (PyObject*)&PyString_Type; PyObject* pytype = 0; bool incref = true; switch (type) { case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: #if PY_MAJOR_VERSION < 3 if (cur->cnxn->str_enc.ctype == SQL_C_CHAR) pytype = (PyObject*)&PyString_Type; else pytype = (PyObject*)&PyUnicode_Type; #else pytype = (PyObject*)&PyUnicode_Type; #endif break; case SQL_GUID: if (UseNativeUUID()) { pytype = GetClassForThread("uuid", "UUID"); incref = false; } else { #if PY_MAJOR_VERSION < 3 if (cur->cnxn->str_enc.ctype == SQL_C_CHAR) pytype = (PyObject*)&PyString_Type; else pytype = (PyObject*)&PyUnicode_Type; #else pytype = (PyObject*)&PyUnicode_Type; #endif } break; case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: case SQL_SS_XML: case SQL_DB2_XML: pytype = (PyObject*)&PyUnicode_Type; break; case SQL_DECIMAL: case SQL_NUMERIC: pytype = GetClassForThread("decimal", "Decimal"); incref = false; break; case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: pytype = (PyObject*)&PyFloat_Type; break; case SQL_SMALLINT: case SQL_INTEGER: case SQL_TINYINT: pytype = (PyObject*)&PyInt_Type; break; case SQL_TYPE_DATE: pytype = (PyObject*)PyDateTimeAPI->DateType; break; case SQL_TYPE_TIME: case SQL_SS_TIME2: // SQL Server 2008+ pytype = (PyObject*)PyDateTimeAPI->TimeType; break; case SQL_TYPE_TIMESTAMP: pytype = (PyObject*)PyDateTimeAPI->DateTimeType; break; case SQL_BIGINT: pytype = (PyObject*)&PyLong_Type; break; case SQL_BIT: pytype = (PyObject*)&PyBool_Type; break; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: default: #if PY_VERSION_HEX >= 0x02060000 pytype = (PyObject*)&PyByteArray_Type; #else pytype = (PyObject*)&PyBuffer_Type; #endif break; } if (pytype && incref) Py_INCREF(pytype); return pytype; }
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; }