Added support for binding objects with sub objects attached to them; ensured
that instances are created when an empty object variable is created; enhanced test cases for binding objects.
This commit is contained in:
parent
cd9fced3e6
commit
39805c66d7
4
Cursor.c
4
Cursor.c
@ -2333,9 +2333,7 @@ static PyObject *Cursor_Var(
|
||||
// define the object type if needed
|
||||
if (type == (PyObject*) &g_ObjectVarType) {
|
||||
objectVar = (udt_ObjectVar*) var;
|
||||
objectVar->objectType = ObjectType_NewByName(self->connection,
|
||||
typeNameObj);
|
||||
if (!objectVar->objectType) {
|
||||
if (ObjectVar_SetType(objectVar, typeNameObj) < 0) {
|
||||
Py_DECREF(var);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -18,8 +18,6 @@ typedef struct {
|
||||
udt_Buffer numberToStringFormatBuffer;
|
||||
udt_Buffer numberFromStringFormatBuffer;
|
||||
udt_Buffer nlsNumericCharactersBuffer;
|
||||
OCIString *tempStringValue;
|
||||
OCIDateTime *tempTimestampValue;
|
||||
} udt_Environment;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -97,14 +95,6 @@ static udt_Environment *Environment_New(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// create the temp datetime handle (used for converting timestamps in
|
||||
// objects)
|
||||
status = OCIDescriptorAlloc(handle, (dvoid**) &env->tempTimestampValue,
|
||||
OCI_DTYPE_TIMESTAMP, 0, 0);
|
||||
if (Environment_CheckForError(env, status,
|
||||
"Environment_New(): create timestamp descriptor") < 0)
|
||||
return NULL;
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
|
||||
217
Object.c
217
Object.c
@ -24,6 +24,7 @@ static int Object_SetAttr(udt_Object*, PyObject*, PyObject*);
|
||||
static PyObject *Object_ConvertToPython(udt_Environment*, OCITypeCode, dvoid*,
|
||||
dvoid*, udt_ObjectType*);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Declaration of external object variable members.
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -32,6 +33,7 @@ static PyMemberDef g_ObjectMembers[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Python type declaration
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -68,6 +70,41 @@ static PyTypeObject g_ObjectType = {
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Declaration of attribute data union
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef union {
|
||||
OCINumber numberValue;
|
||||
OCIDate dateValue;
|
||||
OCIDateTime *timestampValue;
|
||||
OCIString *stringValue;
|
||||
} udt_AttributeData;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AttributeData_Free()
|
||||
// Free any memory that was allocated by the convert from Python calls.
|
||||
//-----------------------------------------------------------------------------
|
||||
static void AttributeData_Free(
|
||||
udt_Environment *environment, // environment object
|
||||
udt_AttributeData *data, // data structure to initialize
|
||||
OCITypeCode typeCode) // type of Oracle data
|
||||
{
|
||||
switch (typeCode) {
|
||||
case OCI_TYPECODE_CHAR:
|
||||
case OCI_TYPECODE_VARCHAR:
|
||||
case OCI_TYPECODE_VARCHAR2:
|
||||
if (data->stringValue)
|
||||
OCIStringResize(environment->handle, environment->errorHandle,
|
||||
0, &data->stringValue);
|
||||
break;
|
||||
case OCI_TYPECODE_TIMESTAMP:
|
||||
if (data->timestampValue)
|
||||
OCIDescriptorFree(data->timestampValue, OCI_DTYPE_TIMESTAMP);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object_New()
|
||||
// Create a new object.
|
||||
@ -184,6 +221,103 @@ static PyObject *Object_ConvertCollection(
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object_ConvertFromPython()
|
||||
// Convert a Python value to an Oracle value.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int Object_ConvertFromPython(
|
||||
udt_Environment *environment, // environment to use
|
||||
PyObject *pythonValue, // Python value to convert
|
||||
OCITypeCode typeCode, // type of Oracle data
|
||||
udt_AttributeData *oracleValue, // Oracle value
|
||||
dvoid **ociValue, // OCI value
|
||||
OCIInd *ociValueIndicator, // OCI value indicator
|
||||
dvoid **ociObjectIndicator, // OCI object indicator
|
||||
udt_ObjectType *subType) // sub type (for sub objects)
|
||||
{
|
||||
udt_Object *objectValue;
|
||||
udt_Buffer buffer;
|
||||
sword status;
|
||||
|
||||
// None is treated as null
|
||||
if (pythonValue == Py_None) {
|
||||
*ociValueIndicator = OCI_IND_NULL;
|
||||
|
||||
// all other values need to be converted
|
||||
} else {
|
||||
|
||||
*ociValueIndicator = OCI_IND_NOTNULL;
|
||||
switch (typeCode) {
|
||||
case OCI_TYPECODE_CHAR:
|
||||
case OCI_TYPECODE_VARCHAR:
|
||||
case OCI_TYPECODE_VARCHAR2:
|
||||
oracleValue->stringValue = NULL;
|
||||
if (cxBuffer_FromObject(&buffer, pythonValue,
|
||||
environment->encoding) < 0)
|
||||
return -1;
|
||||
status = OCIStringAssignText(environment->handle,
|
||||
environment->errorHandle, buffer.ptr,
|
||||
buffer.size, &oracleValue->stringValue);
|
||||
cxBuffer_Clear(&buffer);
|
||||
if (Environment_CheckForError(environment, status,
|
||||
"Object_ConvertFromPython(): assigning string") < 0)
|
||||
return -1;
|
||||
*ociValue = oracleValue->stringValue;
|
||||
break;
|
||||
case OCI_TYPECODE_NUMBER:
|
||||
if (PythonNumberToOracleNumber(environment,
|
||||
pythonValue, &oracleValue->numberValue) < 0)
|
||||
return -1;
|
||||
*ociValue = &oracleValue->numberValue;
|
||||
break;
|
||||
case OCI_TYPECODE_DATE:
|
||||
if (PythonDateToOracleDate(pythonValue,
|
||||
&oracleValue->dateValue) < 0)
|
||||
return -1;
|
||||
*ociValue = &oracleValue->dateValue;
|
||||
break;
|
||||
case OCI_TYPECODE_TIMESTAMP:
|
||||
oracleValue->timestampValue = NULL;
|
||||
status = OCIDescriptorAlloc(environment->handle,
|
||||
(dvoid**) &oracleValue->timestampValue,
|
||||
OCI_DTYPE_TIMESTAMP, 0, 0);
|
||||
if (Environment_CheckForError(environment, status,
|
||||
"Object_ConvertFromPython(): "
|
||||
"create timestamp descriptor") < 0)
|
||||
return -1;
|
||||
if (PythonDateToOracleTimestamp(environment,
|
||||
pythonValue, oracleValue->timestampValue) < 0)
|
||||
return -1;
|
||||
*ociValue = oracleValue->timestampValue;
|
||||
break;
|
||||
case OCI_TYPECODE_OBJECT:
|
||||
if (Py_TYPE(pythonValue) != &g_ObjectType) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"expecting cx_Oracle.Object");
|
||||
return -1;
|
||||
}
|
||||
objectValue = (udt_Object*) pythonValue;
|
||||
if (objectValue->objectType->tdo != subType->tdo) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"expecting an object of the correct type");
|
||||
return -1;
|
||||
}
|
||||
*ociValue = objectValue->instance;
|
||||
*ociObjectIndicator = objectValue->indicator;
|
||||
break;
|
||||
default:
|
||||
PyErr_Format(g_NotSupportedErrorException,
|
||||
"Object_ConvertFromPython(): unhandled data type %d",
|
||||
typeCode);
|
||||
return -1;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Object_ConvertToPython()
|
||||
// Convert an Oracle value to a Python value.
|
||||
@ -282,85 +416,38 @@ static int Object_SetAttributeValue(
|
||||
udt_ObjectAttribute *attribute, // attribute to set
|
||||
PyObject *value) // value to set
|
||||
{
|
||||
dvoid *ociValueIndicator, *ociValue;
|
||||
OCIInd ociScalarValueIndicator;
|
||||
dvoid *ociObjectIndicator, *ociValue;
|
||||
udt_AttributeData attributeData;
|
||||
OCIInd ociValueIndicator;
|
||||
udt_Connection *connection;
|
||||
OCINumber numericValue;
|
||||
udt_Buffer buffer;
|
||||
OCIDate dateValue;
|
||||
sword status;
|
||||
|
||||
// initialization
|
||||
ociValue = NULL;
|
||||
ociValueIndicator = NULL;
|
||||
// convert from Python
|
||||
ociValue = ociObjectIndicator = NULL;
|
||||
connection = self->objectType->connection;
|
||||
|
||||
// None is treated as null
|
||||
if (value == Py_None) {
|
||||
ociScalarValueIndicator = OCI_IND_NULL;
|
||||
|
||||
// all other values need to be converted
|
||||
} else {
|
||||
|
||||
ociScalarValueIndicator = OCI_IND_NOTNULL;
|
||||
switch (attribute->typeCode) {
|
||||
case OCI_TYPECODE_CHAR:
|
||||
case OCI_TYPECODE_VARCHAR:
|
||||
case OCI_TYPECODE_VARCHAR2:
|
||||
if (cxBuffer_FromObject(&buffer, value,
|
||||
connection->environment->encoding) < 0)
|
||||
return -1;
|
||||
status = OCIStringAssignText(connection->environment->handle,
|
||||
connection->environment->errorHandle, buffer.ptr,
|
||||
buffer.size,
|
||||
&connection->environment->tempStringValue);
|
||||
cxBuffer_Clear(&buffer);
|
||||
if (Environment_CheckForError(connection->environment, status,
|
||||
"Object_SetAttributeValue(): assigning string") < 0)
|
||||
return -1;
|
||||
ociValue = connection->environment->tempStringValue;
|
||||
break;
|
||||
case OCI_TYPECODE_NUMBER:
|
||||
ociValue = &numericValue;
|
||||
if (PythonNumberToOracleNumber(connection->environment, value,
|
||||
ociValue) < 0)
|
||||
return -1;
|
||||
break;
|
||||
case OCI_TYPECODE_DATE:
|
||||
ociValue = &dateValue;
|
||||
if (PythonDateToOracleDate(value, ociValue) < 0)
|
||||
return -1;
|
||||
break;
|
||||
case OCI_TYPECODE_TIMESTAMP:
|
||||
ociValue = connection->environment->tempTimestampValue;
|
||||
if (PythonDateToOracleTimestamp(connection->environment, value,
|
||||
ociValue) < 0)
|
||||
return -1;
|
||||
break;
|
||||
case OCI_TYPECODE_OBJECT:
|
||||
break;
|
||||
case OCI_TYPECODE_NAMEDCOLLECTION:
|
||||
break;
|
||||
};
|
||||
|
||||
if (!ociValue) {
|
||||
PyErr_Format(g_NotSupportedErrorException,
|
||||
"Object_SetAttributeValue(): unhandled data type %d",
|
||||
attribute->typeCode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Object_ConvertFromPython(connection->environment, value,
|
||||
attribute->typeCode, &attributeData, &ociValue, &ociValueIndicator,
|
||||
&ociObjectIndicator, attribute->subType) < 0) {
|
||||
AttributeData_Free(connection->environment, &attributeData,
|
||||
attribute->typeCode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// set the value for the attribute
|
||||
if (cxBuffer_FromObject(&buffer, attribute->name,
|
||||
connection->environment->encoding) < 0)
|
||||
connection->environment->encoding) < 0) {
|
||||
AttributeData_Free(connection->environment, &attributeData,
|
||||
attribute->typeCode);
|
||||
return -1;
|
||||
}
|
||||
status = OCIObjectSetAttr(connection->environment->handle,
|
||||
connection->environment->errorHandle, self->instance,
|
||||
self->indicator, self->objectType->tdo,
|
||||
(const OraText**) &buffer.ptr, (ub4*) &buffer.size, 1, 0, 0,
|
||||
ociScalarValueIndicator, ociValueIndicator, ociValue);
|
||||
ociValueIndicator, ociObjectIndicator, ociValue);
|
||||
AttributeData_Free(connection->environment, &attributeData,
|
||||
attribute->typeCode);
|
||||
cxBuffer_Clear(&buffer);
|
||||
if (Environment_CheckForError(connection->environment, status,
|
||||
"Object_SetAttributeValue(): setting value") < 0)
|
||||
|
||||
55
ObjectVar.c
55
ObjectVar.c
@ -29,6 +29,7 @@ static int ObjectVar_PostDefine(udt_ObjectVar*);
|
||||
static int ObjectVar_PostBind(udt_ObjectVar*);
|
||||
static int ObjectVar_PreFetch(udt_ObjectVar*);
|
||||
static int ObjectVar_IsNull(udt_ObjectVar*, unsigned);
|
||||
static int ObjectVar_SetType(udt_ObjectVar*, PyObject*);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// declaration of members for Oracle objects
|
||||
@ -202,6 +203,11 @@ static int ObjectVar_PostBind(
|
||||
{
|
||||
sword status;
|
||||
|
||||
if (!self->objectType) {
|
||||
PyErr_SetString(g_InterfaceErrorException,
|
||||
"object type not associated with bind variable");
|
||||
return -1;
|
||||
}
|
||||
status = OCIBindObject(self->bindHandle, self->environment->errorHandle,
|
||||
self->objectType->tdo, self->data, 0, self->objectIndicator, 0);
|
||||
return Environment_CheckForError(self->environment, status,
|
||||
@ -264,7 +270,7 @@ static int ObjectVar_SetValue(
|
||||
if (!self->objectType) {
|
||||
Py_INCREF(object->objectType);
|
||||
self->objectType = object->objectType;
|
||||
} else if (object->objectType != self->objectType) {
|
||||
} else if (object->objectType->tdo != self->objectType->tdo) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"expecting same type as the variable itself");
|
||||
return -1;
|
||||
@ -311,3 +317,50 @@ static PyObject *ObjectVar_GetValue(
|
||||
return self->objects[pos];
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ObjectVar_SetType()
|
||||
// Internal method used to set the type when creating an object variable.
|
||||
// This will also create the object instances.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int ObjectVar_SetType(
|
||||
udt_ObjectVar *self, // variable to initialize the type
|
||||
PyObject *typeNameObj) // value to set
|
||||
{
|
||||
dvoid *instance, *indicator;
|
||||
sword status;
|
||||
ub4 i;
|
||||
|
||||
// get the object type from the name
|
||||
self->objectType = ObjectType_NewByName(self->connection, typeNameObj);
|
||||
if (!self->objectType)
|
||||
return -1;
|
||||
|
||||
// initialize the object instances
|
||||
for (i = 0; i < self->allocatedElements; i++) {
|
||||
|
||||
// create the object instance
|
||||
status = OCIObjectNew(self->connection->environment->handle,
|
||||
self->connection->environment->errorHandle,
|
||||
self->connection->handle, OCI_TYPECODE_OBJECT,
|
||||
self->objectType->tdo, NULL, OCI_DURATION_SESSION, TRUE,
|
||||
&instance);
|
||||
if (Environment_CheckForError(self->connection->environment, status,
|
||||
"ObjectVar_SetType(): create object instance") < 0)
|
||||
return -1;
|
||||
self->data[i] = instance;
|
||||
|
||||
// get the null indicator structure
|
||||
status = OCIObjectGetInd(self->connection->environment->handle,
|
||||
self->connection->environment->errorHandle, instance,
|
||||
&indicator);
|
||||
if (Environment_CheckForError(self->connection->environment, status,
|
||||
"ObjectVar_SetType(): get indicator structure") < 0)
|
||||
return -1;
|
||||
*((OCIInd*) indicator) = OCI_IND_NULL;
|
||||
self->objectIndicator[i] = indicator;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -441,6 +441,7 @@ static PyObject *Module_Initialize(void)
|
||||
ADD_TYPE_OBJECT("Date", PyDateTimeAPI->DateType)
|
||||
ADD_TYPE_OBJECT("SessionPool", &g_SessionPoolType)
|
||||
ADD_TYPE_OBJECT("_Error", &g_ErrorType)
|
||||
ADD_TYPE_OBJECT("Object", &g_ObjectType)
|
||||
|
||||
// the name "connect" is required by the DB API
|
||||
ADD_TYPE_OBJECT("connect", &g_ConnectionType)
|
||||
|
||||
@ -1,22 +1,77 @@
|
||||
"""Module for testing object variables."""
|
||||
|
||||
import sys
|
||||
import cx_Oracle
|
||||
import datetime
|
||||
|
||||
class TestObjectVar(BaseTestCase):
|
||||
|
||||
def __GetObjectAsTuple(self, obj):
|
||||
attributeValues = []
|
||||
for attribute in obj.type.attributes:
|
||||
value = getattr(obj, attribute.name)
|
||||
if isinstance(value, cx_Oracle.Object):
|
||||
value = self.__GetObjectAsTuple(value)
|
||||
elif isinstance(value, list):
|
||||
subValue = []
|
||||
for v in value:
|
||||
if isinstance(v, cx_Oracle.Object):
|
||||
v = self.__GetObjectAsTuple(v)
|
||||
subValue.append(v)
|
||||
value = subValue
|
||||
attributeValues.append(value)
|
||||
return tuple(attributeValues)
|
||||
|
||||
def __TestData(self, expectedIntValue, expectedObjectValue,
|
||||
expectedArrayValue):
|
||||
intValue, objectValue, arrayValue = self.cursor.fetchone()
|
||||
if objectValue is not None:
|
||||
attributeValues = []
|
||||
for attribute in objectValue.type.attributes:
|
||||
value = getattr(objectValue, attribute.name)
|
||||
attributeValues.append(value)
|
||||
objectValue = tuple(attributeValues)
|
||||
objectValue = self.__GetObjectAsTuple(objectValue)
|
||||
self.assertEqual(intValue, expectedIntValue)
|
||||
self.assertEqual(objectValue, expectedObjectValue)
|
||||
self.assertEqual(arrayValue, expectedArrayValue)
|
||||
|
||||
def testBindNullIn(self):
|
||||
"test binding a null value (IN)"
|
||||
var = self.cursor.var(cx_Oracle.OBJECT, typename = "UDT_OBJECT")
|
||||
result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
|
||||
(var,))
|
||||
self.assertEqual(result, "null")
|
||||
|
||||
def testBindObjectIn(self):
|
||||
"test binding an object (IN)"
|
||||
typeObj = self.connection.gettype("UDT_OBJECT")
|
||||
obj = typeObj.newobject()
|
||||
obj.NUMBERVALUE = 13
|
||||
obj.STRINGVALUE = "Test String"
|
||||
result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
|
||||
(obj,))
|
||||
self.assertEqual(result,
|
||||
"udt_Object(13, 'Test String', null, null, null, null, null)")
|
||||
obj.NUMBERVALUE = None
|
||||
obj.STRINGVALUE = "Test With Dates"
|
||||
obj.DATEVALUE = datetime.datetime(2016, 2, 10)
|
||||
obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 10, 14, 13, 50)
|
||||
result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
|
||||
(obj,))
|
||||
self.assertEqual(result,
|
||||
"udt_Object(null, 'Test With Dates', null, " \
|
||||
"to_date('2016-02-10', 'YYYY-MM-DD'), " \
|
||||
"to_timestamp('2016-02-10 14:13:50', " \
|
||||
"'YYYY-MM-DD HH24:MI:SS'), " \
|
||||
"null, null)")
|
||||
obj.DATEVALUE = None
|
||||
obj.TIMESTAMPVALUE = None
|
||||
subTypeObj = self.connection.gettype("UDT_SUBOBJECT")
|
||||
subObj = subTypeObj.newobject()
|
||||
subObj.SUBNUMBERVALUE = 15
|
||||
subObj.SUBSTRINGVALUE = "Sub String"
|
||||
obj.SUBOBJECTVALUE = subObj
|
||||
result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
|
||||
(obj,))
|
||||
self.assertEqual(result,
|
||||
"udt_Object(null, 'Test With Dates', null, null, null, " \
|
||||
"udt_SubObject(15, 'Sub String'), null)")
|
||||
|
||||
def testFetchData(self):
|
||||
"test fetching objects"
|
||||
self.cursor.execute("""
|
||||
@ -32,11 +87,28 @@ class TestObjectVar(BaseTestCase):
|
||||
('ARRAYCOL', cx_Oracle.OBJECT, -1, 2000, 0, 0, 1) ])
|
||||
self.__TestData(1, (1, 'First row', 'First ',
|
||||
cx_Oracle.Timestamp(2007, 3, 6, 0, 0, 0),
|
||||
cx_Oracle.Timestamp(2008, 9, 12, 16, 40)), [5, 10, None, 20])
|
||||
cx_Oracle.Timestamp(2008, 9, 12, 16, 40),
|
||||
(11, 'Sub object 1'),
|
||||
[(5, 'first element'), (6, 'second element')]),
|
||||
[5, 10, None, 20])
|
||||
self.__TestData(2, None, [3, None, 9, 12, 15])
|
||||
self.__TestData(3, (3, 'Third row', 'Third ',
|
||||
cx_Oracle.Timestamp(2007, 6, 21, 0, 0, 0),
|
||||
cx_Oracle.Timestamp(2007, 12, 13, 7, 30, 45)), None)
|
||||
cx_Oracle.Timestamp(2007, 12, 13, 7, 30, 45),
|
||||
(13, 'Sub object 3'),
|
||||
[(10, 'element #1'), (20, 'element #2'),
|
||||
(30, 'element #3'), (40, 'element #4')]), None)
|
||||
|
||||
def testGetObjectType(self):
|
||||
"test getting object type"
|
||||
typeObj = self.connection.gettype("UDT_OBJECT")
|
||||
self.assertEqual(typeObj.schema, "CX_ORACLE")
|
||||
self.assertEqual(typeObj.name, "UDT_OBJECT")
|
||||
expectedAttributeNames = ["NUMBERVALUE", "STRINGVALUE",
|
||||
"FIXEDCHARVALUE", "DATEVALUE", "TIMESTAMPVALUE",
|
||||
"SUBOBJECTVALUE", "SUBOBJECTARRAY"]
|
||||
actualAttributeNames = [a.name for a in typeObj.attributes]
|
||||
self.assertEqual(actualAttributeNames, expectedAttributeNames)
|
||||
|
||||
def testObjectType(self):
|
||||
"test object type data"
|
||||
|
||||
@ -38,12 +38,23 @@ grant
|
||||
to cx_Oracle;
|
||||
|
||||
-- create types
|
||||
create type cx_Oracle.udt_SubObject as object (
|
||||
SubNumberValue number,
|
||||
SubStringValue varchar2(60)
|
||||
);
|
||||
/
|
||||
|
||||
create type cx_Oracle.udt_ObjectArray as varray(10) of cx_Oracle.udt_SubObject;
|
||||
/
|
||||
|
||||
create type cx_Oracle.udt_Object as object (
|
||||
NumberValue number,
|
||||
StringValue varchar2(60),
|
||||
FixedCharValue char(10),
|
||||
DateValue date,
|
||||
TimestampValue timestamp
|
||||
TimestampValue timestamp,
|
||||
SubObjectValue cx_Oracle.udt_SubObject,
|
||||
SubObjectArray cx_Oracle.udt_ObjectArray
|
||||
);
|
||||
/
|
||||
|
||||
@ -233,7 +244,11 @@ end;
|
||||
insert into cx_Oracle.TestObjects values (1,
|
||||
cx_Oracle.udt_Object(1, 'First row', 'First',
|
||||
to_date(20070306, 'YYYYMMDD'),
|
||||
to_timestamp('20080912 16:40:00', 'YYYYMMDD HH24:MI:SS')),
|
||||
to_timestamp('20080912 16:40:00', 'YYYYMMDD HH24:MI:SS'),
|
||||
cx_Oracle.udt_SubObject(11, 'Sub object 1'),
|
||||
cx_Oracle.udt_ObjectArray(
|
||||
cx_Oracle.udt_SubObject(5, 'first element'),
|
||||
cx_Oracle.udt_SubObject(6, 'second element'))),
|
||||
cx_Oracle.udt_Array(5, 10, null, 20));
|
||||
|
||||
insert into cx_Oracle.TestObjects values (2, null,
|
||||
@ -242,7 +257,13 @@ insert into cx_Oracle.TestObjects values (2, null,
|
||||
insert into cx_Oracle.TestObjects values (3,
|
||||
cx_Oracle.udt_Object(3, 'Third row', 'Third',
|
||||
to_date(20070621, 'YYYYMMDD'),
|
||||
to_timestamp('20071213 07:30:45', 'YYYYMMDD HH24:MI:SS')), null);
|
||||
to_timestamp('20071213 07:30:45', 'YYYYMMDD HH24:MI:SS'),
|
||||
cx_Oracle.udt_SubObject(13, 'Sub object 3'),
|
||||
cx_Oracle.udt_ObjectArray(
|
||||
cx_Oracle.udt_SubObject(10, 'element #1'),
|
||||
cx_Oracle.udt_SubObject(20, 'element #2'),
|
||||
cx_Oracle.udt_SubObject(30, 'element #3'),
|
||||
cx_Oracle.udt_SubObject(40, 'element #4'))), null);
|
||||
|
||||
commit;
|
||||
|
||||
@ -597,5 +618,75 @@ create or replace package body cx_Oracle.pkg_TestBooleans as
|
||||
end;
|
||||
/
|
||||
|
||||
create or replace package cx_Oracle.pkg_TestBindObject as
|
||||
|
||||
function GetStringRep (
|
||||
a_Object udt_Object
|
||||
) return varchar2;
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
create or replace package body cx_Oracle.pkg_TestBindObject as
|
||||
|
||||
function GetStringRep (
|
||||
a_Object udt_SubObject
|
||||
) return varchar2 is
|
||||
begin
|
||||
if a_Object is null then
|
||||
return 'null';
|
||||
end if;
|
||||
return 'udt_SubObject(' ||
|
||||
nvl(to_char(a_Object.SubNumberValue), 'null') || ', ' ||
|
||||
case when a_Object.SubStringValue is null then 'null'
|
||||
else '''' || a_Object.SubStringValue || '''' end || ')';
|
||||
end;
|
||||
|
||||
function GetStringRep (
|
||||
a_Array udt_ObjectArray
|
||||
) return varchar2 is
|
||||
t_StringRep varchar2(4000);
|
||||
begin
|
||||
if a_Array is null then
|
||||
return 'null';
|
||||
end if;
|
||||
t_StringRep := 'udt_ObjectArray(';
|
||||
for i in 1..a_Array.count loop
|
||||
if i > 1 then
|
||||
t_StringRep := t_StringRep || ', ';
|
||||
end if;
|
||||
t_StringRep := t_StringRep || GetStringRep(a_Array(i));
|
||||
end loop;
|
||||
return t_StringRep || ')';
|
||||
end;
|
||||
|
||||
function GetStringRep (
|
||||
a_Object udt_Object
|
||||
) return varchar2 is
|
||||
begin
|
||||
if a_Object is null then
|
||||
return 'null';
|
||||
end if;
|
||||
return 'udt_Object(' ||
|
||||
nvl(to_char(a_Object.NumberValue), 'null') || ', ' ||
|
||||
case when a_Object.StringValue is null then 'null'
|
||||
else '''' || a_Object.StringValue || '''' end || ', ' ||
|
||||
case when a_Object.FixedCharValue is null then 'null'
|
||||
else '''' || a_Object.FixedCharValue || '''' end || ', ' ||
|
||||
case when a_Object.DateValue is null then 'null'
|
||||
else 'to_date(''' ||
|
||||
to_char(a_Object.DateValue, 'YYYY-MM-DD') ||
|
||||
''', ''YYYY-MM-DD'')' end || ', ' ||
|
||||
case when a_Object.TimestampValue is null then 'null'
|
||||
else 'to_timestamp(''' || to_char(a_Object.TimestampValue,
|
||||
'YYYY-MM-DD HH24:MI:SS') ||
|
||||
''', ''YYYY-MM-DD HH24:MI:SS'')' end || ', ' ||
|
||||
GetStringRep(a_Object.SubObjectValue) || ', ' ||
|
||||
GetStringRep(a_Object.SubObjectArray) || ')';
|
||||
end;
|
||||
|
||||
end;
|
||||
/
|
||||
|
||||
exit
|
||||
|
||||
|
||||
@ -1,18 +1,30 @@
|
||||
"""Module for testing object variables."""
|
||||
|
||||
import sys
|
||||
import cx_Oracle
|
||||
|
||||
class TestObjectVar(BaseTestCase):
|
||||
|
||||
def __GetObjectAsTuple(self, obj):
|
||||
attributeValues = []
|
||||
for attribute in obj.type.attributes:
|
||||
value = getattr(obj, attribute.name)
|
||||
if isinstance(value, cx_Oracle.Object):
|
||||
value = self.__GetObjectAsTuple(value)
|
||||
elif isinstance(value, list):
|
||||
subValue = []
|
||||
for v in value:
|
||||
if isinstance(v, cx_Oracle.Object):
|
||||
v = self.__GetObjectAsTuple(v)
|
||||
subValue.append(v)
|
||||
value = subValue
|
||||
attributeValues.append(value)
|
||||
return tuple(attributeValues)
|
||||
|
||||
def __TestData(self, expectedIntValue, expectedObjectValue,
|
||||
expectedArrayValue):
|
||||
intValue, objectValue, arrayValue = self.cursor.fetchone()
|
||||
if objectValue is not None:
|
||||
attributeValues = []
|
||||
for attribute in objectValue.type.attributes:
|
||||
value = getattr(objectValue, attribute.name)
|
||||
attributeValues.append(value)
|
||||
objectValue = tuple(attributeValues)
|
||||
objectValue = self.__GetObjectAsTuple(objectValue)
|
||||
self.assertEqual(intValue, expectedIntValue)
|
||||
self.assertEqual(objectValue, expectedObjectValue)
|
||||
self.assertEqual(arrayValue, expectedArrayValue)
|
||||
@ -32,11 +44,17 @@ class TestObjectVar(BaseTestCase):
|
||||
(u'ARRAYCOL', cx_Oracle.OBJECT, -1, 2000, 0, 0, 1) ])
|
||||
self.__TestData(1, (1, u'First row', u'First ',
|
||||
cx_Oracle.Timestamp(2007, 3, 6, 0, 0, 0),
|
||||
cx_Oracle.Timestamp(2008, 9, 12, 16, 40)), [5, 10, None, 20])
|
||||
cx_Oracle.Timestamp(2008, 9, 12, 16, 40),
|
||||
(11, 'Sub object 1'),
|
||||
[(5, 'first element'), (6, 'second element')]),
|
||||
[5, 10, None, 20])
|
||||
self.__TestData(2, None, [3, None, 9, 12, 15])
|
||||
self.__TestData(3, (3, u'Third row', u'Third ',
|
||||
cx_Oracle.Timestamp(2007, 6, 21, 0, 0, 0),
|
||||
cx_Oracle.Timestamp(2007, 12, 13, 7, 30, 45)), None)
|
||||
cx_Oracle.Timestamp(2007, 12, 13, 7, 30, 45),
|
||||
(13, 'Sub object 3'),
|
||||
[(10, 'element #1'), (20, 'element #2'),
|
||||
(30, 'element #3'), (40, 'element #4')]), None)
|
||||
|
||||
def testObjectType(self):
|
||||
"test object type data"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user