Skip to content

Commit

Permalink
Use a cx_Oracle._Error object (not a string) for all cx_Oracle except…
Browse files Browse the repository at this point in the history
…ion types,

as suggested (#51).
  • Loading branch information
anthony-tuininga committed Mar 31, 2018
1 parent 4f5c04f commit 623718f
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 92 deletions.
2 changes: 1 addition & 1 deletion src/cxoConnection.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ static int cxoConnectionParams_finalize(cxoConnectionParams *params)
int cxoConnection_isConnected(cxoConnection *conn)
{
if (!conn->handle) {
PyErr_SetString(cxoInterfaceErrorException, "not connected");
cxoError_raiseFromString(cxoInterfaceErrorException, "not connected");
return -1;
}
return 0;
Expand Down
58 changes: 22 additions & 36 deletions src/cxoCursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ static void cxoCursor_free(cxoCursor *cursor)
static int cxoCursor_isOpen(cxoCursor *cursor)
{
if (!cursor->isOpen) {
PyErr_SetString(cxoInterfaceErrorException, "not open");
cxoError_raiseFromString(cxoInterfaceErrorException, "not open");
return -1;
}
return cxoConnection_isConnected(cursor->connection);
Expand Down Expand Up @@ -297,7 +297,7 @@ static int cxoCursor_verifyFetch(cxoCursor *cursor)

// make sure the cursor is for a query
if (!cursor->fetchVariables) {
PyErr_SetString(cxoInterfaceErrorException, "not a query");
cxoError_raiseFromString(cxoInterfaceErrorException, "not a query");
return -1;
}

Expand Down Expand Up @@ -737,7 +737,7 @@ int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters,
if (cursor->bindVariables) {
origBoundByPos = PyList_Check(cursor->bindVariables);
if (boundByPos != origBoundByPos) {
PyErr_SetString(cxoProgrammingErrorException,
cxoError_raiseFromString(cxoProgrammingErrorException,
"positional and named binds cannot be intermixed");
return -1;
}
Expand Down Expand Up @@ -908,7 +908,7 @@ static int cxoCursor_internalPrepare(cxoCursor *cursor, PyObject *statement,

// make sure we don't get a situation where nothing is to be executed
if (statement == Py_None && !cursor->statement) {
PyErr_SetString(cxoProgrammingErrorException,
cxoError_raiseFromString(cxoProgrammingErrorException,
"no statement specified and no prior statement prepared");
return -1;
}
Expand Down Expand Up @@ -1078,7 +1078,8 @@ static int cxoCursor_callCalculateSize(PyObject *name,
// error if the number of arguments exceeds this value; more than this
// number would probably be unusable in any case!
if (numPositionalArgs + numKeywordArgs > 10000) {
PyErr_SetString(cxoInterfaceErrorException, "too many arguments");
cxoError_raiseFromString(cxoInterfaceErrorException,
"too many arguments");
return -1;
}

Expand Down Expand Up @@ -1346,11 +1347,8 @@ static PyObject *cxoCursor_execute(cxoCursor *cursor, PyObject *args,
if (executeArgs && keywordArgs) {
if (PyDict_Size(keywordArgs) == 0)
keywordArgs = NULL;
else {
PyErr_SetString(cxoInterfaceErrorException,
"expecting argument or keyword arguments, not both");
return NULL;
}
else return cxoError_raiseFromString(cxoInterfaceErrorException,
"expecting argument or keyword arguments, not both");
}
if (keywordArgs)
executeArgs = keywordArgs;
Expand Down Expand Up @@ -1446,11 +1444,9 @@ static PyObject *cxoCursor_executeMany(cxoCursor *cursor, PyObject *args,
numRows = (uint32_t) PyList_GET_SIZE(listOfArguments);
for (i = 0; i < numRows; i++) {
arguments = PyList_GET_ITEM(listOfArguments, i);
if (!PyDict_Check(arguments) && !PySequence_Check(arguments)) {
PyErr_SetString(cxoInterfaceErrorException,
if (!PyDict_Check(arguments) && !PySequence_Check(arguments))
return cxoError_raiseFromString(cxoInterfaceErrorException,
"expecting a list of dictionaries or sequences");
return NULL;
}
if (cxoCursor_setBindVariables(cursor, arguments, numRows, i,
(i < numRows - 1)) < 0)
return NULL;
Expand Down Expand Up @@ -1624,11 +1620,9 @@ static PyObject *cxoCursor_fetchRaw(cxoCursor *cursor, PyObject *args,
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|i", keywordList,
&numRowsToFetch))
return NULL;
if (numRowsToFetch > cursor->fetchArraySize) {
PyErr_SetString(cxoInterfaceErrorException,
if (numRowsToFetch > cursor->fetchArraySize)
return cxoError_raiseFromString(cxoInterfaceErrorException,
"rows to fetch exceeds array size");
return NULL;
}

// perform the fetch
if (dpiStmt_fetchRows(cursor->handle, numRowsToFetch, &bufferRowIndex,
Expand Down Expand Up @@ -1671,11 +1665,8 @@ static PyObject *cxoCursor_scroll(cxoCursor *cursor, PyObject *args,
mode = DPI_MODE_FETCH_FIRST;
else if (strcmp(strMode, "last") == 0)
mode = DPI_MODE_FETCH_LAST;
else {
PyErr_SetString(cxoInterfaceErrorException,
"mode must be one of relative, absolute, first or last");
return NULL;
}
else return cxoError_raiseFromString(cxoInterfaceErrorException,
"mode must be one of relative, absolute, first or last");

// make sure the cursor is open
if (cxoCursor_isOpen(cursor) < 0)
Expand Down Expand Up @@ -1713,11 +1704,9 @@ static PyObject *cxoCursor_setInputSizes(cxoCursor *cursor, PyObject *args,

// only expect keyword arguments or positional arguments, not both
numPositionalArgs = PyTuple_Size(args);
if (keywordArgs && numPositionalArgs > 0) {
PyErr_SetString(cxoInterfaceErrorException,
if (keywordArgs && numPositionalArgs > 0)
return cxoError_raiseFromString(cxoInterfaceErrorException,
"expecting arguments or keyword arguments, not both");
return NULL;
}

// make sure the cursor is open
if (cxoCursor_isOpen(cursor) < 0)
Expand Down Expand Up @@ -1898,11 +1887,9 @@ static PyObject *cxoCursor_bindNames(cxoCursor *cursor, PyObject *args)
return NULL;

// ensure that a statement has already been prepared
if (!cursor->statement) {
PyErr_SetString(cxoProgrammingErrorException,
if (!cursor->statement)
return cxoError_raiseFromString(cxoProgrammingErrorException,
"statement must be prepared first");
return NULL;
}

// determine the number of binds
if (dpiStmt_getBindCount(cursor->handle, &numBinds) < 0)
Expand Down Expand Up @@ -2015,7 +2002,7 @@ static PyObject* cxoCursor_getBatchErrors(cxoCursor *cursor)
result = PyList_New(numErrors);
if (result) {
for (i = 0; i < numErrors; i++) {
error = cxoError_internalNew(&errors[i]);
error = cxoError_newFromInfo(&errors[i]);
if (!error) {
Py_CLEAR(result);
break;
Expand Down Expand Up @@ -2075,10 +2062,9 @@ static PyObject *cxoCursor_getImplicitResults(cxoCursor *cursor)
return NULL;

// make sure we have a statement executed (handle defined)
if (!cursor->handle) {
PyErr_SetString(cxoInterfaceErrorException, "no statement executed");
return NULL;
}
if (!cursor->handle)
return cxoError_raiseFromString(cxoInterfaceErrorException,
"no statement executed");

// create result
result = PyList_New(0);
Expand Down
93 changes: 68 additions & 25 deletions src/cxoError.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,44 @@ static void cxoError_free(cxoError *error)


//-----------------------------------------------------------------------------
// cxoError_internalNew()
// cxoError_new()
// Create a new error object. This is intended to only be used by the
// unpickling routine, and not by direct creation!
//-----------------------------------------------------------------------------
static PyObject *cxoError_new(PyTypeObject *type, PyObject *args,
PyObject *keywordArgs)
{
PyObject *message, *context;
int isRecoverable, code;
cxoError *error;
unsigned offset;

isRecoverable = 0;
if (!PyArg_ParseTuple(args, "OiIO|i", &message, &code, &offset, &context,
&isRecoverable))
return NULL;
error = (cxoError*) type->tp_alloc(type, 0);
if (!error)
return NULL;

error->code = code;
error->offset = offset;
error->isRecoverable = (char) isRecoverable;
Py_INCREF(message);
error->message = message;
Py_INCREF(context);
error->context = context;

return (PyObject*) error;
}


//-----------------------------------------------------------------------------
// cxoError_newFromInfo()
// Internal method for creating an error object from the DPI error
// information.
//-----------------------------------------------------------------------------
cxoError *cxoError_internalNew(dpiErrorInfo *errorInfo)
cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo)
{
cxoError *error;

Expand Down Expand Up @@ -149,35 +182,26 @@ cxoError *cxoError_internalNew(dpiErrorInfo *errorInfo)


//-----------------------------------------------------------------------------
// cxoError_new()
// Create a new error object. This is intended to only be used by the
// unpickling routine, and not by direct creation!
// cxoError_newFromString()
// Internal method for creating an error object from the DPI error
// information.
//-----------------------------------------------------------------------------
static PyObject *cxoError_new(PyTypeObject *type, PyObject *args,
PyObject *keywordArgs)
static cxoError *cxoError_newFromString(const char *message)
{
PyObject *message, *context;
int isRecoverable, code;
cxoError *error;
unsigned offset;

isRecoverable = 0;
if (!PyArg_ParseTuple(args, "OiIO|i", &message, &code, &offset, &context,
&isRecoverable))
return NULL;
error = (cxoError*) type->tp_alloc(type, 0);
error = (cxoError*) cxoPyTypeError.tp_alloc(&cxoPyTypeError, 0);
if (!error)
return NULL;
Py_INCREF(Py_None);
error->context = Py_None;
error->message = cxoPyString_fromAscii(message);
if (!error->message) {
Py_DECREF(error);
return NULL;
}

error->code = code;
error->offset = offset;
error->isRecoverable = (char) isRecoverable;
Py_INCREF(message);
error->message = message;
Py_INCREF(context);
error->context = context;

return (PyObject*) error;
return error;
}


Expand Down Expand Up @@ -217,7 +241,7 @@ int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo)
PyObject *exceptionType;
cxoError *error;

error = cxoError_internalNew(errorInfo);
error = cxoError_newFromInfo(errorInfo);
if (!error)
return -1;
switch (errorInfo->code) {
Expand Down Expand Up @@ -265,6 +289,25 @@ int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo)
}


//-----------------------------------------------------------------------------
// cxoError_raiseFromString()
// Internal method for raising an exception given an error information
// structure from DPI. Return -1 as a convenience to the caller.
//-----------------------------------------------------------------------------
PyObject *cxoError_raiseFromString(PyObject *exceptionType,
const char *message)
{
cxoError *error;

error = cxoError_newFromString(message);
if (!error)
return NULL;
PyErr_SetObject(exceptionType, (PyObject*) error);
Py_DECREF(error);
return NULL;
}


//-----------------------------------------------------------------------------
// cxoError_reduce()
// Method provided for pickling/unpickling of Error objects.
Expand Down
6 changes: 2 additions & 4 deletions src/cxoModule.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,8 @@ static PyObject* cxoModule_clientVersion(PyObject* self, PyObject* args)
//-----------------------------------------------------------------------------
static PyObject* cxoModule_time(PyObject* self, PyObject* args)
{
PyErr_SetString(cxoNotSupportedErrorException,
return cxoError_raiseFromString(cxoNotSupportedErrorException,
"Oracle does not support time only variables");
return NULL;
}


Expand All @@ -167,9 +166,8 @@ static PyObject* cxoModule_time(PyObject* self, PyObject* args)
//-----------------------------------------------------------------------------
static PyObject* cxoModule_timeFromTicks(PyObject* self, PyObject* args)
{
PyErr_SetString(cxoNotSupportedErrorException,
return cxoError_raiseFromString(cxoNotSupportedErrorException,
"Oracle does not support time only variables");
return NULL;
}


Expand Down
4 changes: 3 additions & 1 deletion src/cxoModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,12 @@ cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection);

cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection);

cxoError *cxoError_internalNew(dpiErrorInfo *errorInfo);
cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo);
int cxoError_raiseAndReturnInt(void);
PyObject *cxoError_raiseAndReturnNull(void);
int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo);
PyObject *cxoError_raiseFromString(PyObject *exceptionType,
const char *message);

PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
dpiLob *handle);
Expand Down
8 changes: 4 additions & 4 deletions src/cxoObject.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ static PyObject *cxoObject_getAttributeValue(cxoObject *obj,
if (attribute->transformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message), "Oracle type %d not supported.",
attribute->oracleTypeNum);
PyErr_SetString(cxoNotSupportedErrorException, message);
return NULL;
return cxoError_raiseFromString(cxoNotSupportedErrorException,
message);
}
cxoTransform_getTypeInfo(attribute->transformNum, &oracleTypeNum,
&nativeTypeNum);
Expand Down Expand Up @@ -367,8 +367,8 @@ static PyObject *cxoObject_internalGetElementByIndex(cxoObject *obj,
if (obj->objectType->elementTransformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message), "Oracle type %d not supported.",
obj->objectType->elementOracleTypeNum);
PyErr_SetString(cxoNotSupportedErrorException, message);
return NULL;
return cxoError_raiseFromString(cxoNotSupportedErrorException,
message);
}
cxoTransform_getTypeInfo(obj->objectType->elementTransformNum,
&oracleTypeNum, &nativeTypeNum);
Expand Down
10 changes: 4 additions & 6 deletions src/cxoSessionPool.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,12 @@ static int cxoSessionPool_init(cxoSessionPool *pool, PyObject *args,
&dpiCommonParams.nencoding, &editionObj))
return -1;
if (!PyType_Check(connectionType)) {
PyErr_SetString(cxoProgrammingErrorException,
cxoError_raiseFromString(cxoProgrammingErrorException,
"connectiontype must be a type");
return -1;
}
if (!PyType_IsSubtype(connectionType, &cxoPyTypeConnection)) {
PyErr_SetString(cxoProgrammingErrorException,
cxoError_raiseFromString(cxoProgrammingErrorException,
"connectiontype must be a subclass of Connection");
return -1;
}
Expand Down Expand Up @@ -320,11 +320,9 @@ static PyObject *cxoSessionPool_acquire(cxoSessionPool *pool, PyObject *args,
&passwordLength, &cclassObj, &purityObj, &tagObj, &matchAnyTagObj,
&shardingKeyObj, &superShardingKeyObj))
return NULL;
if (pool->homogeneous && username) {
PyErr_SetString(cxoProgrammingErrorException,
if (pool->homogeneous && username)
return cxoError_raiseFromString(cxoProgrammingErrorException,
"pool is homogeneous. Proxy authentication is not possible.");
return NULL;
}

// create arguments
if (keywordArgs)
Expand Down
5 changes: 2 additions & 3 deletions src/cxoTransform.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ int cxoTransform_fromPython(cxoTransformNum transformNum, PyObject *pyValue,
break;
}

PyErr_SetString(cxoNotSupportedErrorException,
cxoError_raiseFromString(cxoNotSupportedErrorException,
"Python value cannot be converted to a database value");
return -1;
}
Expand Down Expand Up @@ -754,8 +754,7 @@ PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
break;
}

PyErr_SetString(cxoNotSupportedErrorException,
return cxoError_raiseFromString(cxoNotSupportedErrorException,
"Database value cannot be converted to a Python value");
return NULL;
}

Loading

0 comments on commit 623718f

Please sign in to comment.