Continued work on Unicode support; added new test cases for full unicode

support within Python 2.x; move away from character semantics which Oracle is
deprecating anyway to byte semantics which should hopefully eliminate the
problem with a backend character set of UTF-8.
This commit is contained in:
Anthony Tuininga 2008-10-14 04:51:43 +00:00
parent 45faba4a48
commit 2e26d0beb8
13 changed files with 327 additions and 136 deletions

View File

@ -56,7 +56,7 @@ static PyObject *Connection_GetVersion(udt_Connection*, void*);
static PyObject *Connection_GetMaxBytesPerCharacter(udt_Connection*, void*);
static PyObject *Connection_ContextManagerEnter(udt_Connection*, PyObject*);
static PyObject *Connection_ContextManagerExit(udt_Connection*, PyObject*);
#ifdef OCI_NLS_CHARSET_MAXBYTESZ
#ifndef WITH_UNICODE
static PyObject *Connection_GetEncoding(udt_Connection*, void*);
static PyObject *Connection_GetNationalEncoding(udt_Connection*, void*);
#endif
@ -119,7 +119,7 @@ static PyMemberDef g_ConnectionMembers[] = {
// declaration of calculated members for Python type "Connection"
//-----------------------------------------------------------------------------
static PyGetSetDef g_ConnectionCalcMembers[] = {
#ifdef OCI_NLS_CHARSET_MAXBYTESZ
#ifndef WITH_UNICODE
{ "encoding", (getter) Connection_GetEncoding, 0, 0, 0 },
{ "nencoding", (getter) Connection_GetNationalEncoding, 0, 0, 0 },
#endif
@ -364,6 +364,7 @@ static int Connection_SetOCIAttr(
PyObject *value, // value to set
ub4 *attribute) // OCI attribute type
{
udt_StringBuffer buffer;
sword status;
// make sure connection is connected
@ -371,13 +372,15 @@ static int Connection_SetOCIAttr(
return -1;
// set the value in the OCI
if (!PyString_Check(value)) {
if (!CXORA_STRING_CHECK(value)) {
PyErr_SetString(PyExc_TypeError, "value must be a string");
return -1;
}
if (StringBuffer_Fill(&buffer, value))
return -1;
status = OCIAttrSet(self->sessionHandle, OCI_HTYPE_SESSION,
PyString_AS_STRING(value), PyString_GET_SIZE(value),
*attribute, self->environment->errorHandle);
buffer.ptr, buffer.size, *attribute,
self->environment->errorHandle);
if (Environment_CheckForError(self->environment, status,
"Connection_SetOCIAttr()") < 0)
return -1;
@ -786,7 +789,7 @@ static PyObject *Connection_Repr(
}
#ifdef OCI_NLS_CHARSET_MAXBYTESZ
#ifndef WITH_UNICODE
//-----------------------------------------------------------------------------
// Connection_GetCharacterSetName()
// Retrieve the IANA character set name for the attribute.
@ -911,8 +914,8 @@ static PyObject *Connection_GetVersion(
udt_Connection *self, // connection object
void *arg) // optional argument (ignored)
{
PyObject *procName, *listOfArguments;
udt_Variable *versionVar, *compatVar;
PyObject *results, *temp;
udt_Cursor *cursor;
// if version has already been determined, no need to determine again
@ -943,29 +946,37 @@ static PyObject *Connection_GetVersion(
return NULL;
}
// create the parameters for the function call
temp = Py_BuildValue("(s,[OO])",
"begin dbms_utility.db_version(:ver, :compat); end;",
versionVar, compatVar);
Py_DECREF(versionVar);
Py_DECREF(compatVar);
if (!temp) {
// create the list of arguments
listOfArguments = PyList_New(2);
if (!listOfArguments) {
Py_DECREF(versionVar);
Py_DECREF(compatVar);
Py_DECREF(cursor);
return NULL;
}
PyList_SET_ITEM(listOfArguments, 0, (PyObject*) versionVar);
PyList_SET_ITEM(listOfArguments, 1, (PyObject*) compatVar);
// create the string variable
procName = CXORA_ASCII_TO_STRING("dbms_utility.db_version");
if (!procName) {
Py_DECREF(listOfArguments);
Py_DECREF(cursor);
return NULL;
}
// execute the cursor
results = Cursor_Execute(cursor, temp, NULL);
if (!results) {
Py_DECREF(temp);
// call stored procedure
if (Cursor_Call(cursor, NULL, procName, listOfArguments) < 0) {
Py_DECREF(procName);
Py_DECREF(listOfArguments);
Py_DECREF(cursor);
return NULL;
}
Py_DECREF(results);
Py_DECREF(procName);
// retrieve value
self->version = Variable_GetValue(versionVar, 0);
Py_DECREF(temp);
Py_DECREF(listOfArguments);
Py_DECREF(cursor);
Py_XINCREF(self->version);
return self->version;

View File

@ -204,29 +204,22 @@ static int Cursor_FreeHandle(
udt_Cursor *self, // cursor object
int raiseException) // raise an exception, if necesary?
{
ub4 tagLength;
udt_StringBuffer buffer;
sword status;
char *tag;
if (self->handle) {
if (self->isOwned) {
OCIHandleFree(self->handle, OCI_HTYPE_STMT);
} else {
if (self->statementTag) {
tag = PyString_AS_STRING(self->statementTag);
tagLength = PyString_GET_SIZE(self->statementTag);
} else {
tag = NULL;
tagLength = 0;
}
if (self->connection->handle != 0) {
status = OCIStmtRelease(self->handle,
self->environment->errorHandle, (text*) tag, tagLength,
OCI_DEFAULT);
if (raiseException && Environment_CheckForError(
self->environment, status, "Cursor_FreeHandle()") < 0)
return -1;
}
} else if (self->connection->handle != 0) {
if (!StringBuffer_Fill(&buffer, self->statementTag) < 0)
return (raiseException) ? -1 : 0;
status = OCIStmtRelease(self->handle,
self->environment->errorHandle, (text*) buffer.ptr,
buffer.size, OCI_DEFAULT);
StringBuffer_CLEAR(&buffer);
if (raiseException && Environment_CheckForError(
self->environment, status, "Cursor_FreeHandle()") < 0)
return -1;
}
}
return 0;
@ -415,8 +408,7 @@ static int Cursor_GetBindNames(
// process the bind information returned
for (i = 0; i < foundElements; i++) {
if (!duplicate[i]) {
temp = PyString_FromStringAndSize(bindNames[i],
bindNameLengths[i]);
temp = CXORA_BUFFER_TO_STRING(bindNames[i], bindNameLengths[i]);
if (!temp) {
Py_DECREF(*names);
PyMem_Free(buffer);
@ -649,14 +641,18 @@ static PyObject *Cursor_ItemDescriptionHelper(
type = (PyObject*) varType->pythonType;
if (type == (PyObject*) &g_StringVarType)
displaySize = internalSize;
#ifndef WITH_UNICODE
else if (type == (PyObject*) &g_UnicodeVarType)
displaySize = internalSize / 2;
#endif
else if (type == (PyObject*) &g_BinaryVarType)
displaySize = internalSize;
else if (type == (PyObject*) &g_FixedCharVarType)
displaySize = internalSize;
#ifndef WITH_UNICODE
else if (type == (PyObject*) &g_FixedUnicodeVarType)
displaySize = internalSize / 2;
#endif
else if (type == (PyObject*) &g_NumberVarType) {
if (precision) {
displaySize = precision + 1;
@ -676,7 +672,7 @@ static PyObject *Cursor_ItemDescriptionHelper(
return NULL;
// set each of the items in the tuple
PyTuple_SET_ITEM(tuple, 0, CXORA_TO_STRING_OBJ(name, nameLength));
PyTuple_SET_ITEM(tuple, 0, CXORA_BUFFER_TO_STRING(name, nameLength));
Py_INCREF(type);
PyTuple_SET_ITEM(tuple, 1, type);
PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(displaySize));
@ -1308,7 +1304,7 @@ static int Cursor_Call(
Py_DECREF(arguments);
// create the statement object
format = PyString_FromString(statement);
format = PyBytes_FromString(statement);
PyMem_Free(statement);
if (!format) {
Py_DECREF(bindVariables);
@ -1436,13 +1432,8 @@ static PyObject *Cursor_Execute(
executeArgs = NULL;
if (!PyArg_ParseTuple(args, "O|O", &statement, &executeArgs))
return NULL;
#ifdef WITH_UNICODE
if (statement != Py_None && !PyUnicode_Check(statement)) {
PyErr_SetString(PyExc_TypeError, "expecting None or unicode string");
#else
if (statement != Py_None && !PyString_Check(statement)) {
if (statement != Py_None && !CXORA_STRING_CHECK(statement)) {
PyErr_SetString(PyExc_TypeError, "expecting None or a string");
#endif
return NULL;
}
if (executeArgs && keywordArgs) {
@ -1520,8 +1511,8 @@ static PyObject *Cursor_ExecuteMany(
if (!PyArg_ParseTuple(args, "OO!", &statement, &PyList_Type,
&listOfArguments))
return NULL;
if (statement != Py_None && !PyString_Check(statement)) {
PyErr_SetString(PyExc_TypeError, "expecting None or a string");
if (statement != Py_None && !CXORA_STRING_CHECK(statement)) {
PyErr_SetString(PyExc_TypeError, "expecting None or string");
return NULL;
}

View File

@ -75,7 +75,11 @@ static udt_Environment *Environment_New(
return NULL;
environment->handle = NULL;
environment->errorHandle = NULL;
#ifdef WITH_UNICODE
environment->maxBytesPerCharacter = 2;
#else
environment->maxBytesPerCharacter = 1;
#endif
environment->fixedWidth = 1;
environment->maxStringBytes = MAX_STRING_CHARS;
@ -114,7 +118,7 @@ static udt_Environment *Environment_New(
}
// acquire max bytes per character
#ifdef OCI_NLS_CHARSET_MAXBYTESZ
#ifndef WITH_UNICODE
status = OCINlsNumericInfoGet(environment->handle,
environment->errorHandle, &environment->maxBytesPerCharacter,
OCI_NLS_CHARSET_MAXBYTESZ);

View File

@ -109,7 +109,7 @@ static udt_Error *Error_New(
if (errorText[len] == 0 && errorText[len + 1] == 0)
break;
}
error->message = CXORA_TO_STRING_OBJ(errorText, len);
error->message = CXORA_BUFFER_TO_STRING(errorText, len);
#else
error->message = PyString_FromString(errorText);
#endif
@ -146,10 +146,6 @@ static PyObject *Error_Str(
Py_INCREF(self->message);
return self->message;
}
#ifdef WITH_UNICODE
return PyUnicode_DecodeASCII("", 0, NULL);
#else
return PyString_FromString("");
#endif
return CXORA_ASCII_TO_STRING("");
}

View File

@ -453,10 +453,10 @@ static PyObject *NumberVar_GetValue(
udt_NumberVar *var, // variable to determine value for
unsigned pos) // array position
{
PyObject *result, *stringObj;
char stringValue[200];
long integerValue;
ub4 stringLength;
PyObject *result;
sword status;
if (var->type == &vt_Integer || var->type == &vt_Boolean) {
@ -473,18 +473,19 @@ static PyObject *NumberVar_GetValue(
if (var->type == &vt_NumberAsString || var->type == &vt_LongInteger) {
stringLength = sizeof(stringValue);
status = OCINumberToText(var->environment->errorHandle,
&var->data[pos], (unsigned char*) "TM9", 3, NULL, 0,
&stringLength, (unsigned char*) stringValue);
&var->data[pos], (text*) g_NumberToStringFormatBuffer.ptr,
g_NumberToStringFormatBuffer.size, NULL, 0, &stringLength,
(unsigned char*) stringValue);
if (Environment_CheckForError(var->environment, status,
"NumberVar_GetValue(): as string") < 0)
return NULL;
stringObj = CXORA_BUFFER_TO_STRING(stringValue, stringLength);
if (!stringObj)
return NULL;
if (var->type == &vt_NumberAsString)
return PyString_FromStringAndSize(stringValue, stringLength);
result = PyInt_FromString(stringValue, NULL, 10);
if (result || !PyErr_ExceptionMatches(PyExc_ValueError))
return result;
PyErr_Clear();
result = PyLong_FromString(stringValue, NULL, 10);
return stringObj;
result = PyNumber_Int(stringObj);
Py_DECREF(stringObj);
if (result || !PyErr_ExceptionMatches(PyExc_ValueError))
return result;
PyErr_Clear();

View File

@ -169,12 +169,13 @@ static int SessionPool_Init(
PyObject *args, // arguments
PyObject *keywordArgs) // keyword arguments
{
unsigned usernameLength, passwordLength, dsnLength, poolNameLength;
unsigned minSessions, maxSessions, sessionIncrement;
PyObject *threadedObj, *eventsObj, *homogeneousObj;
const char *username, *password, *dsn, *poolName;
udt_StringBuffer username, password, dsn;
int threaded, events, homogeneous;
PyTypeObject *connectionType;
unsigned poolNameLength;
const char *poolName;
sword status;
ub4 poolMode;
ub1 getMode;
@ -190,11 +191,11 @@ static int SessionPool_Init(
threadedObj = eventsObj = homogeneousObj = NULL;
connectionType = &g_ConnectionType;
getMode = OCI_SPOOL_ATTRVAL_NOWAIT;
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "s#s#s#iii|OObOO",
keywordList, &username, &usernameLength, &password,
&passwordLength, &dsn, &dsnLength, &minSessions, &maxSessions,
&sessionIncrement, &connectionType, &threadedObj, &getMode,
&eventsObj, &homogeneousObj))
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O!O!O!iii|OObOO",
keywordList, CXORA_STRING_TYPE, &self->username,
CXORA_STRING_TYPE, &self->password, CXORA_STRING_TYPE, &self->dsn,
&minSessions, &maxSessions, &sessionIncrement, &connectionType,
&threadedObj, &getMode, &eventsObj, &homogeneousObj))
return -1;
if (!PyType_Check(connectionType)) {
PyErr_SetString(g_ProgrammingErrorException,
@ -225,6 +226,9 @@ static int SessionPool_Init(
// initialize the object's members
Py_INCREF(connectionType);
self->connectionType = connectionType;
Py_INCREF(self->dsn);
Py_INCREF(self->username);
Py_INCREF(self->password);
self->minSessions = minSessions;
self->maxSessions = maxSessions;
self->sessionIncrement = sessionIncrement;
@ -235,21 +239,6 @@ static int SessionPool_Init(
if (!self->environment)
return -1;
// create the string for the username
self->username = PyString_FromStringAndSize(username, usernameLength);
if (!self->username)
return -1;
// create the string for the password
self->password = PyString_FromStringAndSize(password, passwordLength);
if (!self->password)
return -1;
// create the string for the TNS entry
self->dsn = PyString_FromStringAndSize(dsn, dsnLength);
if (!self->dsn)
return -1;
// create the session pool handle
status = OCIHandleAlloc(self->environment->handle, (dvoid**) &self->handle,
OCI_HTYPE_SPOOL, 0, 0);
@ -263,19 +252,34 @@ static int SessionPool_Init(
poolMode |= OCI_SPC_HOMOGENEOUS;
// create the session pool
if (StringBuffer_Fill(&username, self->username) < 0)
return -1;
if (StringBuffer_Fill(&password, self->password) < 0) {
StringBuffer_CLEAR(&username);
return -1;
}
if (StringBuffer_Fill(&dsn, self->dsn) < 0) {
StringBuffer_CLEAR(&username);
StringBuffer_CLEAR(&password);
return -1;
}
Py_BEGIN_ALLOW_THREADS
status = OCISessionPoolCreate(self->environment->handle,
self->environment->errorHandle, self->handle,
(OraText**) &poolName, &poolNameLength, (OraText*) dsn, dsnLength,
minSessions, maxSessions, sessionIncrement, (OraText*) username,
usernameLength, (OraText*) password, passwordLength, poolMode);
(OraText**) &poolName, &poolNameLength, (OraText*) dsn.ptr,
dsn.size, minSessions, maxSessions, sessionIncrement,
(OraText*) username.ptr, username.size, (OraText*) password.ptr,
password.size, poolMode);
Py_END_ALLOW_THREADS
StringBuffer_CLEAR(&username);
StringBuffer_CLEAR(&password);
StringBuffer_CLEAR(&dsn);
if (Environment_CheckForError(self->environment, status,
"SessionPool_New(): create pool") < 0)
return -1;
// create the string for the pool name
self->name = PyString_FromStringAndSize(poolName, poolNameLength);
self->name = CXORA_BUFFER_TO_STRING(poolName, poolNameLength);
if (!self->name)
return -1;

View File

@ -3,6 +3,7 @@
// Defines constants and routines specific to handling strings.
//-----------------------------------------------------------------------------
// define structure for abstracting string buffers
typedef struct {
char *ptr;
Py_ssize_t size;
@ -12,6 +13,19 @@ typedef struct {
} udt_StringBuffer;
// use the bytes methods in cx_Oracle and define them as the equivalent string
// type methods as is done in Python 2.6
#ifndef PyBytes_Check
#define PyBytes_Type PyString_Type
#define PyBytes_AS_STRING PyString_AS_STRING
#define PyBytes_GET_SIZE PyString_GET_SIZE
#define PyBytes_Check PyString_Check
#define PyBytes_Format PyString_Format
#define PyBytes_FromString PyString_FromString
#define PyBytes_FromStringAndSize PyString_FromStringAndSize
#endif
//-----------------------------------------------------------------------------
// StringBuffer_Fill()
// Fill the string buffer with the UTF-16 data that Oracle expects.
@ -36,15 +50,15 @@ static int StringBuffer_Fill(
PyUnicode_GET_SIZE(obj), NULL, byteOrder);
if (!buf->encodedString)
return -1;
buf->ptr = PyString_AS_STRING(buf->encodedString);
buf->size = PyString_GET_SIZE(buf->encodedString);
buf->ptr = PyBytes_AS_STRING(buf->encodedString);
buf->size = PyBytes_GET_SIZE(buf->encodedString);
#else
buf->ptr = (char*) PyUnicode_AS_UNICODE(obj);
buf->size = PyUnicode_GET_DATA_SIZE(obj);
#endif
#else
buf->ptr = PyString_AS_STRING(obj);
buf->size = PyString_GET_SIZE(obj);
buf->ptr = PyBytes_AS_STRING(obj);
buf->size = PyBytes_GET_SIZE(obj);
#endif
return 0;
}
@ -55,23 +69,29 @@ static int StringBuffer_Fill(
#define CXORA_ERROR_TEXT_LENGTH 2048
#define CXORA_STRING_TYPE &PyUnicode_Type
#define CXORA_STRING_FROM_FORMAT PyUnicode_Format
#define CXORA_STRING_CHECK PyUnicode_Check
#define CXORA_ASCII_TO_STRING(str) \
PyUnicode_DecodeASCII(str, strlen(str), NULL)
#ifdef Py_UNICODE_WIDE
#define StringBuffer_CLEAR(buffer) \
Py_XDECREF((buffer)->encodedString)
#define CXORA_TO_STRING_OBJ(buffer, numBytes) \
#define CXORA_BUFFER_TO_STRING(buffer, numBytes) \
PyUnicode_DecodeUTF16(buffer, numBytes, NULL, NULL)
#else
#define StringBuffer_CLEAR(buffer)
#define CXORA_TO_STRING_OBJ(buffer, numBytes) \
#define CXORA_BUFFER_TO_STRING(buffer, numBytes) \
PyUnicode_FromUnicode((Py_UNICODE*) (buffer), (numBytes) / 2)
#endif
#else
#define CXORA_CHARSETID 0
#define CXORA_ERROR_TEXT_LENGTH 1024
#define CXORA_STRING_TYPE &PyString_Type
#define CXORA_STRING_FROM_FORMAT PyString_Format
#define CXORA_STRING_TYPE &PyBytes_Type
#define CXORA_STRING_FROM_FORMAT PyBytes_Format
#define CXORA_STRING_CHECK PyBytes_Check
#define StringBuffer_CLEAR(buffer)
#define CXORA_TO_STRING_OBJ(buffer, numBytes) \
PyString_FromStringAndSize(buffer, numBytes)
#define CXORA_ASCII_TO_STRING(str) \
PyBytes_FromString(str)
#define CXORA_BUFFER_TO_STRING(buffer, numBytes) \
PyBytes_FromStringAndSize(buffer, numBytes)
#endif

View File

@ -16,7 +16,9 @@ typedef struct {
// Declaration of string variable functions.
//-----------------------------------------------------------------------------
static int StringVar_Initialize(udt_StringVar*, udt_Cursor*);
#ifndef WITH_UNICODE
static int StringVar_PostDefine(udt_StringVar*);
#endif
static int StringVar_SetValue(udt_StringVar*, unsigned, PyObject*);
static PyObject *StringVar_GetValue(udt_StringVar*, unsigned);
@ -48,6 +50,7 @@ static PyTypeObject g_StringVarType = {
};
#ifndef WITH_UNICODE
static PyTypeObject g_UnicodeVarType = {
PyVarObject_HEAD_INIT(NULL, 0)
"cx_Oracle.UNICODE", // tp_name
@ -71,6 +74,7 @@ static PyTypeObject g_UnicodeVarType = {
Py_TPFLAGS_DEFAULT, // tp_flags
0 // tp_doc
};
#endif
static PyTypeObject g_FixedCharVarType = {
@ -98,6 +102,7 @@ static PyTypeObject g_FixedCharVarType = {
};
#ifndef WITH_UNICODE
static PyTypeObject g_FixedUnicodeVarType = {
PyVarObject_HEAD_INIT(NULL, 0)
"cx_Oracle.FIXED_UNICODE", // tp_name
@ -121,6 +126,7 @@ static PyTypeObject g_FixedUnicodeVarType = {
Py_TPFLAGS_DEFAULT, // tp_flags
0 // tp_doc
};
#endif
static PyTypeObject g_RowidVarType = {
@ -194,6 +200,7 @@ static udt_VariableType vt_String = {
};
#ifndef WITH_UNICODE
static udt_VariableType vt_NationalCharString = {
(InitializeProc) StringVar_Initialize,
(FinalizeProc) NULL,
@ -210,6 +217,7 @@ static udt_VariableType vt_NationalCharString = {
1, // can be copied
1 // can be in array
};
#endif
static udt_VariableType vt_FixedChar = {
@ -230,6 +238,7 @@ static udt_VariableType vt_FixedChar = {
};
#ifndef WITH_UNICODE
static udt_VariableType vt_FixedNationalChar = {
(InitializeProc) StringVar_Initialize,
(FinalizeProc) NULL,
@ -246,6 +255,7 @@ static udt_VariableType vt_FixedNationalChar = {
1, // can be copied
1 // can be in array
};
#endif
static udt_VariableType vt_Rowid = {
@ -302,6 +312,7 @@ static int StringVar_Initialize(
}
#ifndef WITH_UNICODE
//-----------------------------------------------------------------------------
// StringVar_PostDefine()
// Set the character set information when values are fetched from this
@ -329,6 +340,7 @@ static int StringVar_PostDefine(
return 0;
}
#endif
//-----------------------------------------------------------------------------
@ -343,7 +355,6 @@ static int StringVar_SetValue(
PyObject *encodedString;
Py_ssize_t bufferSize;
const void *buffer;
ub2 actualLength;
// get the buffer data and size for binding
encodedString = NULL;
@ -359,7 +370,6 @@ static int StringVar_SetValue(
"expecting string or buffer data");
return -1;
}
actualLength = (ub2) bufferSize;
} else {
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError, "expecting unicode data");
@ -372,13 +382,12 @@ static int StringVar_SetValue(
PyUnicode_GET_SIZE(value), NULL, byteOrder);
if (!encodedString)
return -1;
buffer = PyString_AS_STRING(encodedString);
bufferSize = PyString_GET_SIZE(encodedString);
buffer = PyBytes_AS_STRING(encodedString);
bufferSize = PyBytes_GET_SIZE(encodedString);
#else
buffer = PyUnicode_AS_UNICODE(value);
bufferSize = sizeof(Py_UNICODE) * PyUnicode_GET_SIZE(value);
bufferSize = PyUnicode_GET_DATA_SIZE(value);
#endif
actualLength = bufferSize / 2;
}
// ensure that the buffer is not too large
@ -395,7 +404,7 @@ static int StringVar_SetValue(
}
// keep a copy of the string
var->actualLength[pos] = actualLength;
var->actualLength[pos] = (ub2) bufferSize;
if (bufferSize)
memcpy(var->data + var->maxLength * pos, buffer, bufferSize);
Py_XDECREF(encodedString);
@ -415,12 +424,18 @@ static PyObject *StringVar_GetValue(
char *data;
data = var->data + pos * var->maxLength;
#ifdef WTIH_UNICODE
if (var->type->charsetForm == SQLCS_IMPLICIT)
#else
#ifdef WITH_UNICODE
if (var->type == &vt_Binary)
return PyBytes_FromStringAndSize(data, var->actualLength[pos]);
return CXORA_BUFFER_TO_STRING(data, var->actualLength[pos]);
#else
if (var->type->charsetForm == SQLCS_IMPLICIT)
return PyBytes_FromStringAndSize(data, var->actualLength[pos]);
#ifdef Py_UNICODE_WIDE
return PyUnicode_DecodeUTF16(data, var->actualLength[pos], NULL, NULL);
#else
return PyUnicode_FromUnicode((Py_UNICODE*) data, var->actualLength[pos]);
#endif
#endif
return PyString_FromStringAndSize(data, var->actualLength[pos]);
return CXORA_TO_STRING_OBJ(data, var->actualLength[pos]);
}

View File

@ -332,8 +332,10 @@ static int Variable_Check(
Py_TYPE(object) == &g_NumberVarType ||
Py_TYPE(object) == &g_StringVarType ||
Py_TYPE(object) == &g_FixedCharVarType ||
#ifndef WITH_UNICODE
Py_TYPE(object) == &g_UnicodeVarType ||
Py_TYPE(object) == &g_FixedUnicodeVarType ||
#endif
Py_TYPE(object) == &g_RowidVarType ||
Py_TYPE(object) == &g_BinaryVarType ||
Py_TYPE(object) == &g_TimestampVarType
@ -355,16 +357,18 @@ static udt_VariableType *Variable_TypeByPythonType(
{
if (type == (PyObject*) &g_StringVarType)
return &vt_String;
if (type == (PyObject*) &PyString_Type)
if (type == (PyObject*) CXORA_STRING_TYPE)
return &vt_String;
if (type == (PyObject*) &g_FixedCharVarType)
return &vt_FixedChar;
#ifndef WITH_UNICODE
if (type == (PyObject*) &g_UnicodeVarType)
return &vt_NationalCharString;
if (type == (PyObject*) &PyUnicode_Type)
return &vt_NationalCharString;
if (type == (PyObject*) &g_FixedUnicodeVarType)
return &vt_FixedNationalChar;
#endif
if (type == (PyObject*) &g_RowidVarType)
return &vt_Rowid;
if (type == (PyObject*) &g_BinaryVarType)
@ -437,10 +441,12 @@ static udt_VariableType *Variable_TypeByValue(
// handle scalars
if (value == Py_None)
return &vt_String;
if (PyString_Check(value))
if (CXORA_STRING_CHECK(value))
return &vt_String;
#ifndef WITH_UNICODE
if (PyUnicode_Check(value))
return &vt_NationalCharString;
#endif
if (PyInt_Check(value))
return &vt_Integer;
if (PyLong_Check(value))
@ -503,12 +509,16 @@ static udt_VariableType *Variable_TypeByOracleDataType (
case SQLT_LNG:
return &vt_LongString;
case SQLT_AFC:
#ifndef WITH_UNICODE
if (charsetForm == SQLCS_NCHAR)
return &vt_FixedNationalChar;
#endif
return &vt_FixedChar;
case SQLT_CHR:
#ifndef WITH_UNICODE
if (charsetForm == SQLCS_NCHAR)
return &vt_NationalCharString;
#endif
return &vt_String;
case SQLT_RDD:
return &vt_Rowid;
@ -1031,8 +1041,8 @@ static int Variable_InternalBind(
return -1;
// set the charset form and id if applicable
#ifndef WITH_UNICODE
if (var->type->charsetForm != SQLCS_IMPLICIT) {
ub4 lengthInChars = var->maxLength / 2;
ub2 charsetId = OCI_UTF16ID;
status = OCIAttrSet(var->bindHandle, OCI_HTYPE_BIND,
(dvoid*) &var->type->charsetForm, 0, OCI_ATTR_CHARSET_FORM,
@ -1047,12 +1057,13 @@ static int Variable_InternalBind(
"Variable_InternalBind(): setting charset Id") < 0)
return -1;
status = OCIAttrSet(var->bindHandle, OCI_HTYPE_BIND,
(dvoid*) &lengthInChars, 0, OCI_ATTR_CHAR_COUNT,
(dvoid*) &var->maxLength, 0, OCI_ATTR_MAXDATA_SIZE,
var->environment->errorHandle);
if (Environment_CheckForError(var->environment, status,
"Variable_InternalBind(): set char count") < 0)
"Variable_InternalBind(): set max data size") < 0)
return -1;
}
#endif
// set the max data size for strings
if ((var->type == &vt_String || var->type == &vt_FixedChar)

View File

@ -79,6 +79,7 @@ typedef int Py_ssize_t;
#define BUILD_VERSION_STRING xstr(BUILD_VERSION)
#define DRIVER_NAME "cx_Oracle-"BUILD_VERSION_STRING
#include "StringUtils.c"
//-----------------------------------------------------------------------------
// Globals
@ -98,6 +99,8 @@ static PyObject *g_ProgrammingErrorException = NULL;
static PyObject *g_NotSupportedErrorException = NULL;
static PyTypeObject *g_DateTimeType = NULL;
static PyTypeObject *g_DecimalType = NULL;
static PyObject *g_NumberToStringFormatObj = NULL;
static udt_StringBuffer g_NumberToStringFormatBuffer;
//-----------------------------------------------------------------------------
@ -141,7 +144,6 @@ static int GetModuleAndName(
}
#include "StringUtils.c"
#include "Environment.c"
#include "SessionPool.c"
@ -346,6 +348,14 @@ void initcx_Oracle(void)
"Decimal");
PyErr_Clear();
// set up the string and buffer for converting numbers to strings
g_NumberToStringFormatObj = CXORA_ASCII_TO_STRING("TM9");
if (!g_NumberToStringFormatObj)
return;
if (StringBuffer_Fill(&g_NumberToStringFormatBuffer,
g_NumberToStringFormatObj) < 0)
return;
// prepare the types for use by the module
MAKE_TYPE_READY(&g_ConnectionType);
MAKE_TYPE_READY(&g_CursorType);
@ -374,8 +384,10 @@ void initcx_Oracle(void)
MAKE_VARIABLE_TYPE_READY(&g_BFILEVarType);
MAKE_VARIABLE_TYPE_READY(&g_CursorVarType);
MAKE_VARIABLE_TYPE_READY(&g_ObjectVarType);
#ifndef WITH_UNICODE
MAKE_VARIABLE_TYPE_READY(&g_UnicodeVarType);
MAKE_VARIABLE_TYPE_READY(&g_FixedUnicodeVarType);
#endif
#ifdef SQLT_BFLOAT
MAKE_VARIABLE_TYPE_READY(&g_NativeFloatVarType);
#endif
@ -442,7 +454,10 @@ void initcx_Oracle(void)
ADD_TYPE_OBJECT("OBJECT", &g_ObjectVarType)
ADD_TYPE_OBJECT("DATETIME", &g_DateTimeVarType)
ADD_TYPE_OBJECT("FIXED_CHAR", &g_FixedCharVarType)
#ifndef WITH_UNICODE
ADD_TYPE_OBJECT("FIXED_UNICODE", &g_FixedUnicodeVarType)
ADD_TYPE_OBJECT("UNICODE", &g_UnicodeVarType)
#endif
ADD_TYPE_OBJECT("LOB", &g_ExternalLobVarType)
ADD_TYPE_OBJECT("LONG_BINARY", &g_LongBinaryVarType)
ADD_TYPE_OBJECT("LONG_STRING", &g_LongStringVarType)
@ -451,7 +466,6 @@ void initcx_Oracle(void)
ADD_TYPE_OBJECT("ROWID", &g_RowidVarType)
ADD_TYPE_OBJECT("STRING", &g_StringVarType)
ADD_TYPE_OBJECT("TIMESTAMP", &g_TimestampVarType)
ADD_TYPE_OBJECT("UNICODE", &g_UnicodeVarType)
#ifdef SQLT_BFLOAT
ADD_TYPE_OBJECT("NATIVE_FLOAT", &g_NativeFloatVarType)
#endif

View File

@ -2,13 +2,16 @@
import cx_Oracle
import os
import sys
import unittest
def GetValue(name, label):
value = os.environ.get("CX_ORACLE_" + name)
if value is None:
value = raw_input(label + ": ")
return value
if hasattr(cx_Oracle, "UNICODE") or sys.version_info[0] >= 3:
return value
return unicode(value)
USERNAME = GetValue("USERNAME", "user name")
PASSWORD = GetValue("PASSWORD", "password")

View File

@ -10,20 +10,25 @@ print "Running tests for cx_Oracle version", cx_Oracle.version
import TestEnv
moduleNames = [
"Connection",
"Cursor",
"CursorVar",
"DateTimeVar",
"LobVar",
"LongVar",
"NumberVar",
"ObjectVar",
"SessionPool",
"StringVar",
"TimestampVar",
"UnicodeVar"
]
if hasattr(cx_Oracle, "UNICODE") or sys.version_info[0] >= 3:
moduleNames = [
"Connection",
"Cursor",
"CursorVar",
"DateTimeVar",
"LobVar",
"LongVar",
"NumberVar",
"ObjectVar",
"SessionPool",
"StringVar",
"TimestampVar",
"UnicodeVar"
]
else:
moduleNames = [
"uConnection"
]
class BaseTestCase(unittest.TestCase):

116
test/uConnection.py Normal file
View File

@ -0,0 +1,116 @@
"""Module for testing connections."""
import threading
class TestConnection(TestCase):
def __ConnectAndDrop(self):
"""Connect to the database, perform a query and drop the connection."""
connection = cx_Oracle.connect(self.username, self.password,
self.tnsentry, threaded = True)
cursor = connection.cursor()
cursor.execute(u"select count(*) from TestNumbers")
count, = cursor.fetchone()
self.failUnlessEqual(count, 10)
def setUp(self):
self.username = USERNAME
self.password = PASSWORD
self.tnsentry = TNSENTRY
def verifyArgs(self, connection):
self.failUnlessEqual(connection.username, self.username,
"user name differs")
self.failUnlessEqual(connection.password, self.password,
"password differs")
self.failUnlessEqual(connection.tnsentry, self.tnsentry,
"tnsentry differs")
def testAllArgs(self):
"connection to database with user, password, TNS separate"
connection = cx_Oracle.connect(self.username, self.password,
self.tnsentry)
self.verifyArgs(connection)
def testBadConnectString(self):
"connection to database with bad connect string"
self.failUnlessRaises(cx_Oracle.DatabaseError, cx_Oracle.connect,
self.username)
self.failUnlessRaises(cx_Oracle.DatabaseError, cx_Oracle.connect,
self.username + u"@" + self.tnsentry)
self.failUnlessRaises(cx_Oracle.DatabaseError, cx_Oracle.connect,
self.username + u"@" + self.tnsentry + u"/" + self.password)
def testBadPassword(self):
"connection to database with bad password"
self.failUnlessRaises(cx_Oracle.DatabaseError, cx_Oracle.connect,
self.username, self.password + u"X", self.tnsentry)
def testExceptionOnClose(self):
"confirm an exception is raised after closing a connection"
connection = cx_Oracle.connect(self.username, self.password,
self.tnsentry)
connection.close()
self.failUnlessRaises(cx_Oracle.InterfaceError, connection.rollback)
def testMakeDSN(self):
"test making a data source name from host, port and sid"
formatString = u"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=" \
u"(PROTOCOL=TCP)(HOST=%s)(PORT=%d)))(CONNECT_DATA=(SID=%s)))"
args = (u"hostname", 1521, u"TEST")
result = cx_Oracle.makedsn(*args)
self.failUnlessEqual(result, formatString % args)
def testSingleArg(self):
"connection to database with user, password, TNS together"
connection = cx_Oracle.connect(u"%s/%s@%s" % \
(self.username, self.password, self.tnsentry))
self.verifyArgs(connection)
def testVersion(self):
"connection version is a string"
connection = cx_Oracle.connect(self.username, self.password,
self.tnsentry)
self.failUnless(isinstance(connection.version, unicode))
def testRollbackOnClose(self):
"connection rolls back before close"
connection = cx_Oracle.connect(self.username, self.password,
self.tnsentry)
cursor = connection.cursor()
cursor.execute(u"truncate table TestExecuteMany")
otherConnection = cx_Oracle.connect(self.username, self.password,
self.tnsentry)
otherCursor = otherConnection.cursor()
otherCursor.execute(u"insert into TestExecuteMany values (1)")
otherConnection.close()
cursor.execute(u"select count(*) from TestExecuteMany")
count, = cursor.fetchone()
self.failUnlessEqual(count, 0)
def testRollbackOnDel(self):
"connection rolls back before destruction"
connection = cx_Oracle.connect(self.username, self.password,
self.tnsentry)
cursor = connection.cursor()
cursor.execute(u"truncate table TestExecuteMany")
otherConnection = cx_Oracle.connect(self.username, self.password,
self.tnsentry)
otherCursor = otherConnection.cursor()
otherCursor.execute(u"insert into TestExecuteMany values (1)")
del otherCursor
del otherConnection
cursor.execute(u"select count(*) from TestExecuteMany")
count, = cursor.fetchone()
self.failUnlessEqual(count, 0)
def testThreading(self):
"connection to database with multiple threads"
threads = []
for i in range(20):
thread = threading.Thread(None, self.__ConnectAndDrop)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()