Esempio n. 1
0
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;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
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;
}