399 lines
14 KiB
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;
|
|
}
|
|
|