NATIVE_FLOAT or NATIVE_DOUBLE types, all numbers are converted to strings and passed through to ODPI-C in all Python versions; improved error message when a value cannot be represented by an Oracle number value; improved test suite to verify that calling executemany() with integers, floats and decimal values intermixed with each other works as expected (https://github.com/oracle/python-cx_Oracle/issues/241).
310 lines
8.7 KiB
C
310 lines
8.7 KiB
C
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// cxoVarType.c
|
|
// Defines variable types for various transformations.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "cxoModule.h"
|
|
|
|
// define variable types for each of the supported transforms
|
|
static cxoVarType cxoAllVarTypes[] = {
|
|
{
|
|
CXO_TRANSFORM_NONE,
|
|
&cxoPyTypeStringVar,
|
|
1
|
|
},
|
|
{
|
|
CXO_TRANSFORM_BINARY,
|
|
&cxoPyTypeBinaryVar,
|
|
4000
|
|
},
|
|
{
|
|
CXO_TRANSFORM_BFILE,
|
|
&cxoPyTypeBfileVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_BLOB,
|
|
&cxoPyTypeBlobVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_BOOLEAN,
|
|
&cxoPyTypeBooleanVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_CLOB,
|
|
&cxoPyTypeClobVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_CURSOR,
|
|
&cxoPyTypeCursorVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_DATE,
|
|
&cxoPyTypeDateTimeVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_DATETIME,
|
|
&cxoPyTypeDateTimeVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_DECIMAL,
|
|
&cxoPyTypeNumberVar,
|
|
1000
|
|
},
|
|
{
|
|
CXO_TRANSFORM_FIXED_CHAR,
|
|
&cxoPyTypeFixedCharVar,
|
|
2000
|
|
},
|
|
{
|
|
CXO_TRANSFORM_FIXED_NCHAR,
|
|
&cxoPyTypeFixedNcharVar,
|
|
2000
|
|
},
|
|
{
|
|
CXO_TRANSFORM_FLOAT,
|
|
&cxoPyTypeNumberVar,
|
|
1000
|
|
},
|
|
{
|
|
CXO_TRANSFORM_INT,
|
|
&cxoPyTypeNumberVar,
|
|
1000
|
|
},
|
|
{
|
|
CXO_TRANSFORM_LONG_BINARY,
|
|
&cxoPyTypeLongBinaryVar,
|
|
128 * 1024
|
|
},
|
|
{
|
|
CXO_TRANSFORM_LONG_STRING,
|
|
&cxoPyTypeLongStringVar,
|
|
128 * 1024
|
|
},
|
|
{
|
|
CXO_TRANSFORM_NATIVE_DOUBLE,
|
|
&cxoPyTypeNativeFloatVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_NATIVE_FLOAT,
|
|
&cxoPyTypeNativeFloatVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_NATIVE_INT,
|
|
&cxoPyTypeNativeIntVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_NCLOB,
|
|
&cxoPyTypeNclobVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_NSTRING,
|
|
&cxoPyTypeNcharVar,
|
|
4000
|
|
},
|
|
{
|
|
CXO_TRANSFORM_OBJECT,
|
|
&cxoPyTypeObjectVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_ROWID,
|
|
&cxoPyTypeRowidVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_STRING,
|
|
&cxoPyTypeStringVar,
|
|
4000
|
|
},
|
|
{
|
|
CXO_TRANSFORM_TIMEDELTA,
|
|
&cxoPyTypeIntervalVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_TIMESTAMP,
|
|
&cxoPyTypeTimestampVar,
|
|
0
|
|
},
|
|
{
|
|
CXO_TRANSFORM_TIMESTAMP_LTZ,
|
|
&cxoPyTypeTimestampVar,
|
|
0
|
|
}
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// cxoVarType_fromDataTypeInfo()
|
|
// Return a variable type given query metadata, or NULL indicating that the
|
|
// data indicated by the query metadata is not supported.
|
|
//-----------------------------------------------------------------------------
|
|
cxoVarType *cxoVarType_fromDataTypeInfo(dpiDataTypeInfo *info)
|
|
{
|
|
cxoTransformNum transformNum;
|
|
char message[120];
|
|
|
|
transformNum = cxoTransform_getNumFromDataTypeInfo(info);
|
|
if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
|
|
snprintf(message, sizeof(message), "Oracle type %d not supported.",
|
|
info->oracleTypeNum);
|
|
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
|
|
return NULL;
|
|
}
|
|
return &cxoAllVarTypes[transformNum];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// cxoVarType_fromPythonType()
|
|
// Return a variable type given a Python type object or NULL if the Python
|
|
// type does not have a corresponding variable type. If the type provided is an
|
|
// object type, return that as well.
|
|
//-----------------------------------------------------------------------------
|
|
cxoVarType *cxoVarType_fromPythonType(PyObject *type, cxoObjectType **objType)
|
|
{
|
|
cxoTransformNum transformNum;
|
|
PyTypeObject *pyType;
|
|
char message[250];
|
|
|
|
if (Py_TYPE(type) == &cxoPyTypeObjectType) {
|
|
transformNum = CXO_TRANSFORM_OBJECT;
|
|
*objType = (cxoObjectType*) type;
|
|
} else if (Py_TYPE(type) != &PyType_Type) {
|
|
PyErr_SetString(PyExc_TypeError, "expecting type");
|
|
return NULL;
|
|
} else {
|
|
*objType = NULL;
|
|
pyType = (PyTypeObject*) type;
|
|
transformNum = cxoTransform_getNumFromType(pyType);
|
|
if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
|
|
snprintf(message, sizeof(message), "Python type %s not supported.",
|
|
pyType->tp_name);
|
|
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
|
|
return NULL;
|
|
}
|
|
}
|
|
return &cxoAllVarTypes[transformNum];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// cxoVarType_calculateSize()
|
|
// Calculate the size to use with the specified transform and Python value.
|
|
// This function is only called by cxoVarType_fromPythonValue() and no attempt
|
|
// is made to verify the value further.
|
|
//-----------------------------------------------------------------------------
|
|
static Py_ssize_t cxoVarType_calculateSize(PyObject *value,
|
|
cxoTransformNum transformNum)
|
|
{
|
|
Py_ssize_t size = 0;
|
|
#if PY_MAJOR_VERSION < 3
|
|
const void *ptr;
|
|
#endif
|
|
|
|
switch (transformNum) {
|
|
case CXO_TRANSFORM_NONE:
|
|
return 1;
|
|
case CXO_TRANSFORM_BINARY:
|
|
#if PY_MAJOR_VERSION >= 3
|
|
return PyBytes_GET_SIZE(value);
|
|
#else
|
|
PyObject_AsReadBuffer(value, &ptr, &size);
|
|
return size;
|
|
#endif
|
|
case CXO_TRANSFORM_NSTRING:
|
|
size = PyUnicode_GET_SIZE(value);
|
|
return (size == 0) ? 1 : size;
|
|
case CXO_TRANSFORM_STRING:
|
|
#if PY_MAJOR_VERSION >= 3
|
|
size = PyUnicode_GET_SIZE(value);
|
|
#else
|
|
size = PyString_GET_SIZE(value);
|
|
#endif
|
|
return (size == 0) ? 1 : size;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// cxoVarType_fromPythonValue()
|
|
// Return a variable type given a Python object or NULL if the Python object
|
|
// does not have a corresponding variable type.
|
|
//-----------------------------------------------------------------------------
|
|
cxoVarType *cxoVarType_fromPythonValue(PyObject *value, int *isArray,
|
|
Py_ssize_t *size, Py_ssize_t *numElements, int plsql)
|
|
{
|
|
cxoTransformNum transformNum, tempTransformNum;
|
|
PyObject *elementValue;
|
|
Py_ssize_t i, tempSize;
|
|
char message[250];
|
|
|
|
// initialization (except numElements which always has a valid value and is
|
|
// only overridden when a an array is encountered)
|
|
*size = 0;
|
|
*isArray = 0;
|
|
|
|
// handle arrays
|
|
if (PyList_Check(value)) {
|
|
transformNum = CXO_TRANSFORM_NONE;
|
|
for (i = 0; i < PyList_GET_SIZE(value); i++) {
|
|
elementValue = PyList_GET_ITEM(value, i);
|
|
tempTransformNum = cxoTransform_getNumFromValue(elementValue, 1);
|
|
if (tempTransformNum == CXO_TRANSFORM_UNSUPPORTED) {
|
|
snprintf(message, sizeof(message),
|
|
"element %u value is unsupported", (unsigned) i);
|
|
cxoError_raiseFromString(cxoNotSupportedErrorException,
|
|
message);
|
|
return NULL;
|
|
} else if (transformNum == CXO_TRANSFORM_NONE) {
|
|
transformNum = tempTransformNum;
|
|
} else if (transformNum != tempTransformNum) {
|
|
snprintf(message, sizeof(message),
|
|
"element %u value is not the same type as previous "
|
|
"elements", (unsigned) i);
|
|
cxoError_raiseFromString(cxoNotSupportedErrorException,
|
|
message);
|
|
return NULL;
|
|
}
|
|
tempSize = cxoVarType_calculateSize(elementValue,
|
|
tempTransformNum);
|
|
if (tempSize > *size)
|
|
*size = tempSize;
|
|
}
|
|
*isArray = 1;
|
|
*numElements = PyList_GET_SIZE(value);
|
|
return &cxoAllVarTypes[transformNum];
|
|
}
|
|
|
|
// handle scalar values
|
|
transformNum = cxoTransform_getNumFromValue(value, plsql);
|
|
if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
|
|
snprintf(message, sizeof(message),
|
|
"Python value of type %s not supported.",
|
|
Py_TYPE(value)->tp_name);
|
|
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
|
|
return NULL;
|
|
}
|
|
*size = cxoVarType_calculateSize(value, transformNum);
|
|
return &cxoAllVarTypes[transformNum];
|
|
}
|
|
|