Added support for binding boolean values to PL/SQL booleans -- a feature that

is only available in Oracle 12.1 and higher.
This commit is contained in:
Anthony Tuininga 2016-01-14 21:41:36 -07:00
parent 2398ed617d
commit 8c1e6f68e4
11 changed files with 218 additions and 4 deletions

105
BooleanVar.c Normal file
View File

@ -0,0 +1,105 @@
//-----------------------------------------------------------------------------
// BooleanVar.c
// Defines the routines for handling boolean variables (only available after
// Oracle 12.1).
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Data types
//-----------------------------------------------------------------------------
typedef struct {
Variable_HEAD
int *data;
} udt_BooleanVar;
//-----------------------------------------------------------------------------
// Declaration of variable functions.
//-----------------------------------------------------------------------------
static int BooleanVar_SetValue(udt_BooleanVar*, unsigned, PyObject*);
static PyObject *BooleanVar_GetValue(udt_BooleanVar*, unsigned);
//-----------------------------------------------------------------------------
// Python type declaration
//-----------------------------------------------------------------------------
static PyTypeObject g_BooleanVarType = {
PyVarObject_HEAD_INIT(NULL, 0)
"cx_Oracle.BOOLEAN", // tp_name
sizeof(udt_BooleanVar), // tp_basicsize
0, // tp_itemsize
0, // 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
};
//-----------------------------------------------------------------------------
// variable type declarations
//-----------------------------------------------------------------------------
static udt_VariableType vt_Boolean = {
(InitializeProc) NULL,
(FinalizeProc) NULL,
(PreDefineProc) NULL,
(PostDefineProc) NULL,
(PreFetchProc) NULL,
(IsNullProc) NULL,
(SetValueProc) BooleanVar_SetValue,
(GetValueProc) BooleanVar_GetValue,
(GetBufferSizeProc) NULL,
&g_BooleanVarType, // Python type
SQLT_BOL, // Oracle type
SQLCS_IMPLICIT, // charset form
sizeof(int), // element length
0, // is character data
0, // is variable length
1, // can be copied
0 // can be in array
};
//-----------------------------------------------------------------------------
// BooleanVar_GetValue()
// Returns the value stored at the given array position.
//-----------------------------------------------------------------------------
static PyObject *BooleanVar_GetValue(
udt_BooleanVar *var, // variable to determine value for
unsigned pos) // array position
{
int integerValue;
PyObject *value;
integerValue = var->data[pos];
value = (integerValue) ? Py_True : Py_False;
Py_INCREF(value);
return value;
}
//-----------------------------------------------------------------------------
// BooleanVar_SetValue()
// Set the value of the variable at the given array position.
//-----------------------------------------------------------------------------
static int BooleanVar_SetValue(
udt_BooleanVar *var, // variable to set value for
unsigned pos, // array position to set
PyObject *value) // value to set
{
var->data[pos] = (value == Py_True);
return 0;
}

View File

@ -1313,7 +1313,7 @@ static int Cursor_CallCalculateSize(
// assume up to 9 characters for each positional argument
// this allows up to four digits for the placeholder if the bind variale
// is a boolean value
// is a boolean value (prior to Oracle 12.1)
if (listOfArguments) {
numPositionalArgs = PySequence_Size(listOfArguments);
if (numPositionalArgs < 0)
@ -1323,7 +1323,7 @@ static int Cursor_CallCalculateSize(
// assume up to 15 characters for each keyword argument
// this allows up to four digits for the placeholder if the bind variable
// is a boolean value
// is a boolean value (prior to Oracle 12.1)
if (keywordArguments) {
numKeywordArgs = PyDict_Size(keywordArguments);
if (numKeywordArgs < 0)
@ -1400,8 +1400,10 @@ static int Cursor_CallBuildStatement(
if (i > 0)
*ptr++ = ',';
ptr += sprintf(ptr, ":%d", argNum++);
#if ORACLE_VERSION_HEX < ORACLE_VERSION(12, 1)
if (PyBool_Check(PySequence_Fast_GET_ITEM(positionalArgs, i)))
ptr += sprintf(ptr, " = 1");
#endif
}
Py_DECREF(positionalArgs);
}
@ -1421,8 +1423,10 @@ static int Cursor_CallBuildStatement(
if ((argNum > 1 && !returnValue) || (argNum > 2 && returnValue))
*ptr++ = ',';
ptr += sprintf(ptr, "%%s => :%d", argNum++);
#if ORACLE_VERSION_HEX < ORACLE_VERSION(12, 1)
if (PyBool_Check(value))
ptr += sprintf(ptr, " = 1");
#endif
}
}

View File

@ -189,6 +189,7 @@ static udt_VariableType vt_NumberAsString = {
};
#if ORACLE_VERSION_HEX < ORACLE_VERSION(12, 1)
static udt_VariableType vt_Boolean = {
(InitializeProc) NULL,
(FinalizeProc) NULL,
@ -208,6 +209,7 @@ static udt_VariableType vt_Boolean = {
1, // can be copied
1 // can be in array
};
#endif
//-----------------------------------------------------------------------------

View File

@ -162,6 +162,9 @@ static PyTypeObject g_BaseVarType = {
};
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12, 1)
#include "BooleanVar.c"
#endif
#include "Transforms.c"
#include "StringVar.c"
#include "LongVar.c"
@ -382,6 +385,9 @@ static int Variable_Check(
Py_TYPE(object) == &g_BinaryVarType ||
Py_TYPE(object) == &g_TimestampVarType ||
Py_TYPE(object) == &g_IntervalVarType ||
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12,1)
Py_TYPE(object) == &g_BooleanVarType ||
#endif
Py_TYPE(object) == &g_NativeFloatVarType);
}
@ -442,6 +448,10 @@ static udt_VariableType *Variable_TypeByPythonType(
#endif
if (type == (PyObject*) &PyLong_Type)
return &vt_LongInteger;
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12,1)
if (type == (PyObject*) &g_BooleanVarType)
return &vt_Boolean;
#endif
if (type == (PyObject*) &PyBool_Type)
return &vt_Boolean;
if (type == (PyObject*) &g_DateTimeVarType)
@ -495,6 +505,8 @@ static udt_VariableType *Variable_TypeByValue(
return &vt_LongString;
return &vt_String;
}
if (PyBool_Check(value))
return &vt_Boolean;
#if PY_MAJOR_VERSION < 3
if (PyUnicode_Check(value)) {
*size = PyUnicode_GET_SIZE(value);
@ -510,8 +522,6 @@ static udt_VariableType *Variable_TypeByValue(
return &vt_Binary;
}
#endif
if (PyBool_Check(value))
return &vt_Boolean;
if (PyLong_Check(value))
return &vt_LongInteger;
if (PyFloat_Check(value))

View File

@ -415,6 +415,9 @@ static PyObject *Module_Initialize(void)
MAKE_VARIABLE_TYPE_READY(&g_NCLOBVarType);
MAKE_VARIABLE_TYPE_READY(&g_NativeFloatVarType);
MAKE_VARIABLE_TYPE_READY(&g_IntervalVarType);
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12, 1)
MAKE_VARIABLE_TYPE_READY(&g_BooleanVarType);
#endif
// initialize module and retrieve the dictionary
#if PY_MAJOR_VERSION >= 3
@ -494,6 +497,9 @@ static PyObject *Module_Initialize(void)
ADD_TYPE_OBJECT("STRING", &g_StringVarType)
ADD_TYPE_OBJECT("TIMESTAMP", &g_TimestampVarType)
ADD_TYPE_OBJECT("NATIVE_FLOAT", &g_NativeFloatVarType)
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12, 1)
ADD_TYPE_OBJECT("BOOLEAN", &g_BooleanVarType)
#endif
// create constants required by Python DB API 2.0
if (PyModule_AddStringConstant(module, "apilevel", "2.0") < 0)

