Skip to content

Commit c61973c

Browse files
Added support for DML returning of multiple rows using cursor.executemany().
1 parent b753ca7 commit c61973c

8 files changed

Lines changed: 195 additions & 101 deletions

File tree

doc/src/module.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Module Interface
1616
the connection when the block is completed. This will become the default
1717
behavior in cx_Oracle 7.
1818

19+
- dml_ret_array_val -- if this value is True, variables bound to a DML
20+
returning statement (and have not had any values set on them) will return
21+
an array. This will become the default behavior in cx_Oracle 7.
22+
1923
All other attributes will silently ignore being set and will always appear
2024
to have the value None.
2125

src/cxoCursor.c

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ static PyObject* cxoCursor_getBatchErrors(cxoCursor*);
4646
static PyObject *cxoCursor_getArrayDMLRowCounts(cxoCursor*);
4747
static PyObject *cxoCursor_getImplicitResults(cxoCursor*);
4848
static int cxoCursor_performDefine(cxoCursor*, uint32_t);
49-
static int cxoCursor_getVarData(cxoCursor*);
5049

5150

5251
//-----------------------------------------------------------------------------
@@ -878,7 +877,7 @@ static PyObject *cxoCursor_createRow(cxoCursor *cursor, uint32_t pos)
878877
// acquire the value for each item
879878
for (i = 0; i < numItems; i++) {
880879
var = (cxoVar*) PyList_GET_ITEM(cursor->fetchVariables, i);
881-
item = cxoVar_getSingleValue(var, pos);
880+
item = cxoVar_getSingleValue(var, var->data, pos);
882881
if (!item) {
883882
Py_DECREF(tuple);
884883
return NULL;
@@ -1401,10 +1400,6 @@ static PyObject *cxoCursor_execute(cxoCursor *cursor, PyObject *args,
14011400
return (PyObject*) cursor;
14021401
}
14031402

1404-
// for returning statements, get the variable data for each bound variable
1405-
if (cursor->stmtInfo.isReturning && cxoCursor_getVarData(cursor) < 0)
1406-
return NULL;
1407-
14081403
// for statements other than queries, simply return None
14091404
Py_RETURN_NONE;
14101405
}
@@ -1988,49 +1983,6 @@ static PyObject *cxoCursor_getNext(cxoCursor *cursor)
19881983
}
19891984

19901985

1991-
//-----------------------------------------------------------------------------
1992-
// cxoCursor_getVarData()
1993-
// Get the data for all variables bound to the cursor. This is needed for a
1994-
// returning statement which may have changed the number of elements in the
1995-
// variable and the location of the variable data.
1996-
//-----------------------------------------------------------------------------
1997-
static int cxoCursor_getVarData(cxoCursor *cursor)
1998-
{
1999-
Py_ssize_t i, size, pos;
2000-
PyObject *key, *value;
2001-
cxoVar *var;
2002-
2003-
// if there are no bind variables, nothing to do
2004-
if (!cursor->bindVariables)
2005-
return 0;
2006-
2007-
// handle bind by position
2008-
if (PyList_Check(cursor->bindVariables)) {
2009-
size = PyList_GET_SIZE(cursor->bindVariables);
2010-
for (i = 0; i < size; i++) {
2011-
var = (cxoVar*) PyList_GET_ITEM(cursor->bindVariables, i);
2012-
if (dpiVar_getData(var->handle, &var->allocatedElements,
2013-
&var->data) < 0)
2014-
return cxoError_raiseAndReturnInt();
2015-
}
2016-
2017-
// handle bind by name
2018-
} else {
2019-
pos = 0;
2020-
while (PyDict_Next(cursor->bindVariables, &pos, &key, &value)) {
2021-
var = (cxoVar*) value;
2022-
if (dpiVar_getData(var->handle, &var->allocatedElements,
2023-
&var->data) < 0)
2024-
return cxoError_raiseAndReturnInt();
2025-
}
2026-
}
2027-
2028-
return 0;
2029-
}
2030-
2031-
2032-
2033-
20341986
//-----------------------------------------------------------------------------
20351987
// cxoCursor_getBatchErrors()
20361988
// Returns a list of batch error objects.

