python-cx_Oracle/Environment.c

399 lines
14 KiB
C

//-----------------------------------------------------------------------------
// Environment.c
// Environment handling.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// structure for the Python type
//-----------------------------------------------------------------------------
typedef struct {
PyObject_HEAD
OCIEnv *handle;
OCIError *errorHandle;
int maxBytesPerCharacter;
int fixedWidth;
char *encoding;
char *nencoding;
PyObject *cloneEnv;
udt_Buffer numberToStringFormatBuffer;
udt_Buffer numberFromStringFormatBuffer;
udt_Buffer nlsNumericCharactersBuffer;
} udt_Environment;
//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------
static void Environment_Free(udt_Environment*);
static int Environment_CheckForError(udt_Environment*, sword, const char*);
//-----------------------------------------------------------------------------
// declaration of Python type
//-----------------------------------------------------------------------------
static PyTypeObject g_EnvironmentType = {
PyVarObject_HEAD_INIT(NULL, 0)
"OracleEnvironment", // tp_name
sizeof(udt_Environment), // tp_basicsize
0, // tp_itemsize
(destructor) Environment_Free, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_compare
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
0 // tp_doc
};
#include "Error.c"
//-----------------------------------------------------------------------------
// Environment_New()
// Create a new environment object.
//-----------------------------------------------------------------------------
static udt_Environment *Environment_New(
OCIEnv *handle) // handle to use
{
udt_Environment *env;
sword status;
// create a new object for the Oracle environment
env = (udt_Environment*) g_EnvironmentType.tp_alloc(&g_EnvironmentType, 0);
if (!env)
return NULL;
env->handle = NULL;
env->errorHandle = NULL;
env->fixedWidth = 1;
env->maxBytesPerCharacter = 1;
env->cloneEnv = NULL;
cxBuffer_Init(&env->numberToStringFormatBuffer);
cxBuffer_Init(&env->numberFromStringFormatBuffer);
cxBuffer_Init(&env->nlsNumericCharactersBuffer);
// create the error handle
status = OCIHandleAlloc(handle, (dvoid**) &env->errorHandle,
OCI_HTYPE_ERROR, 0, 0);
if (Environment_CheckForError(env, status,
"Environment_New(): create error handle") < 0) {
Py_DECREF(env);
return NULL;
}
env->handle = handle;
return env;
}
//-----------------------------------------------------------------------------
// Environment_GetCharacterSetName()
// Retrieve and store the IANA character set name for the attribute.
//-----------------------------------------------------------------------------
static int Environment_GetCharacterSetName(
udt_Environment *self, // environment object
ub2 attribute, // attribute to fetch
const char *overrideValue, // override value, if specified
char **result) // place to store result
{
char charsetName[OCI_NLS_MAXBUFSZ], ianaCharsetName[OCI_NLS_MAXBUFSZ];
ub2 charsetId;
sword status;
// if override value specified, use it
if (overrideValue) {
*result = PyMem_Malloc(strlen(overrideValue) + 1);
if (!*result)
return -1;
strcpy(*result, overrideValue);
return 0;
}
// get character set id
status = OCIAttrGet(self->handle, OCI_HTYPE_ENV, &charsetId, NULL,
attribute, self->errorHandle);
if (Environment_CheckForError(self, status,
"Environment_GetCharacterSetName(): get charset id") < 0)
return -1;
// get character set name
status = OCINlsCharSetIdToName(self->handle, (text*) charsetName,
OCI_NLS_MAXBUFSZ, charsetId);
if (Environment_CheckForError(self, status,
"Environment_GetCharacterSetName(): get Oracle charset name") < 0)
return -1;
// get IANA character set name
status = OCINlsNameMap(self->handle, (oratext*) ianaCharsetName,
OCI_NLS_MAXBUFSZ, (oratext*) charsetName, OCI_NLS_CS_ORA_TO_IANA);
if (Environment_CheckForError(self, status,
"Environment_GetCharacterSetName(): translate NLS charset") < 0)
return -1;
// store results
*result = PyMem_Malloc(strlen(ianaCharsetName) + 1);
if (!*result)
return -1;
strcpy(*result, ianaCharsetName);
return 0;
}
//-----------------------------------------------------------------------------
// Environment_SetBuffer()
// Set the buffer in the environment from the specified string.
//-----------------------------------------------------------------------------
static int Environment_SetBuffer(
udt_Buffer *buf, // buffer to set
const char *value, // ASCII value to use
const char *encoding) // encoding to use
{
PyObject *obj;
obj = cxString_FromAscii(value);
if (!obj)
return -1;
if (cxBuffer_FromObject(buf, obj, encoding)<0)
return -1;
Py_CLEAR(obj);
return 0;
}
//-----------------------------------------------------------------------------
// Environment_NewFromScratch()
// Create a new environment object from scratch.
//-----------------------------------------------------------------------------
static udt_Environment *Environment_NewFromScratch(
int threaded, // use threaded mode?
int events, // use events mode?
char *encoding, // override value for encoding
char *nencoding) // override value for nencoding
{
udt_Environment *env;
OCIEnv *handle;
sword status;
ub4 mode;
// turn threading mode on, if desired
mode = OCI_OBJECT;
if (threaded)
mode |= OCI_THREADED;
#ifdef OCI_EVENTS
if (events)
mode |= OCI_EVENTS;
#endif
// create the new environment handle
status = OCIEnvNlsCreate(&handle, mode, NULL, NULL, NULL, NULL, 0, NULL, 0,
0);
if (!handle ||
(status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO)) {
PyErr_SetString(g_InterfaceErrorException,
"Unable to acquire Oracle environment handle");
return NULL;
}
// create the environment object
env = Environment_New(handle);
if (!env) {
OCIHandleFree(handle, OCI_HTYPE_ENV);
return NULL;
}
// acquire max bytes per character
status = OCINlsNumericInfoGet(env->handle, env->errorHandle,
&env->maxBytesPerCharacter, OCI_NLS_CHARSET_MAXBYTESZ);
if (Environment_CheckForError(env, status,
"Environment_New(): get max bytes per character") < 0) {
Py_DECREF(env);
return NULL;
}
// acquire whether character set is fixed width
status = OCINlsNumericInfoGet(env->handle, env->errorHandle,
&env->fixedWidth, OCI_NLS_CHARSET_FIXEDWIDTH);
if (Environment_CheckForError(env, status,
"Environment_New(): determine if charset fixed width") < 0) {
Py_DECREF(env);
return NULL;
}
// determine encodings to use for Unicode values
if (Environment_GetCharacterSetName(env, OCI_ATTR_ENV_CHARSET_ID,
encoding, &env->encoding) < 0)
return NULL;
if (Environment_GetCharacterSetName(env, OCI_ATTR_ENV_NCHARSET_ID,
nencoding, &env->nencoding) < 0)
return NULL;
// fill buffers for number formats
if (Environment_SetBuffer(&env->numberToStringFormatBuffer, "TM9",
env->encoding) < 0)
return NULL;
if (Environment_SetBuffer(&env->numberFromStringFormatBuffer,
"999999999999999999999999999999999999999999999999999999999999999",
env->encoding) < 0)
return NULL;
if (Environment_SetBuffer(&env->nlsNumericCharactersBuffer,
"NLS_NUMERIC_CHARACTERS='.,'", env->encoding) < 0)
return NULL;
return env;
}
//-----------------------------------------------------------------------------
// Environment_Clone()
// Clone an existing environment which is used when acquiring a connection
// from a session pool, for example.
//-----------------------------------------------------------------------------
static udt_Environment *Environment_Clone(
udt_Environment *cloneEnv) // environment to clone
{
udt_Environment *env;
env = Environment_New(cloneEnv->handle);
if (!env)
return NULL;
env->maxBytesPerCharacter = cloneEnv->maxBytesPerCharacter;
env->fixedWidth = cloneEnv->fixedWidth;
Py_INCREF(cloneEnv);
env->cloneEnv = (PyObject*) cloneEnv;
env->encoding = cloneEnv->encoding;
env->nencoding = cloneEnv->nencoding;
cxBuffer_Copy(&env->numberToStringFormatBuffer,
&cloneEnv->numberToStringFormatBuffer);
cxBuffer_Copy(&env->numberFromStringFormatBuffer,
&cloneEnv->numberFromStringFormatBuffer);
cxBuffer_Copy(&env->nlsNumericCharactersBuffer,
&cloneEnv->nlsNumericCharactersBuffer);
return env;
}
//-----------------------------------------------------------------------------
// Environment_Free()
// Deallocate the environment. Note that destroying the environment handle
// will automatically destroy any child handles that were created.
//-----------------------------------------------------------------------------
static void Environment_Free(
udt_Environment *self) // environment object
{
if (self->errorHandle)
OCIHandleFree(self->errorHandle, OCI_HTYPE_ERROR);
if (self->handle && !self->cloneEnv)
OCIHandleFree(self->handle, OCI_HTYPE_ENV);
if (!self->cloneEnv) {
if (self->encoding)
PyMem_Free(self->encoding);
if (self->nencoding)
PyMem_Free(self->nencoding);
}
cxBuffer_Clear(&self->numberToStringFormatBuffer);
cxBuffer_Clear(&self->numberFromStringFormatBuffer);
cxBuffer_Clear(&self->nlsNumericCharactersBuffer);
Py_CLEAR(self->cloneEnv);
Py_TYPE(self)->tp_free((PyObject*) self);
}
//-----------------------------------------------------------------------------
// Environment_RaiseError()
// Reads the error that was caused by the last Oracle statement and raise an
// exception for Python. At this point it is assumed that the Oracle
// environment is fully initialized.
//-----------------------------------------------------------------------------
static int Environment_RaiseError(
udt_Environment *environment, // environment to raise error for
const char *context) // context in which error occurred
{
PyObject *exceptionType;
udt_Error *error;
error = Error_New(environment, context, 1);
if (error) {
switch (error->code) {
case 1:
case 1400:
case 2290:
case 2291:
case 2292:
exceptionType = g_IntegrityErrorException;
break;
case 22:
case 378:
case 602:
case 603:
case 604:
case 609:
case 1012:
case 1013:
case 1033:
case 1034:
case 1041:
case 1043:
case 1089:
case 1090:
case 1092:
case 3113:
case 3114:
case 3122:
case 3135:
case 12153:
case 12203:
case 12500:
case 12571:
case 27146:
case 28511:
exceptionType = g_OperationalErrorException;
break;
default:
exceptionType = g_DatabaseErrorException;
break;
}
PyErr_SetObject(exceptionType, (PyObject*) error);
Py_DECREF(error);
}
return -1;
}
//-----------------------------------------------------------------------------
// Environment_CheckForError()
// Check for an error in the last call and if an error has occurred, raise a
// Python exception.
//-----------------------------------------------------------------------------
static int Environment_CheckForError(
udt_Environment *environment, // environment to raise error in
sword status, // status of last call
const char *context) // context
{
udt_Error *error;
if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) {
if (status != OCI_INVALID_HANDLE)
return Environment_RaiseError(environment, context);
error = Error_New(environment, context, 0);
if (!error)
return -1;
error->code = 0;
error->message = cxString_FromAscii("Invalid handle!");
if (!error->message)
Py_DECREF(error);
else PyErr_SetObject(g_DatabaseErrorException, (PyObject*) error);
return -1;
}
return 0;
}