Ensure that the encoding and nencoding values are respected when the OCI
environment is created; ensure that these values are also used when LOBs are read and written.
This commit is contained in:
parent
8fda704186
commit
2c57b455e6
@ -11,9 +11,11 @@ typedef struct {
|
||||
OCIEnv *handle;
|
||||
OCIError *errorHandle;
|
||||
int maxBytesPerCharacter;
|
||||
int fixedWidth;
|
||||
int nmaxBytesPerCharacter;
|
||||
char *encoding;
|
||||
char *nencoding;
|
||||
ub2 charsetId;
|
||||
ub2 ncharsetId;
|
||||
PyObject *cloneEnv;
|
||||
udt_Buffer numberToStringFormatBuffer;
|
||||
udt_Buffer numberFromStringFormatBuffer;
|
||||
@ -73,8 +75,8 @@ static udt_Environment *Environment_New(
|
||||
if (!env)
|
||||
return NULL;
|
||||
env->handle = handle;
|
||||
env->fixedWidth = 1;
|
||||
env->maxBytesPerCharacter = 1;
|
||||
env->nmaxBytesPerCharacter = 4;
|
||||
cxBuffer_Init(&env->numberToStringFormatBuffer);
|
||||
cxBuffer_Init(&env->numberFromStringFormatBuffer);
|
||||
cxBuffer_Init(&env->nlsNumericCharactersBuffer);
|
||||
@ -107,12 +109,19 @@ 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 **result, // place to store result
|
||||
ub2 *charsetId) // character set ID (OUT)
|
||||
{
|
||||
char charsetName[OCI_NLS_MAXBUFSZ], ianaCharsetName[OCI_NLS_MAXBUFSZ];
|
||||
ub2 charsetId;
|
||||
sword status;
|
||||
|
||||
// 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;
|
||||
|
||||
// if override value specified, use it
|
||||
if (overrideValue) {
|
||||
*result = PyMem_Malloc(strlen(overrideValue) + 1);
|
||||
@ -122,16 +131,9 @@ static int Environment_GetCharacterSetName(
|
||||
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);
|
||||
OCI_NLS_MAXBUFSZ, *charsetId);
|
||||
if (Environment_CheckForError(self, status,
|
||||
"Environment_GetCharacterSetName(): get Oracle charset name") < 0)
|
||||
return -1;
|
||||
@ -292,23 +294,20 @@ static udt_Environment *Environment_NewFromScratch(
|
||||
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)
|
||||
encoding, &env->encoding, &env->charsetId) < 0)
|
||||
return NULL;
|
||||
if (Environment_GetCharacterSetName(env, OCI_ATTR_ENV_NCHARSET_ID,
|
||||
nencoding, &env->nencoding) < 0)
|
||||
nencoding, &env->nencoding, &env->ncharsetId) < 0)
|
||||
return NULL;
|
||||
|
||||
// max bytes per character for NCHAR can be assigned only if it matches the
|
||||
// character set used for CHAR data; OCI does not provide a way of
|
||||
// determining it otherwise
|
||||
if (env->ncharsetId == env->charsetId)
|
||||
env->nmaxBytesPerCharacter = env->maxBytesPerCharacter;
|
||||
|
||||
// fill buffers for number formats
|
||||
if (Environment_SetBuffer(&env->numberToStringFormatBuffer, "TM9",
|
||||
env->encoding) < 0)
|
||||
@ -339,11 +338,12 @@ static udt_Environment *Environment_Clone(
|
||||
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;
|
||||
env->charsetId = cloneEnv->charsetId;
|
||||
env->ncharsetId = cloneEnv->ncharsetId;
|
||||
cxBuffer_Copy(&env->numberToStringFormatBuffer,
|
||||
&cloneEnv->numberToStringFormatBuffer);
|
||||
cxBuffer_Copy(&env->numberFromStringFormatBuffer,
|
||||
|
||||
@ -169,17 +169,13 @@ static int ExternalLobVar_InternalRead(
|
||||
oraub8 *length, // length of data (IN/OUT)
|
||||
oraub8 offset) // offset
|
||||
{
|
||||
oraub8 lengthInBytes, lengthInChars;
|
||||
oraub8 lengthInBytes = 0, lengthInChars = 0;
|
||||
ub2 charsetId;
|
||||
sword status;
|
||||
|
||||
if (var->lobVar->type == &vt_NCLOB || var->lobVar->type == &vt_CLOB) {
|
||||
lengthInBytes = 0;
|
||||
if (var->lobVar->type == &vt_NCLOB || var->lobVar->type == &vt_CLOB)
|
||||
lengthInChars = *length;
|
||||
} else {
|
||||
lengthInChars = 0;
|
||||
lengthInBytes = *length;
|
||||
}
|
||||
else lengthInBytes = *length;
|
||||
|
||||
if (var->lobVar->isFile) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
@ -193,9 +189,9 @@ static int ExternalLobVar_InternalRead(
|
||||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
if (var->lobVar->type == &vt_NCLOB)
|
||||
charsetId = OCI_UTF16ID;
|
||||
else charsetId = 0;
|
||||
charsetId = (var->lobVar->type->charsetForm == SQLCS_NCHAR) ?
|
||||
var->lobVar->environment->ncharsetId :
|
||||
var->lobVar->environment->charsetId;
|
||||
status = OCILobRead2(var->lobVar->connection->handle,
|
||||
var->lobVar->environment->errorHandle, var->lobVar->data[var->pos],
|
||||
&lengthInBytes, &lengthInChars, offset, buffer, bufferSize,
|
||||
@ -275,10 +271,11 @@ static PyObject *ExternalLobVar_Value(
|
||||
else amount = 1;
|
||||
}
|
||||
length = amount;
|
||||
|
||||
if (var->lobVar->type == &vt_CLOB)
|
||||
bufferSize = amount * var->lobVar->environment->maxBytesPerCharacter;
|
||||
else if (var->lobVar->type == &vt_NCLOB)
|
||||
bufferSize = amount * 2;
|
||||
bufferSize = amount * var->lobVar->environment->nmaxBytesPerCharacter;
|
||||
else bufferSize = amount;
|
||||
|
||||
// create a string for retrieving the value
|
||||
@ -296,7 +293,8 @@ static PyObject *ExternalLobVar_Value(
|
||||
result = cxString_FromEncodedString(buffer, length,
|
||||
var->lobVar->environment->encoding);
|
||||
} else if (var->lobVar->type == &vt_NCLOB) {
|
||||
result = PyUnicode_DecodeUTF16(buffer, length, NULL, NULL);
|
||||
result = PyUnicode_Decode(buffer, length,
|
||||
var->lobVar->environment->encoding, NULL);
|
||||
} else {
|
||||
result = PyBytes_FromStringAndSize(buffer, length);
|
||||
}
|
||||
@ -413,7 +411,7 @@ static PyObject *ExternalLobVar_Str(
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExternalLobVar_Write()
|
||||
// Write a value to the LOB variable; return the number of bytes written.
|
||||
// Write a value to the LOB at the specified offset.
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ExternalLobVar_Write(
|
||||
udt_ExternalLobVar *var, // variable to perform write against
|
||||
@ -421,8 +419,8 @@ static PyObject *ExternalLobVar_Write(
|
||||
PyObject *keywordArgs) // keyword arguments
|
||||
{
|
||||
static char *keywordList[] = { "data", "offset", NULL };
|
||||
oraub8 amount, offset;
|
||||
PyObject *dataObj;
|
||||
oraub8 offset;
|
||||
|
||||
// buffer is expected, offset is optional
|
||||
offset = 1;
|
||||
@ -433,11 +431,10 @@ static PyObject *ExternalLobVar_Write(
|
||||
// perform the write, if possible
|
||||
if (ExternalLobVar_Verify(var) < 0)
|
||||
return NULL;
|
||||
if (LobVar_Write(var->lobVar, var->pos, dataObj, offset, &amount) < 0)
|
||||
if (LobVar_Write(var->lobVar, var->pos, dataObj, offset) < 0)
|
||||
return NULL;
|
||||
|
||||
// return the result
|
||||
return PyLong_FromUnsignedLong(amount);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
52
LobVar.c
52
LobVar.c
@ -21,7 +21,7 @@ static int LobVar_PreFetch(udt_LobVar*);
|
||||
static void LobVar_Finalize(udt_LobVar*);
|
||||
static PyObject *LobVar_GetValue(udt_LobVar*, unsigned);
|
||||
static int LobVar_SetValue(udt_LobVar*, unsigned, PyObject*);
|
||||
static int LobVar_Write(udt_LobVar*, unsigned, PyObject*, oraub8, oraub8*);
|
||||
static int LobVar_Write(udt_LobVar*, unsigned, PyObject*, oraub8);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Python type declarations
|
||||
@ -318,49 +318,44 @@ static int LobVar_Write(
|
||||
udt_LobVar *var, // variable to perform write against
|
||||
unsigned position, // position to perform write against
|
||||
PyObject *dataObj, // data object to write into LOB
|
||||
oraub8 offset, // offset into variable
|
||||
oraub8 *amount) // amount to write
|
||||
oraub8 offset) // offset into variable
|
||||
{
|
||||
oraub8 lengthInBytes, lengthInChars = 0;
|
||||
const char *encoding;
|
||||
udt_Buffer buffer;
|
||||
ub2 charsetId;
|
||||
sword status;
|
||||
|
||||
// verify the data type
|
||||
if (var->type == &vt_BFILE) {
|
||||
PyErr_SetString(PyExc_TypeError, "BFILEs are read only");
|
||||
return -1;
|
||||
} else if (var->type == &vt_BLOB) {
|
||||
if (cxBuffer_FromObject(&buffer, dataObj,
|
||||
var->environment->encoding) < 0)
|
||||
return -1;
|
||||
*amount = buffer.size;
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
} else if (var->type == &vt_NCLOB) {
|
||||
if (cxBuffer_FromObject(&buffer, dataObj,
|
||||
var->environment->nencoding) < 0)
|
||||
return -1;
|
||||
*amount = buffer.size;
|
||||
#endif
|
||||
} else {
|
||||
if (cxBuffer_FromObject(&buffer, dataObj,
|
||||
var->environment->encoding) < 0)
|
||||
return -1;
|
||||
if (var->environment->fixedWidth
|
||||
&& var->environment->maxBytesPerCharacter > 1)
|
||||
*amount = buffer.size / var->environment->maxBytesPerCharacter;
|
||||
else *amount = buffer.size;
|
||||
}
|
||||
|
||||
// determine the buffer to write
|
||||
if (var->type->charsetForm == SQLCS_NCHAR) {
|
||||
charsetId = var->environment->ncharsetId;
|
||||
encoding = var->environment->nencoding;
|
||||
} else {
|
||||
charsetId = var->environment->charsetId;
|
||||
encoding = var->environment->encoding;
|
||||
}
|
||||
if (cxBuffer_FromObject(&buffer, dataObj, encoding) < 0)
|
||||
return -1;
|
||||
lengthInBytes = buffer.size;
|
||||
|
||||
// nothing to do if no data to write
|
||||
if (*amount == 0) {
|
||||
if (lengthInBytes == 0) {
|
||||
cxBuffer_Clear(&buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// write the data with the correct character set
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
status = OCILobWrite2(var->connection->handle,
|
||||
var->environment->errorHandle, var->data[position], amount, 0,
|
||||
offset, (void*) buffer.ptr, buffer.size, OCI_ONE_PIECE, NULL, NULL,
|
||||
0, var->type->charsetForm);
|
||||
var->environment->errorHandle, var->data[position], &lengthInBytes,
|
||||
&lengthInChars, offset, (void*) buffer.ptr, buffer.size,
|
||||
OCI_ONE_PIECE, NULL, NULL, charsetId, var->type->charsetForm);
|
||||
Py_END_ALLOW_THREADS
|
||||
cxBuffer_Clear(&buffer);
|
||||
if (Environment_CheckForError(var->environment, status,
|
||||
@ -393,7 +388,6 @@ static int LobVar_SetValue(
|
||||
PyObject *value) // value to set
|
||||
{
|
||||
boolean isTemporary;
|
||||
oraub8 amount;
|
||||
sword status;
|
||||
ub1 lobType;
|
||||
|
||||
@ -428,6 +422,6 @@ static int LobVar_SetValue(
|
||||
return -1;
|
||||
|
||||
// set the current value
|
||||
return LobVar_Write(var, position, value, 1, &amount);
|
||||
return LobVar_Write(var, position, value, 1);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user