bool BindParameter(Cursor* cur, Py_ssize_t index, ParamInfo& info) { TRACE("BIND: param=%d InputOutputType=%d (%s) ValueType=%d (%s) ParameterType=%d (%s) ColumnSize=%d DecimalDigits=%d BufferLength=%d *pcb=%d\n", (index+1), info.InputOutputType, CInputOutputName(info.InputOutputType), info.ValueType, CTypeName(info.ValueType), info.ParameterType, SqlTypeName(info.ParameterType), info.ColumnSize, info.DecimalDigits, info.BufferLength, info.StrLen_or_Ind); SQLRETURN ret = -1; Py_BEGIN_ALLOW_THREADS ret = SQLBindParameter(cur->hstmt, (SQLUSMALLINT)(index + 1), info.InputOutputType, info.ValueType, info.ParameterType, info.ColumnSize, info.DecimalDigits, info.ParameterValuePtr, info.BufferLength, &info.StrLen_or_Ind); Py_END_ALLOW_THREADS; if (GetConnection(cur)->hdbc == SQL_NULL_HANDLE) { // The connection was closed by another thread in the ALLOW_THREADS block above. RaiseErrorV(0, ProgrammingError, "The cursor's connection was closed."); return false; } if (!SQL_SUCCEEDED(ret)) { RaiseErrorFromHandle("SQLBindParameter", GetConnection(cur)->hdbc, cur->hstmt); return false; } return true; }
static bool GetParameterInfo(Cursor* cur, Py_ssize_t index, PyObject* param, ParamInfo& info) { // Determines the type of SQL parameter that will be used for this parameter based on the Python data type. // // Populates `info`. // TODO: if the param is a SQLParameter object, then bind to the wrapped value and use the input/output type from that // Hold a reference to param until info is freed, because info will often be holding data borrowed from param. if (PyObject_TypeCheck(param, (PyTypeObject*)SQLParameter_type)) { info.pParam = ((SQLParameter*)param)->value; info.InputOutputType = ((SQLParameter*)param)->type; } else { info.pParam = param; info.InputOutputType = SQL_PARAM_INPUT; } if (info.pParam == Py_None) return GetNullInfo(cur, index, info); if (info.pParam == null_binary) return GetNullBinaryInfo(cur, index, info); if (PyBytes_Check(info.pParam)) return GetBytesInfo(cur, index, info.pParam, info); if (PyUnicode_Check(info.pParam)) return GetUnicodeInfo(cur, index, info.pParam, info); if (PyBool_Check(info.pParam)) return GetBooleanInfo(cur, index, info.pParam, info); if (PyDateTime_Check(info.pParam)) return GetDateTimeInfo(cur, index, info.pParam, info); if (PyDate_Check(info.pParam)) return GetDateInfo(cur, index, info.pParam, info); if (PyTime_Check(info.pParam)) return GetTimeInfo(cur, index, info.pParam, info); if (PyLong_Check(info.pParam)) return GetLongInfo(cur, index, info.pParam, info); if (PyFloat_Check(info.pParam)) return GetFloatInfo(cur, index, info.pParam, info); if (PyDecimal_Check(info.pParam)) return GetDecimalInfo(cur, index, info.pParam, info); #if PY_VERSION_HEX >= 0x02060000 if (PyByteArray_Check(info.pParam)) return GetByteArrayInfo(cur, index, info.pParam, info); #endif #if PY_MAJOR_VERSION < 3 if (PyInt_Check(info.pParam)) return GetIntInfo(cur, index, info.pParam, info); if (PyBuffer_Check(info.pParam)) return GetBufferInfo(cur, index, info.pParam, info); #endif RaiseErrorV("HY105", ProgrammingError, "Invalid parameter type. param-index=%zd param-type=%s", index, Py_TYPE(info.pParam)->tp_name); return false; }
bool PrepareAndBind(Cursor* cur, PyObject* pSql, PyObject* original_params, bool skip_first) { // // Normalize the parameter variables. // // Since we may replace parameters (we replace objects with Py_True/Py_False when writing to a bit/bool column), // allocate an array and use it instead of the original sequence. Since we don't change ownership we don't bother // with incref. (That is, PySequence_GetItem will INCREF and ~ObjectArrayHolder will DECREF.) int params_offset = skip_first ? 1 : 0; Py_ssize_t cParams = original_params == 0 ? 0 : PySequence_Length(original_params) - params_offset; PyObject** params = (PyObject**)malloc(sizeof(PyObject*) * cParams); if (!params) { PyErr_NoMemory(); return 0; } for (Py_ssize_t i = 0; i < cParams; i++) params[i] = PySequence_GetItem(original_params, i + params_offset); ObjectArrayHolder holder(cParams, params); // // Prepare the SQL if necessary. // if (pSql == cur->pPreparedSQL) { // We've already prepared this SQL, so we don't need to do so again. We've also cached the parameter // information in cur->paramdescs. if (cParams != cur->paramcount) { RaiseErrorV(0, ProgrammingError, "The SQL contains %d parameter markers, but %d parameters were supplied", cur->paramcount, cParams); return false; } } else { FreeParameterInfo(cur); SQLRETURN ret; if (PyString_Check(pSql)) { Py_BEGIN_ALLOW_THREADS ret = SQLPrepare(cur->hstmt, (SQLCHAR*)PyString_AS_STRING(pSql), SQL_NTS); Py_END_ALLOW_THREADS } else { Py_BEGIN_ALLOW_THREADS ret = SQLPrepareW(cur->hstmt, (SQLWCHAR*)PyUnicode_AsUnicode(pSql), SQL_NTS); Py_END_ALLOW_THREADS } if (cur->cnxn->hdbc == SQL_NULL_HANDLE) { // The connection was closed by another thread in the ALLOW_THREADS block above. RaiseErrorV(0, ProgrammingError, "The cursor's connection was closed."); return false; } if (!SQL_SUCCEEDED(ret)) { RaiseErrorFromHandle("SQLPrepare", GetConnection(cur)->hdbc, cur->hstmt); return false; } if (!CacheParamDesc(cur)) return false; cur->pPreparedSQL = pSql; Py_INCREF(cur->pPreparedSQL); }
PyObject* GetData(Cursor* cur, Py_ssize_t iCol) { // Returns an object representing the value in the row/field. If 0 is returned, an exception has already been set. // // The data is assumed to be the default C type for the column's SQL type. ColumnInfo* pinfo = &cur->colinfos[iCol]; // First see if there is a user-defined conversion. int conv_index = GetUserConvIndex(cur, pinfo->sql_type); if (conv_index != -1) return GetDataUser(cur, iCol, conv_index); switch (pinfo->sql_type) { case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: return GetText(cur, iCol); case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_SS_XML: case SQL_DB2_XML: return GetText(cur, iCol); case SQL_GUID: if (UseNativeUUID()) return GetUUID(cur, iCol); return GetText(cur, iCol); break; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: return GetBinary(cur, iCol); case SQL_DECIMAL: case SQL_NUMERIC: return GetDataDecimal(cur, iCol); case SQL_BIT: return GetDataBit(cur, iCol); case SQL_TINYINT: case SQL_SMALLINT: case SQL_INTEGER: return GetDataLong(cur, iCol); case SQL_BIGINT: return GetDataLongLong(cur, iCol); case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: return GetDataDouble(cur, iCol); case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: return GetDataTimestamp(cur, iCol); case SQL_SS_TIME2: return GetSqlServerTime(cur, iCol); } return RaiseErrorV("HY106", ProgrammingError, "ODBC SQL type %d is not yet supported. column-index=%zd type=%d", (int)pinfo->sql_type, iCol, (int)pinfo->sql_type); }
PyObject* GetData(Cursor* cur, Py_ssize_t iCol) { // Returns an object representing the value in the row/field. If 0 is returned, an exception has already been set. // // The data is assumed to be the default C type for the column's SQL type. ColumnInfo* pinfo = &cur->colinfos[iCol]; switch (pinfo->sql_type) { case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_GUID: return GetDataString(cur, iCol); case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: return GetDataBuffer(cur, iCol); case SQL_DECIMAL: case SQL_NUMERIC: { if (decimal_type == 0) break; return GetDataDecimal(cur, iCol); } case SQL_BIT: return GetDataBit(cur, iCol); case SQL_TINYINT: case SQL_SMALLINT: case SQL_INTEGER: return GetDataLong(cur, iCol); case SQL_BIGINT: return GetDataLongLong(cur, iCol); case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: return GetDataDouble(cur, iCol); case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: return GetDataTimestamp(cur, iCol); case SQL_SS_TIME2: return GetSqlServerTime(cur, iCol); } return RaiseErrorV("HY106", ProgrammingError, "ODBC SQL type %d is not yet supported. column-index=%zd type=%d", (int)pinfo->sql_type, iCol, (int)pinfo->sql_type); }