src/cxoFuture.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ static PyObject *cxoFuture_getAttr(cxoFuture *obj, PyObject *nameObject)
6969
return NULL;
7070
if (strncmp(buffer.ptr, "ctx_mgr_close", buffer.size) == 0)
7171
result = PyBool_FromLong(obj->contextManagerClose);
72+
else if (strncmp(buffer.ptr, "dml_ret_array_val", buffer.size) == 0)
73+
result = PyBool_FromLong(obj->dmlReturningArray);
7274
else {
7375
Py_INCREF(Py_None);
7476
result = Py_None;
@@ -92,6 +94,8 @@ static int cxoFuture_setAttr(cxoFuture *obj, PyObject *nameObject,
9294
return -1;
9395
if (strncmp(buffer.ptr, "ctx_mgr_close", buffer.size) == 0)
9496
result = cxoUtils_getBooleanValue(value, 0, &obj->contextManagerClose);
97+
else if (strncmp(buffer.ptr, "dml_ret_array_val", buffer.size) == 0)
98+
result = cxoUtils_getBooleanValue(value, 0, &obj->dmlReturningArray);
9599
cxoBuffer_clear(&buffer);
96100
return result;
97101
}

src/cxoModule.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,6 @@ static PyObject *cxoModule_initialize(void)
392392
cxoFutureObj = (cxoFuture*) cxoPyTypeFuture.tp_alloc(&cxoPyTypeFuture, 0);
393393
if (!cxoFutureObj)
394394
return NULL;
395-
cxoFutureObj->contextManagerClose = 0;
396395
if (PyModule_AddObject(module, "__future__", (PyObject*) cxoFutureObj) < 0)
397396
return NULL;
398397

src/cxoModule.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ struct cxoEnqOptions {
261261
struct cxoFuture {
262262
PyObject_HEAD
263263
int contextManagerClose;
264+
int dmlReturningArray;
264265
};
265266

266267
struct cxoLob {
@@ -377,6 +378,8 @@ struct cxoVar {
377378
uint32_t size;
378379
uint32_t bufferSize;
379380
int isArray;
381+
int isValueSet;
382+
int getReturnedData;
380383
cxoVarType *type;
381384
};
382385

@@ -457,7 +460,7 @@ cxoVarType *cxoVarType_fromPythonValue(PyObject *value, int *isArray,
457460

458461
int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos);
459462
int cxoVar_check(PyObject *object);
460-
PyObject *cxoVar_getSingleValue(cxoVar *var, uint32_t arrayPos);
463+
PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos);
461464
PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos);
462465
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
463466
Py_ssize_t size, int isArray, cxoObjectType *objType);

src/cxoVar.c

Lines changed: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -379,28 +379,81 @@ int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos)
379379
if (status < 0)
380380
return cxoError_raiseAndReturnInt();
381381

382+
// set flag if bound to a DML returning statement and no data set
383+
if (cursor->stmtInfo.isReturning && !var->isValueSet)
384+
var->getReturnedData = 1;
385+
382386
return 0;
383387
}
384388

385389

390+
//-----------------------------------------------------------------------------
391+
// cxoVar_getArrayValue()
392+
// Return the value of the variable as an array.
393+
//-----------------------------------------------------------------------------
394+
static PyObject *cxoVar_getArrayValue(cxoVar *var, uint32_t numElements,
395+
dpiData *data)
396+
{
397+
PyObject *value, *singleValue;
398+
uint32_t i;
399+
400+
// use the first set of returned values if DML returning as array is not
401+
// enabled
402+
if (!(cxoFutureObj && cxoFutureObj->dmlReturningArray) &&
403+
var->getReturnedData && !data) {
404+
if (dpiVar_getReturnedData(var->handle, 0, &numElements, &data) < 0)
405+
return cxoError_raiseAndReturnNull();
406+
}
407+
408+
value = PyList_New(numElements);
409+
if (!value)
410+
return NULL;
411+
412+
for (i = 0; i < numElements; i++) {
413+
singleValue = cxoVar_getSingleValue(var, data, i);
414+
if (!singleValue) {
415+
Py_DECREF(value);
416+
return NULL;
417+
}
418+
PyList_SET_ITEM(value, i, singleValue);
419+
}
420+
421+
return value;
422+
}
423+
424+
386425
//-----------------------------------------------------------------------------
387426
// cxoVar_getSingleValue()
388427
// Return the value of the variable at the given position.
389428
//-----------------------------------------------------------------------------
390-
PyObject *cxoVar_getSingleValue(cxoVar *var, uint32_t arrayPos)
429+
PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos)
391430
{
392431
PyObject *value, *result;
393-
dpiData *data;
394-
395-
// ensure we do not exceed the number of allocated elements
396-
if (arrayPos >= var->allocatedElements) {
397-
PyErr_SetString(PyExc_IndexError,
398-
"cxoVar_getSingleValue: array size exceeded");
399-
return NULL;
432+
uint32_t numReturnedRows;
433+
dpiData *returnedData;
434+
435+
// handle DML returning
436+
if (!data && var->getReturnedData) {
437+
if (cxoFutureObj && cxoFutureObj->dmlReturningArray) {
438+
if (dpiVar_getReturnedData(var->handle, arrayPos, &numReturnedRows,
439+
&returnedData) < 0)
440+
return cxoError_raiseAndReturnNull();
441+
return cxoVar_getArrayValue(var, numReturnedRows, returnedData);
442+
}
443+
if (dpiVar_getReturnedData(var->handle, 0, &numReturnedRows,
444+
&data) < 0)
445+
return cxoError_raiseAndReturnNull();
446+
if (arrayPos >= numReturnedRows) {
447+
PyErr_SetString(PyExc_IndexError,
448+
"cxoVar_getSingleValue: array size exceeded");
449+
return NULL;
450+
}
400451
}
401452

402-
// return the value
403-
data = &var->data[arrayPos];
453+
// in all other cases, just get the value stored at specified position
454+
if (data)
455+
data = &data[arrayPos];
456+
else data = &var->data[arrayPos];
404457
if (data->isNull)
405458
Py_RETURN_NONE;
406459
value = cxoTransform_toPython(var->type->transformNum, var->connection,
@@ -431,33 +484,6 @@ PyObject *cxoVar_getSingleValue(cxoVar *var, uint32_t arrayPos)
431484
}
432485

433486

434-
//-----------------------------------------------------------------------------
435-
// cxoVar_getArrayValue()
436-
// Return the value of the variable as an array.
437-
//-----------------------------------------------------------------------------
438-
static PyObject *cxoVar_getArrayValue(cxoVar *var,
439-
uint32_t numElements)
440-
{
441-
PyObject *value, *singleValue;
442-
uint32_t i;
443-
444-
value = PyList_New(numElements);
445-
if (!value)
446-
return NULL;
447-
448-
for (i = 0; i < numElements; i++) {
449-
singleValue = cxoVar_getSingleValue(var, i);
450-
if (!singleValue) {
451-
Py_DECREF(value);
452-
return NULL;
453-
}
454-
PyList_SET_ITEM(value, i, singleValue);
455-
}
456-
457-
return value;
458-
}
459-
460-
461487
//-----------------------------------------------------------------------------
462488
// cxoVar_getValue()
463489
// Return the value of the variable.
@@ -469,10 +495,14 @@ PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos)
469495
if (var->isArray) {
470496
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
471497
return cxoError_raiseAndReturnNull();
472-
return cxoVar_getArrayValue(var, numElements);
498+
return cxoVar_getArrayValue(var, numElements, var->data);
473499
}
474-
475-
return cxoVar_getSingleValue(var, arrayPos);
500+
if (arrayPos >= var->allocatedElements) {
501+
PyErr_SetString(PyExc_IndexError,
502+
"cxoVar_getSingleValue: array size exceeded");
503+
return NULL;
504+
}
505+
return cxoVar_getSingleValue(var, NULL, arrayPos);
476506
}
477507