View File

@ -637,6 +637,17 @@ Types
This type is an extension to the DB API definition.
.. data:: BOOLEAN
This type object is used to represent PL/SQL booleans.
.. note::
This type is an extension to the DB API definition. It is only available
in Oracle 12.1 and higher and within PL/SQL. It cannot be used in
columns.
.. data:: CLOB
This type object is used to describe columns in a database that are CLOBs.

View File

@ -21,6 +21,8 @@ Version 5.2.1
4) Use the national character set encoding when required (when char set form is
SQLCS_NCHAR); otherwise, the wrong encoding would be used if the environment
variable NLS_NCHAR is set.
5) Added support for binding boolean values to PL/SQL blocks and stored
procedures (available in Oracle 12.1).
Version 5.2

35
test/BooleanVar.py Normal file
View File

@ -0,0 +1,35 @@
"""Module for testing boolean variables."""
class TestBooleanVar(BaseTestCase):
def testBindFalse(self):
"test binding in a False value"
result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
(False,))
self.failUnlessEqual(result, "FALSE")
def testBindNull(self):
"test binding in a null value"
self.cursor.setinputsizes(None, bool)
result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
(None,))
self.failUnlessEqual(result, "NULL")
def testBindOutFalse(self):
"test binding out a boolean value (False)"
result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10",
cx_Oracle.BOOLEAN, (15,))
self.failUnlessEqual(result, False)
def testBindOutTrue(self):
"test binding out a boolean value (True)"
result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10", bool,
(5,))
self.failUnlessEqual(result, True)
def testBindTrue(self):
"test binding in a True value"
result = self.cursor.callfunc("pkg_TestBooleans.GetStringRep", str,
(True,))
self.failUnlessEqual(result, "TRUE")

View File

@ -560,5 +560,42 @@ create or replace package body cx_Oracle.pkg_TestOutCursors as
end;
/
create or replace package cx_Oracle.pkg_TestBooleans as
function GetStringRep (
a_Value boolean
) return varchar2;
function IsLessThan10 (
a_Value number
) return boolean;
end;
/
create or replace package body cx_Oracle.pkg_TestBooleans as
function GetStringRep (
a_Value boolean
) return varchar2 is
begin
if a_Value is null then
return 'NULL';
elsif a_Value then
return 'TRUE';
end if;
return 'FALSE';
end;
function IsLessThan10 (
a_Value number
) return boolean is
begin
return a_Value < 10;
end;
end;
/
exit

View File

@ -46,6 +46,7 @@ else:
if cx_Oracle.clientversion()[0] >= 12:
moduleNames.insert(0, "uArrayDMLBatchError")
moduleNames.insert(0, "ArrayDMLBatchError")
moduleNames.append("BooleanVar")
class BaseTestCase(unittest.TestCase):

View File

@ -31,6 +31,7 @@ else:
]
if cx_Oracle.clientversion()[0] >= 12:
moduleNames.insert(0, "3kArrayDMLBatchError")
moduleNames.append("BooleanVar")
class BaseTestCase(unittest.TestCase):