Added support for DML returning of multiple rows using cursor.executemany().
This commit is contained in:
parent
b753ca7bdf
commit
c61973c28f
@ -16,6 +16,10 @@ Module Interface
|
|||||||
the connection when the block is completed. This will become the default
|
the connection when the block is completed. This will become the default
|
||||||
behavior in cx_Oracle 7.
|
behavior in cx_Oracle 7.
|
||||||
|
|
||||||
|
- dml_ret_array_val -- if this value is True, variables bound to a DML
|
||||||
|
returning statement (and have not had any values set on them) will return
|
||||||
|
an array. This will become the default behavior in cx_Oracle 7.
|
||||||
|
|
||||||
All other attributes will silently ignore being set and will always appear
|
All other attributes will silently ignore being set and will always appear
|
||||||
to have the value None.
|
to have the value None.
|
||||||
|
|
||||||
|
|||||||
2
odpi
2
odpi
@ -1 +1 @@
|
|||||||
Subproject commit 1136e4751571e6f5ccea93e321b169a6899409ec
|
Subproject commit e4a722cf100c8e28a07abf97a06f4f28fdd6cd20
|
||||||
@ -46,7 +46,6 @@ static PyObject* cxoCursor_getBatchErrors(cxoCursor*);
|
|||||||
static PyObject *cxoCursor_getArrayDMLRowCounts(cxoCursor*);
|
static PyObject *cxoCursor_getArrayDMLRowCounts(cxoCursor*);
|
||||||
static PyObject *cxoCursor_getImplicitResults(cxoCursor*);
|
static PyObject *cxoCursor_getImplicitResults(cxoCursor*);
|
||||||
static int cxoCursor_performDefine(cxoCursor*, uint32_t);
|
static int cxoCursor_performDefine(cxoCursor*, uint32_t);
|
||||||
static int cxoCursor_getVarData(cxoCursor*);
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -878,7 +877,7 @@ static PyObject *cxoCursor_createRow(cxoCursor *cursor, uint32_t pos)
|
|||||||
// acquire the value for each item
|
// acquire the value for each item
|
||||||
for (i = 0; i < numItems; i++) {
|
for (i = 0; i < numItems; i++) {
|
||||||
var = (cxoVar*) PyList_GET_ITEM(cursor->fetchVariables, i);
|
var = (cxoVar*) PyList_GET_ITEM(cursor->fetchVariables, i);
|
||||||
item = cxoVar_getSingleValue(var, pos);
|
item = cxoVar_getSingleValue(var, var->data, pos);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
Py_DECREF(tuple);
|
Py_DECREF(tuple);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1401,10 +1400,6 @@ static PyObject *cxoCursor_execute(cxoCursor *cursor, PyObject *args,
|
|||||||
return (PyObject*) cursor;
|
return (PyObject*) cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for returning statements, get the variable data for each bound variable
|
|
||||||
if (cursor->stmtInfo.isReturning && cxoCursor_getVarData(cursor) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// for statements other than queries, simply return None
|
// for statements other than queries, simply return None
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
@ -1988,49 +1983,6 @@ static PyObject *cxoCursor_getNext(cxoCursor *cursor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// cxoCursor_getVarData()
|
|
||||||
// Get the data for all variables bound to the cursor. This is needed for a
|
|
||||||
// returning statement which may have changed the number of elements in the
|
|
||||||
// variable and the location of the variable data.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
static int cxoCursor_getVarData(cxoCursor *cursor)
|
|
||||||
{
|
|
||||||
Py_ssize_t i, size, pos;
|
|
||||||
PyObject *key, *value;
|
|
||||||
cxoVar *var;
|
|
||||||
|
|
||||||
// if there are no bind variables, nothing to do
|
|
||||||
if (!cursor->bindVariables)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// handle bind by position
|
|
||||||
if (PyList_Check(cursor->bindVariables)) {
|
|
||||||
size = PyList_GET_SIZE(cursor->bindVariables);
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
var = (cxoVar*) PyList_GET_ITEM(cursor->bindVariables, i);
|
|
||||||
if (dpiVar_getData(var->handle, &var->allocatedElements,
|
|
||||||
&var->data) < 0)
|
|
||||||
return cxoError_raiseAndReturnInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle bind by name
|
|
||||||
} else {
|
|
||||||
pos = 0;
|
|
||||||
while (PyDict_Next(cursor->bindVariables, &pos, &key, &value)) {
|
|
||||||
var = (cxoVar*) value;
|
|
||||||
if (dpiVar_getData(var->handle, &var->allocatedElements,
|
|
||||||
&var->data) < 0)
|
|
||||||
return cxoError_raiseAndReturnInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// cxoCursor_getBatchErrors()
|
// cxoCursor_getBatchErrors()
|
||||||
// Returns a list of batch error objects.
|
// Returns a list of batch error objects.
|
||||||
|
|||||||
@ -69,6 +69,8 @@ static PyObject *cxoFuture_getAttr(cxoFuture *obj, PyObject *nameObject)
|
|||||||
return NULL;
|
return NULL;
|
||||||
if (strncmp(buffer.ptr, "ctx_mgr_close", buffer.size) == 0)
|
if (strncmp(buffer.ptr, "ctx_mgr_close", buffer.size) == 0)
|
||||||
result = PyBool_FromLong(obj->contextManagerClose);
|
result = PyBool_FromLong(obj->contextManagerClose);
|
||||||
|
else if (strncmp(buffer.ptr, "dml_ret_array_val", buffer.size) == 0)
|
||||||
|
result = PyBool_FromLong(obj->dmlReturningArray);
|
||||||
else {
|
else {
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
result = Py_None;
|
result = Py_None;
|
||||||
@ -92,6 +94,8 @@ static int cxoFuture_setAttr(cxoFuture *obj, PyObject *nameObject,
|
|||||||
return -1;
|
return -1;
|
||||||
if (strncmp(buffer.ptr, "ctx_mgr_close", buffer.size) == 0)
|
if (strncmp(buffer.ptr, "ctx_mgr_close", buffer.size) == 0)
|
||||||
result = cxoUtils_getBooleanValue(value, 0, &obj->contextManagerClose);
|
result = cxoUtils_getBooleanValue(value, 0, &obj->contextManagerClose);
|
||||||
|
else if (strncmp(buffer.ptr, "dml_ret_array_val", buffer.size) == 0)
|
||||||
|
result = cxoUtils_getBooleanValue(value, 0, &obj->dmlReturningArray);
|
||||||
cxoBuffer_clear(&buffer);
|
cxoBuffer_clear(&buffer);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -392,7 +392,6 @@ static PyObject *cxoModule_initialize(void)
|
|||||||
cxoFutureObj = (cxoFuture*) cxoPyTypeFuture.tp_alloc(&cxoPyTypeFuture, 0);
|
cxoFutureObj = (cxoFuture*) cxoPyTypeFuture.tp_alloc(&cxoPyTypeFuture, 0);
|
||||||
if (!cxoFutureObj)
|
if (!cxoFutureObj)
|
||||||
return NULL;
|
return NULL;
|
||||||
cxoFutureObj->contextManagerClose = 0;
|
|
||||||
if (PyModule_AddObject(module, "__future__", (PyObject*) cxoFutureObj) < 0)
|
if (PyModule_AddObject(module, "__future__", (PyObject*) cxoFutureObj) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
|||||||
@ -261,6 +261,7 @@ struct cxoEnqOptions {
|
|||||||
struct cxoFuture {
|
struct cxoFuture {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
int contextManagerClose;
|
int contextManagerClose;
|
||||||
|
int dmlReturningArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cxoLob {
|
struct cxoLob {
|
||||||
@ -377,6 +378,8 @@ struct cxoVar {
|
|||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t bufferSize;
|
uint32_t bufferSize;
|
||||||
int isArray;
|
int isArray;
|
||||||
|
int isValueSet;
|
||||||
|
int getReturnedData;
|
||||||
cxoVarType *type;
|
cxoVarType *type;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -457,7 +460,7 @@ cxoVarType *cxoVarType_fromPythonValue(PyObject *value, int *isArray,
|
|||||||
|
|
||||||
int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos);
|
int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos);
|
||||||
int cxoVar_check(PyObject *object);
|
int cxoVar_check(PyObject *object);
|
||||||
PyObject *cxoVar_getSingleValue(cxoVar *var, uint32_t arrayPos);
|
PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos);
|
||||||
PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos);
|
PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos);
|
||||||
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
|
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
|
||||||
Py_ssize_t size, int isArray, cxoObjectType *objType);
|
Py_ssize_t size, int isArray, cxoObjectType *objType);
|
||||||
|
|||||||
127
src/cxoVar.c
127
src/cxoVar.c
@ -379,28 +379,81 @@ int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos)
|
|||||||
if (status < 0)
|
if (status < 0)
|
||||||
return cxoError_raiseAndReturnInt();
|
return cxoError_raiseAndReturnInt();
|
||||||
|
|
||||||
|
// set flag if bound to a DML returning statement and no data set
|
||||||
|
if (cursor->stmtInfo.isReturning && !var->isValueSet)
|
||||||
|
var->getReturnedData = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoVar_getArrayValue()
|
||||||
|
// Return the value of the variable as an array.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoVar_getArrayValue(cxoVar *var, uint32_t numElements,
|
||||||
|
dpiData *data)
|
||||||
|
{
|
||||||
|
PyObject *value, *singleValue;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
// use the first set of returned values if DML returning as array is not
|
||||||
|
// enabled
|
||||||
|
if (!(cxoFutureObj && cxoFutureObj->dmlReturningArray) &&
|
||||||
|
var->getReturnedData && !data) {
|
||||||
|
if (dpiVar_getReturnedData(var->handle, 0, &numElements, &data) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
value = PyList_New(numElements);
|
||||||
|
if (!value)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < numElements; i++) {
|
||||||
|
singleValue = cxoVar_getSingleValue(var, data, i);
|
||||||
|
if (!singleValue) {
|
||||||
|
Py_DECREF(value);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyList_SET_ITEM(value, i, singleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// cxoVar_getSingleValue()
|
// cxoVar_getSingleValue()
|
||||||
// Return the value of the variable at the given position.
|
// Return the value of the variable at the given position.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
PyObject *cxoVar_getSingleValue(cxoVar *var, uint32_t arrayPos)
|
PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos)
|
||||||
{
|
{
|
||||||
PyObject *value, *result;
|
PyObject *value, *result;
|
||||||
dpiData *data;
|
uint32_t numReturnedRows;
|
||||||
|
dpiData *returnedData;
|
||||||
|
|
||||||
// ensure we do not exceed the number of allocated elements
|
// handle DML returning
|
||||||
if (arrayPos >= var->allocatedElements) {
|
if (!data && var->getReturnedData) {
|
||||||
PyErr_SetString(PyExc_IndexError,
|
if (cxoFutureObj && cxoFutureObj->dmlReturningArray) {
|
||||||
"cxoVar_getSingleValue: array size exceeded");
|
if (dpiVar_getReturnedData(var->handle, arrayPos, &numReturnedRows,
|
||||||
return NULL;
|
&returnedData) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
return cxoVar_getArrayValue(var, numReturnedRows, returnedData);
|
||||||
|
}
|
||||||
|
if (dpiVar_getReturnedData(var->handle, 0, &numReturnedRows,
|
||||||
|
&data) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (arrayPos >= numReturnedRows) {
|
||||||
|
PyErr_SetString(PyExc_IndexError,
|
||||||
|
"cxoVar_getSingleValue: array size exceeded");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the value
|
// in all other cases, just get the value stored at specified position
|
||||||
data = &var->data[arrayPos];
|
if (data)
|
||||||
|
data = &data[arrayPos];
|
||||||
|
else data = &var->data[arrayPos];
|
||||||
if (data->isNull)
|
if (data->isNull)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
value = cxoTransform_toPython(var->type->transformNum, var->connection,
|
value = cxoTransform_toPython(var->type->transformNum, var->connection,
|
||||||
@ -431,33 +484,6 @@ PyObject *cxoVar_getSingleValue(cxoVar *var, uint32_t arrayPos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// cxoVar_getArrayValue()
|
|
||||||
// Return the value of the variable as an array.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
static PyObject *cxoVar_getArrayValue(cxoVar *var,
|
|
||||||
uint32_t numElements)
|
|
||||||
{
|
|
||||||
PyObject *value, *singleValue;
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
value = PyList_New(numElements);
|
|
||||||
if (!value)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (i = 0; i < numElements; i++) {
|
|
||||||
singleValue = cxoVar_getSingleValue(var, i);
|
|
||||||
if (!singleValue) {
|
|
||||||
Py_DECREF(value);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyList_SET_ITEM(value, i, singleValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// cxoVar_getValue()
|
// cxoVar_getValue()
|
||||||
// Return the value of the variable.
|
// Return the value of the variable.
|
||||||
@ -469,10 +495,14 @@ PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos)
|
|||||||
if (var->isArray) {
|
if (var->isArray) {
|
||||||
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
|
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
|
||||||
return cxoError_raiseAndReturnNull();
|
return cxoError_raiseAndReturnNull();
|
||||||
return cxoVar_getArrayValue(var, numElements);
|
return cxoVar_getArrayValue(var, numElements, var->data);
|
||||||
}
|
}
|
||||||
|
if (arrayPos >= var->allocatedElements) {
|
||||||
return cxoVar_getSingleValue(var, arrayPos);
|
PyErr_SetString(PyExc_IndexError,
|
||||||
|
"cxoVar_getSingleValue: array size exceeded");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return cxoVar_getSingleValue(var, NULL, arrayPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -659,6 +689,7 @@ static int cxoVar_setArrayValue(cxoVar *var, PyObject *value)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value)
|
int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value)
|
||||||
{
|
{
|
||||||
|
var->isValueSet = 1;
|
||||||
if (var->isArray) {
|
if (var->isArray) {
|
||||||
if (arrayPos > 0) {
|
if (arrayPos > 0) {
|
||||||
PyErr_SetString(cxoNotSupportedErrorException,
|
PyErr_SetString(cxoNotSupportedErrorException,
|
||||||
@ -737,9 +768,10 @@ static PyObject *cxoVar_externalGetValue(cxoVar *var, PyObject *args,
|
|||||||
static PyObject *cxoVar_externalGetActualElements(cxoVar *var,
|
static PyObject *cxoVar_externalGetActualElements(cxoVar *var,
|
||||||
void *unused)
|
void *unused)
|
||||||
{
|
{
|
||||||
uint32_t numElements;
|
uint32_t numElements = var->allocatedElements;
|
||||||
|
|
||||||
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
|
if (var->isArray &&
|
||||||
|
dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
|
||||||
return cxoError_raiseAndReturnNull();
|
return cxoError_raiseAndReturnNull();
|
||||||
return PyInt_FromLong(numElements);
|
return PyInt_FromLong(numElements);
|
||||||
}
|
}
|
||||||
@ -751,11 +783,12 @@ static PyObject *cxoVar_externalGetActualElements(cxoVar *var,
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
static PyObject *cxoVar_externalGetValues(cxoVar *var, void *unused)
|
static PyObject *cxoVar_externalGetValues(cxoVar *var, void *unused)
|
||||||
{
|
{
|
||||||
uint32_t numElements;
|
uint32_t numElements = var->allocatedElements;
|
||||||
|
|
||||||
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
|
if (var->isArray &&
|
||||||
|
dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
|
||||||
return cxoError_raiseAndReturnNull();
|
return cxoError_raiseAndReturnNull();
|
||||||
return cxoVar_getArrayValue(var, numElements);
|
return cxoVar_getArrayValue(var, numElements, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -771,10 +804,10 @@ static PyObject *cxoVar_repr(cxoVar *var)
|
|||||||
if (var->isArray) {
|
if (var->isArray) {
|
||||||
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
|
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
|
||||||
return cxoError_raiseAndReturnNull();
|
return cxoError_raiseAndReturnNull();
|
||||||
value = cxoVar_getArrayValue(var, numElements);
|
value = cxoVar_getArrayValue(var, numElements, var->data);
|
||||||
} else if (var->allocatedElements == 1)
|
} else if (var->allocatedElements == 1)
|
||||||
value = cxoVar_getSingleValue(var, 0);
|
value = cxoVar_getSingleValue(var, NULL, 0);
|
||||||
else value = cxoVar_getArrayValue(var, var->allocatedElements);
|
else value = cxoVar_getArrayValue(var, var->allocatedElements, NULL);
|
||||||
if (!value)
|
if (!value)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) {
|
if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import sys
|
|||||||
class TestDMLReturning(BaseTestCase):
|
class TestDMLReturning(BaseTestCase):
|
||||||
|
|
||||||
def testInsert(self):
|
def testInsert(self):
|
||||||
"test insert statement with DML returning"
|
"test insert statement (single row) with DML returning"
|
||||||
self.cursor.execute("truncate table TestTempTable")
|
self.cursor.execute("truncate table TestTempTable")
|
||||||
intVal = 5
|
intVal = 5
|
||||||
strVal = "A test string"
|
strVal = "A test string"
|
||||||
@ -21,6 +21,34 @@ class TestDMLReturning(BaseTestCase):
|
|||||||
strVar = strVar)
|
strVar = strVar)
|
||||||
self.assertEqual(intVar.values, [intVal])
|
self.assertEqual(intVar.values, [intVal])
|
||||||
self.assertEqual(strVar.values, [strVal])
|
self.assertEqual(strVar.values, [strVal])
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = True
|
||||||
|
try:
|
||||||
|
self.assertEqual(intVar.values, [[intVal]])
|
||||||
|
self.assertEqual(strVar.values, [[strVal]])
|
||||||
|
finally:
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = False
|
||||||
|
|
||||||
|
def testInsertMany(self):
|
||||||
|
"test insert statement (multiple rows) with DML returning"
|
||||||
|
self.cursor.execute("truncate table TestTempTable")
|
||||||
|
intValues = [5, 8, 17, 24, 6]
|
||||||
|
strValues = ["Test 5", "Test 8", "Test 17", "Test 24", "Test 6"]
|
||||||
|
intVar = self.cursor.var(cx_Oracle.NUMBER, arraysize = len(intValues))
|
||||||
|
strVar = self.cursor.var(str, arraysize = len(intValues))
|
||||||
|
self.cursor.setinputsizes(None, None, intVar, strVar)
|
||||||
|
data = list(zip(intValues, strValues))
|
||||||
|
self.cursor.executemany("""
|
||||||
|
insert into TestTempTable
|
||||||
|
values (:intVal, :strVal)
|
||||||
|
returning IntCol, StringCol into :intVar, :strVar""", data)
|
||||||
|
self.assertEqual(intVar.values, [intValues[0]])
|
||||||
|
self.assertEqual(strVar.values, [strValues[0]])
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = True
|
||||||
|
try:
|
||||||
|
self.assertEqual(intVar.values, [[v] for v in intValues])
|
||||||
|
self.assertEqual(strVar.values, [[v] for v in strValues])
|
||||||
|
finally:
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = False
|
||||||
|
|
||||||
def testInsertWithSmallSize(self):
|
def testInsertWithSmallSize(self):
|
||||||
"test insert statement with DML returning into too small a variable"
|
"test insert statement with DML returning into too small a variable"
|
||||||
@ -57,6 +85,12 @@ class TestDMLReturning(BaseTestCase):
|
|||||||
strVar = strVar)
|
strVar = strVar)
|
||||||
self.assertEqual(intVar.values, [intVal])
|
self.assertEqual(intVar.values, [intVal])
|
||||||
self.assertEqual(strVar.values, [strVal])
|
self.assertEqual(strVar.values, [strVal])
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = True
|
||||||
|
try:
|
||||||
|
self.assertEqual(intVar.values, [[intVal]])
|
||||||
|
self.assertEqual(strVar.values, [[strVal]])
|
||||||
|
finally:
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = False
|
||||||
|
|
||||||
def testUpdateNoRows(self):
|
def testUpdateNoRows(self):
|
||||||
"test update no rows statement with DML returning"
|
"test update no rows statement with DML returning"
|
||||||
@ -78,6 +112,12 @@ class TestDMLReturning(BaseTestCase):
|
|||||||
strVar = strVar)
|
strVar = strVar)
|
||||||
self.assertEqual(intVar.values, [])
|
self.assertEqual(intVar.values, [])
|
||||||
self.assertEqual(strVar.values, [])
|
self.assertEqual(strVar.values, [])
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = True
|
||||||
|
try:
|
||||||
|
self.assertEqual(intVar.values, [[]])
|
||||||
|
self.assertEqual(strVar.values, [[]])
|
||||||
|
finally:
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = False
|
||||||
|
|
||||||
def testUpdateMultipleRows(self):
|
def testUpdateMultipleRows(self):
|
||||||
"test update multiple rows statement with DML returning"
|
"test update multiple rows statement with DML returning"
|
||||||
@ -101,6 +141,59 @@ class TestDMLReturning(BaseTestCase):
|
|||||||
"The final value of string 9",
|
"The final value of string 9",
|
||||||
"The final value of string 10"
|
"The final value of string 10"
|
||||||
])
|
])
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = True
|
||||||
|
try:
|
||||||
|
self.assertEqual(intVar.values, [[23, 24, 25]])
|
||||||
|
self.assertEqual(strVar.values, [[
|
||||||
|
"The final value of string 8",
|
||||||
|
"The final value of string 9",
|
||||||
|
"The final value of string 10"
|
||||||
|
]])
|
||||||
|
finally:
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = False
|
||||||
|
|
||||||
|
def testUpdateMultipleRowsExecuteMany(self):
|
||||||
|
"test update multiple rows with DML returning (executeMany)"
|
||||||
|
self.cursor.execute("truncate table TestTempTable")
|
||||||
|
for i in range(1, 11):
|
||||||
|
self.cursor.execute("insert into TestTempTable values (:1, :2)",
|
||||||
|
(i, "The initial value of string %d" % i))
|
||||||
|
intVar = self.cursor.var(cx_Oracle.NUMBER, arraysize = 3)
|
||||||
|
strVar = self.cursor.var(str, arraysize = 3)
|
||||||
|
self.cursor.setinputsizes(None, intVar, strVar)
|
||||||
|
self.cursor.executemany("""
|
||||||
|
update TestTempTable set
|
||||||
|
IntCol = IntCol + 25,
|
||||||
|
StringCol = 'Updated value of string ' || to_char(IntCol)
|
||||||
|
where IntCol < :inVal
|
||||||
|
returning IntCol, StringCol into :intVar, :strVar""",
|
||||||
|
[[3], [8], [11]])
|
||||||
|
self.assertEqual(intVar.values, [26, 27])
|
||||||
|
self.assertEqual(strVar.values, [
|
||||||
|
"Updated value of string 1",
|
||||||
|
"Updated value of string 2"
|
||||||
|
])
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = True
|
||||||
|
try:
|
||||||
|
self.assertEqual(intVar.values, [
|
||||||
|
[26, 27],
|
||||||
|
[28, 29, 30, 31, 32],
|
||||||
|
[33, 34, 35]
|
||||||
|
])
|
||||||
|
self.assertEqual(strVar.values, [
|
||||||
|
[ "Updated value of string 1",
|
||||||
|
"Updated value of string 2" ],
|
||||||
|
[ "Updated value of string 3",
|
||||||
|
"Updated value of string 4",
|
||||||
|
"Updated value of string 5",
|
||||||
|
"Updated value of string 6",
|
||||||
|
"Updated value of string 7" ],
|
||||||
|
[ "Updated value of string 8",
|
||||||
|
"Updated value of string 9",
|
||||||
|
"Updated value of string 10" ]
|
||||||
|
])
|
||||||
|
finally:
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = False
|
||||||
|
|
||||||
def testInsertAndReturnObject(self):
|
def testInsertAndReturnObject(self):
|
||||||
"test inserting an object with DML returning"
|
"test inserting an object with DML returning"
|
||||||
@ -116,5 +209,11 @@ class TestDMLReturning(BaseTestCase):
|
|||||||
obj = obj, outObj = outVar)
|
obj = obj, outObj = outVar)
|
||||||
result = outVar.getvalue()
|
result = outVar.getvalue()
|
||||||
self.assertEqual(result.STRINGVALUE, stringValue)
|
self.assertEqual(result.STRINGVALUE, stringValue)
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = True
|
||||||
|
try:
|
||||||
|
result, = outVar.getvalue()
|
||||||
|
self.assertEqual(result.STRINGVALUE, stringValue)
|
||||||
|
finally:
|
||||||
|
cx_Oracle.__future__.dml_ret_array_val = False
|
||||||
self.connection.rollback()
|
self.connection.rollback()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user