478508

@@ -659,6 +689,7 @@ static int cxoVar_setArrayValue(cxoVar *var, PyObject *value)
659689
//-----------------------------------------------------------------------------
660690
int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value)
661691
{
692+
var->isValueSet = 1;
662693
if (var->isArray) {
663694
if (arrayPos > 0) {
664695
PyErr_SetString(cxoNotSupportedErrorException,
@@ -737,9 +768,10 @@ static PyObject *cxoVar_externalGetValue(cxoVar *var, PyObject *args,
737768
static PyObject *cxoVar_externalGetActualElements(cxoVar *var,
738769
void *unused)
739770
{
740-
uint32_t numElements;
771+
uint32_t numElements = var->allocatedElements;
741772

742-
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
773+
if (var->isArray &&
774+
dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
743775
return cxoError_raiseAndReturnNull();
744776
return PyInt_FromLong(numElements);
745777
}
@@ -751,11 +783,12 @@ static PyObject *cxoVar_externalGetActualElements(cxoVar *var,
751783
//-----------------------------------------------------------------------------
752784
static PyObject *cxoVar_externalGetValues(cxoVar *var, void *unused)
753785
{
754-
uint32_t numElements;
786+
uint32_t numElements = var->allocatedElements;
755787

756-
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
788+
if (var->isArray &&
789+
dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
757790
return cxoError_raiseAndReturnNull();
758-
return cxoVar_getArrayValue(var, numElements);
791+
return cxoVar_getArrayValue(var, numElements, NULL);
759792
}
760793

761794

@@ -771,10 +804,10 @@ static PyObject *cxoVar_repr(cxoVar *var)
771804
if (var->isArray) {
772805
if (dpiVar_getNumElementsInArray(var->handle, &numElements) < 0)
773806
return cxoError_raiseAndReturnNull();
774-
value = cxoVar_getArrayValue(var, numElements);
807+
value = cxoVar_getArrayValue(var, numElements, var->data);
775808
} else if (var->allocatedElements == 1)
776-
value = cxoVar_getSingleValue(var, 0);
777-
else value = cxoVar_getArrayValue(var, var->allocatedElements);
809+
value = cxoVar_getSingleValue(var, NULL, 0);
810+
else value = cxoVar_getArrayValue(var, var->allocatedElements, NULL);
778811
if (!value)
779812
return NULL;
780813
if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) {

0 commit comments

Comments
 (0)