Reworked type management to clarify and simplify code (see release notes for

details).
This commit is contained in:
Anthony Tuininga 2020-02-10 10:02:03 -07:00
parent 08346005a7
commit f7b7785e82
42 changed files with 1976 additions and 996 deletions

View File

@ -157,7 +157,8 @@ Cursor Object
will be None for operations that do not return rows or if the cursor has will be None for operations that do not return rows or if the cursor has
not had an operation invoked via the :meth:`~Cursor.execute()` method yet. not had an operation invoked via the :meth:`~Cursor.execute()` method yet.
The type will be one of the type objects defined at the module level. The type will be one of the :ref:`database type constants <dbtypes>`
defined at the module level.
.. method:: Cursor.execute(statement, [parameters], \*\*keywordParameters) .. method:: Cursor.execute(statement, [parameters], \*\*keywordParameters)
@ -554,10 +555,34 @@ Cursor Object
use in input and output type handlers defined on cursors or connections. use in input and output type handlers defined on cursors or connections.
The dataType parameter specifies the type of data that should be stored in The dataType parameter specifies the type of data that should be stored in
the variable. This should be one of the types defined at the module level the variable. This should be one of the
(such as :data:`cx_Oracle.STRING`) or a Python type that cx_Oracle knows :ref:`database type constants <dbtypes>`, :ref:`DB API constants <types>`,
how to process (such as str) or an object type returned from the method an object type returned from the method :meth:`Connection.gettype()` or one
:meth:`Connection.gettype()`. of the following Python types:
.. list-table::
:header-rows: 1
* - Python Type
- Database Type
* - bool
- :attr:`cx_Oracle.DB_TYPE_BOOLEAN`
* - bytes
- :attr:`cx_Oracle.DB_TYPE_RAW`
* - datetime.date
- :attr:`cx_Oracle.DB_TYPE_DATE`
* - datetime.datetime
- :attr:`cx_Oracle.DB_TYPE_DATE`
* - datetime.timedelta
- :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS`
* - decimal.Decimal
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
* - float
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
* - int
- :attr:`cx_Oracle.DB_TYPE_NUMBER`
* - str
- :attr:`cx_Oracle.DB_TYPE_VARCHAR`
The size parameter specifies the length of string and raw variables and is The size parameter specifies the length of string and raw variables and is
ignored in all other cases. If not specified for string and raw variables, ignored in all other cases. If not specified for string and raw variables,
@ -577,8 +602,8 @@ Cursor Object
was passed directly as the first parameter. was passed directly as the first parameter.
The encodingErrors parameter specifies what should happen when decoding The encodingErrors parameter specifies what should happen when decoding
byte strings fetched from the database into strings (Python 3) or unicode byte strings fetched from the database into strings. It should be one of
objects (Python 2). It should be one of the values noted in the builtin the values noted in the builtin
`decode <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__ `decode <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__
function. function.

View File

@ -79,6 +79,14 @@ See :ref:`lobdata` for more information about using LOBs.
Trim the LOB to the new size. Trim the LOB to the new size.
.. attribute:: LOB.type
This read-only attribute returns the type of the LOB as one of the
:ref:`database type constants <dbtypes>`.
.. versionadded:: 8.0
.. method:: LOB.write(data, [offset=1]) .. method:: LOB.write(data, [offset=1])
Write the data to the LOB object at the given offset. The offset is in Write the data to the LOB object at the given offset. The offset is in

View File

@ -972,210 +972,369 @@ or more of these values can be OR'ed together.
.. _types: .. _types:
Types DB API Types
===== ------------
.. data:: BINARY .. data:: BINARY
This type object is used to describe columns in a database that contain This type object is used to describe columns in a database that contain
binary data. In Oracle this is RAW columns. binary data. The database types :data:`DB_TYPE_RAW`,
:data:`DB_TYPE_LONG_RAW`, :data:`DB_TYPE_BLOB` and :data:`DB_TYPE_BFILE`.
will all compare equal to this value. If a variable is created with this
.. data:: BFILE type, the database type :data:`DB_TYPE_RAW` will be used.
This type object is used to describe columns in a database that are BFILEs.
.. note::
This type is an extension to the DB API definition.
.. data:: BLOB
This type object is used to describe columns in a database that are BLOBs.
.. note::
This type is an extension to the DB API definition.
.. data:: BOOLEAN
This type object is used to represent PL/SQL booleans.
.. versionadded:: 5.2.1
.. note::
This type is an extension to the DB API definition. It is only
available in Oracle 12.1 and higher and only 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.
.. note::
This type is an extension to the DB API definition.
.. data:: CURSOR
This type object is used to describe columns in a database that are cursors
(in PL/SQL these are known as ref cursors).
.. note::
This type is an extension to the DB API definition.
.. data:: DATETIME .. data:: DATETIME
This type object is used to describe columns in a database that are dates. This type object is used to describe columns in a database that are dates.
The database types :data:`DB_TYPE_DATE`, :data:`DB_TYPE_TIMESTAMP`,
:data:`DB_TYPE_TIMESTAMP_LTZ` and :data:`DB_TYPE_TIMESTAMP_TZ` will all
.. data:: FIXED_CHAR compare equal to this value. If a variable is created with this
type, the database type :data:`DB_TYPE_DATE` will be used.
This type object is used to describe columns in a database that are fixed
length strings (in Oracle these is CHAR columns); these behave differently
in Oracle than varchar2 so they are differentiated here even though the DB
API does not differentiate them.
.. note::
This attribute is an extension to the DB API definition.
.. data:: FIXED_NCHAR
This type object is used to describe columns in a database that are NCHAR
columns in Oracle; these behave differently in Oracle than nvarchar2 so
they are differentiated here even though the DB API does not differentiate
them.
.. note::
This type is an extension to the DB API definition.
.. data:: INTERVAL
This type object is used to describe columns in a database that are of type
interval day to second.
.. note::
This type is an extension to the DB API definition.
.. data:: LOB
This type object is the Python type of :data:`BLOB` and :data:`CLOB` data
that is returned from cursors.
.. note::
This type is an extension to the DB API definition.
.. data:: LONG_BINARY
This type object is used to describe columns in a database that are long
binary (in Oracle these are LONG RAW columns).
.. note::
This type is an extension to the DB API definition.
.. data:: LONG_STRING
This type object is used to describe columns in a database that are long
strings (in Oracle these are LONG columns).
.. note::
This type is an extension to the DB API definition.
.. data:: NATIVE_FLOAT
This type object is used to describe columns in a database that are of type
binary_double or binary_float.
.. note::
This type is an extension to the DB API definition.
.. data:: NATIVE_INT
This type object is used to bind integers using Oracle's native integer
support, rather than the standard number support.
.. versionadded:: 5.3
.. note::
This type is an extension to the DB API definition.
.. data:: NCHAR
This type object is used to describe national character strings (NVARCHAR2)
in Oracle.
.. note::
This type is an extension to the DB API definition.
.. data:: NCLOB
This type object is used to describe columns in a database that are NCLOBs.
.. note::
This type is an extension to the DB API definition.
.. data:: NUMBER .. data:: NUMBER
This type object is used to describe columns in a database that are This type object is used to describe columns in a database that are
numbers. numbers. The database types :data:`DB_TYPE_BINARY_DOUBLE`,
:data:`DB_TYPE_BINARY_FLOAT`, :data:`DB_TYPE_BINARY_INTEGER` and
:data:`DB_TYPE_NUMBER` will all compare equal to this value. If a variable
.. data:: OBJECT is created with this type, the database type :data:`DB_TYPE_NUMBER` will be
used.
This type object is used to describe columns in a database that are
objects.
.. note::
This type is an extension to the DB API definition.
.. data:: ROWID .. data:: ROWID
This type object is used to describe the pseudo column "rowid". This type object is used to describe the pseudo column "rowid". The
database type :data:`DB_TYPE_ROWID` will compare equal to this value. If a
variable is created with this type, the database type
:data:`DB_TYPE_VARCHAR` will be used.
.. data:: STRING .. data:: STRING
This type object is used to describe columns in a database that are strings This type object is used to describe columns in a database that are
(in Oracle this is VARCHAR2 columns). strings. The database types :data:`DB_TYPE_CHAR`, :data:`DB_TYPE_CLOB`,
:data:`DB_TYPE_LONG`, :data:`DB_TYPE_NCHAR`, :data:`DB_TYPE_NCLOB`,
:data:`DB_TYPE_NVARCHAR` and :data:`DB_TYPE_VARCHAR` will all compare equal
to this value. If a variable is created with this type, the database type
:data:`DB_TYPE_VARCHAR` will be used.
.. _dbtypes:
Database Types
--------------
All of these types are extensions to the DB API definition. They are found in
query and object metadata. They can also be used to specify the database type
when binding data.
.. data:: DB_TYPE_BFILE
Describes columns, attributes or array elements in a database that are of
type BFILE. It will compare equal to the DB API type :data:`BINARY`.
.. data:: DB_TYPE_BINARY_DOUBLE
Describes columns, attributes or array elements in a database that are of
type BINARY_DOUBLE. It will compare equal to the DB API type
:data:`NUMBER`.
.. data:: DB_TYPE_BINARY_FLOAT
Describes columns, attributes or array elements in a database that are
of type BINARY_FLOAT. It will compare equal to the DB API type
:data:`NUMBER`.
.. data:: DB_TYPE_BINARY_INTEGER
Describes attributes or array elements in a database that are of type
BINARY_INTEGER. It will compare equal to the DB API type :data:`NUMBER`.
.. data:: DB_TYPE_BLOB
Describes columns, attributes or array elements in a database that are of
type BLOB. It will compare equal to the DB API type :data:`BINARY`.
.. data:: DB_TYPE_BOOLEAN
Describes attributes or array elements in a database that are of type
BOOLEAN. It is only available in Oracle 12.1 and higher and only within
PL/SQL.
.. data:: DB_TYPE_CHAR
Describes columns, attributes or array elements in a database that are of
type CHAR. It will compare equal to the DB API type :data:`STRING`.
Note that these are fixed length string values and behave differently from
VARCHAR2.
.. data:: DB_TYPE_CLOB
Describes columns, attributes or array elements in a database that are of
type CLOB. It will compare equal to the DB API type :data:`STRING`.
.. data:: DB_TYPE_CURSOR
Describes columns in a database that are of type CURSOR. In PL/SQL these
are knoown as REF CURSOR.
.. data:: DB_TYPE_DATE
Describes columns, attributes or array elements in a database that are of
type DATE. It will compare equal to the DB API type :data:`DATETIME`.
.. data:: DB_TYPE_INTERVAL_DS
Describes columns, attributes or array elements in a database that are of
type INTERVAL DAY TO SECOND.
.. data:: DB_TYPE_INTERVAL_YM
Describes columns, attributes or array elements in a database that are of
type INTERVAL YEAR TO MONTH. This database type is not currently supported
by cx_Oracle.
.. data:: DB_TYPE_LONG
Describes columns, attributes or array elements in a database that are of
type LONG. It will compare equal to the DB API type :data:`STRING`.
.. data:: DB_TYPE_LONG_RAW
Describes columns, attributes or array elements in a database that are of
type LONG RAW. It will compare equal to the DB API type :data:`BINARY`.
.. data:: DB_TYPE_NCHAR
Describes columns, attributes or array elements in a database that are of
type NCHAR. It will compare equal to the DB API type :data:`STRING`.
Note that these are fixed length string values and behave differently from
NVARCHAR2.
.. data:: DB_TYPE_NCLOB
Describes columns, attributes or array elements in a database that are of
type NCLOB. It will compare equal to the DB API type :data:`STRING`.
.. data:: DB_TYPE_NUMBER
Describes columns, attributes or array elements in a database that are of
type NUMBER. It will compare equal to the DB API type :data:`NUMBER`.
.. data:: DB_TYPE_NVARCHAR
Describes columns, attributes or array elements in a database that are of
type NVARCHAR2. It will compare equal to the DB API type :data:`STRING`.
.. data:: DB_TYPE_OBJECT
Describes columns, attributes or array elements in a database that are an
instance of a named SQL or PL/SQL type.
.. data:: DB_TYPE_RAW
Describes columns, attributes or array elements in a database that are of
type RAW. It will compare equal to the DB API type :data:`BINARY`.
.. data:: DB_TYPE_ROWID
Describes columns, attributes or array elements in a database that are of
type ROWID or UROWID. It will compare equal to the DB API type
:data:`ROWID`.
.. data:: DB_TYPE_TIMESTAMP
Describes columns, attributes or array elements in a database that are of
type TIMESTAMP. It will compare equal to the DB API type :data:`DATETIME`.
.. data:: DB_TYPE_TIMESTAMP_LTZ
Describes columns, attributes or array elements in a database that are of
type TIMESTAMP WITH LOCAL TIME ZONE. It will compare equal to the DB API
type :data:`DATETIME`.
.. data:: DB_TYPE_TIMESTAMP_TZ
Describes columns, attributes or array elements in a database that are of
type TIMESTAMP WITH TIME ZONE. It will compare equal to the DB API type
:data:`DATETIME`.
.. data:: DB_TYPE_VARCHAR
Describes columns, attributes or array elements in a database that are of
type VARCHAR2. It will compare equal to the DB API type :data:`STRING`.
.. _dbtypesynonyms:
Database Type Synonyms
----------------------
All of the following constants are deprecated and will be removed in a future
version of cx_Oracle.
.. data:: BFILE
A synonym for :data:`DB_TYPE_BFILE`.
.. deprecated:: 8.0
.. data:: BLOB
A synonym for :data:`DB_TYPE_BLOB`.
.. deprecated:: 8.0
.. data:: BOOLEAN
A synonym for :data:`DB_TYPE_BOOLEAN`.
.. deprecated:: 8.0
.. data:: CLOB
A synonym for :data:`DB_TYPE_CLOB`.
.. deprecated:: 8.0
.. data:: CURSOR
A synonym for :data:`DB_TYPE_CURSOR`.
.. deprecated:: 8.0
.. data:: FIXED_CHAR
A synonym for :data:`DB_TYPE_CHAR`.
.. deprecated:: 8.0
.. data:: FIXED_NCHAR
A synonym for :data:`DB_TYPE_NCHAR`.
.. deprecated:: 8.0
.. data:: INTERVAL
A synonym for :data:`DB_TYPE_INTERVAL_DS`.
.. deprecated:: 8.0
.. data:: LONG_BINARY
A synonym for :data:`DB_TYPE_LONG_RAW`.
.. deprecated:: 8.0
.. data:: LONG_STRING
A synonym for :data:`DB_TYPE_LONG`.
.. deprecated:: 8.0
.. data:: NATIVE_FLOAT
A synonym for :data:`DB_TYPE_BINARY_DOUBLE`.
.. deprecated:: 8.0
.. data:: NATIVE_INT
A synonym for :data:`DB_TYPE_BINARY_INTEGER`.
.. deprecated:: 8.0
.. data:: NCHAR
A synonym for :data:`DB_TYPE_NVARCHAR`.
.. deprecated:: 8.0
.. data:: NCLOB
A synonym for :data:`DB_TYPE_NCLOB`.
.. deprecated:: 8.0
.. data:: OBJECT
A synonym for :data:`DB_TYPE_OBJECT`.
.. deprecated:: 8.0
.. data:: TIMESTAMP .. data:: TIMESTAMP
This type object is used to describe columns in a database that are A synonym for :data:`DB_TYPE_TIMESTAMP`.
timestamps.
.. note:: .. deprecated:: 8.0
This attribute is an extension to the DB API definition.
Other Types
-----------
All of these types are extensions to the DB API definition.
.. data:: ApiType
This type object is the Python type of the database API type constants
:data:`BINARY`, :data:`DATETIME`, :data:`NUMBER`, :data:`ROWID` and
:data:`STRING`.
.. data:: DbType
This type object is the Python type of the
:ref:`database type constants <dbtypes>`.
.. data:: LOB
This type object is the Python type of :data:`DB_TYPE_BLOB`,
:data:`DB_TYPE_BFILE`, :data:`DB_TYPE_CLOB` and :data:`DB_TYPE_NCLOB` data
that is returned from cursors.
.. _exceptions: .. _exceptions:

View File

@ -19,8 +19,8 @@ Object Type Objects
.. attribute:: ObjectType.attributes .. attribute:: ObjectType.attributes
This read-only attribute returns a list of the attributes that make up the This read-only attribute returns a list of the :ref:`attributes
object type. Each attribute has a name attribute on it. <objectattr>` that make up the object type.
.. attribute:: ObjectType.iscollection .. attribute:: ObjectType.iscollection
@ -34,6 +34,17 @@ Object Type Objects
This read-only attribute returns the name of the type. This read-only attribute returns the name of the type.
.. attribute:: ObjectType.element_type
This read-only attribute returns the type of elements found in collections
of this type, if :attr:`~ObjectType.iscollection` is ``True``; otherwise,
it returns ``None``. If the collection contains objects, this will be
another object type; otherwise, it will be one of the
:ref:`database type constants <dbtypes>`.
.. versionadded:: 8.0
.. method:: ObjectType.newobject([sequence]) .. method:: ObjectType.newobject([sequence])
Return a new Oracle object of the given type. This object can then be Return a new Oracle object of the given type. This object can then be
@ -151,3 +162,29 @@ Object Objects
.. method:: Object.trim(num) .. method:: Object.trim(num)
Remove the specified number of elements from the end of the collection. Remove the specified number of elements from the end of the collection.
.. _objectattr:
Object Attribute Objects
------------------------
.. note::
This object is an extension to the DB API. The elements of
:attr:`ObjectType.attributes` are instances of this type.
.. attribute:: ObjectAttribute.name
This read-only attribute returns the name of the attribute.
.. attribute:: ObjectAttribute.type
This read-only attribute returns the type of the attribute. This will be an
:ref:`Oracle Object Type <objecttype>` if the variable binds
Oracle objects; otherwise, it will be one of the
:ref:`database type constants <dbtypes>`.
.. versionadded:: 8.0

View File

@ -70,9 +70,14 @@ Variable Objects
.. attribute:: Variable.type .. attribute:: Variable.type
This read-only attribute returns the type of the variable for those This read-only attribute returns the type of the variable. This will be an
variables that bind Oracle objects (it is not present for any other type of :ref:`Oracle Object Type <objecttype>` if the variable binds
variable). Oracle objects; otherwise, it will be one of the
:ref:`database type constants <dbtypes>`.
.. versionchanged:: 8.0
Database type constants are now used when the variable is not used for
binding Oracle objects.
.. attribute:: Variable.values .. attribute:: Variable.values

View File

@ -4,8 +4,8 @@ Welcome to cx_Oracle's documentation!
**cx_Oracle** is a module that enables access to Oracle Database and conforms **cx_Oracle** is a module that enables access to Oracle Database and conforms
to the Python database API specification. This module is currently tested to the Python database API specification. This module is currently tested
against Oracle Client 19c, 18c, 12c, and 11.2, and Python 2.7, 3.5, 3.6, 3.7 against Oracle Client 19c, 18c, 12c, and 11.2, and Python 3.5, 3.6, 3.7 and
and 3.8. 3.8.
**cx_Oracle** is distributed under an open-source :ref:`license <license>` **cx_Oracle** is distributed under an open-source :ref:`license <license>`
(the BSD license). A detailed description of cx_Oracle changes can be found in (the BSD license). A detailed description of cx_Oracle changes can be found in

View File

@ -9,9 +9,48 @@ Version 8.0 (TBD)
----------------- -----------------
#) Dropped support for Python 2.7. #) Dropped support for Python 2.7.
#) Updated embedded ODPI-C to `version 3.4
<https://oracle.github.io/odpi/doc/releasenotes.html#version-3-4-tbd>`__.
#) Reworked type management to clarify and simplify code
- Added :ref:`constants <dbtypes>` for all database types. The database
types :data:`cx_Oracle.DB_TYPE_BINARY_FLOAT`,
:data:`cx_Oracle.DB_TYPE_INTERVAL_YM`,
:data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` and
:data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` are completely new. The other
types were found in earlier releases under a different name. These types
will be found in :data:`Cursor.description` and passed as the defaultType
parameter to the :data:`Connection.outputtypehandler` and
:data:`Cursor.outputtypehandler` functions.
- Added :ref:`synonyms <dbtypesynonyms>` from the old type names to the new
type names for backwards compatibility. They are deprecated and will be
removed in a future version of cx_Oracle.
- The DB API :ref:`constants <types>` are now a specialized constant that
matches to the corresponding database types, as recommended by the DB
API.
- The variable attribute :data:`~Variable.type` now refers to one of the
new database type constants if the variable does not contain objects
(previously it was None in that case).
- The attribute :data:`~LOB.type` was added to LOB values.
- The attribute :data:`~ObjectAttribute.type` was added to attributes of
object types.
- The attribute :data:`~ObjectType.element_type` was added to object types.
- :ref:`Object types <objecttype>` now compare equal if they were created
by the same connection or session pool and their schemas and names match.
- All variables are now instances of the same class (previously each type
was an instance of a separate variable type). The attribute
:data:`~Variable.type` can be examined to determine the database type it
is associated with.
- The string representation of variables has changed to include the type
in addition to the value.
#) Added support for starting up a database using a parameter file (PFILE), #) Added support for starting up a database using a parameter file (PFILE),
as requested as requested
(`issue 295 <https://github.com/oracle/python-cx_Oracle/issues/295>`__). (`issue 295 <https://github.com/oracle/python-cx_Oracle/issues/295>`__).
#) Fixed overflow issue when calling :meth:`Cursor.getbatcherrors()` with
row offsets exceeding 65536.
#) Eliminated spurious error when accessing :attr:`Cursor.lastrowid` after
executing an INSERT ALL statement.
Version 7.3 (December 2019) Version 7.3 (December 2019)

View File

@ -214,9 +214,10 @@ LOB Bind Variables
================== ==================
Database CLOBs, NCLOBS, BLOBs and BFILEs can be bound with types Database CLOBs, NCLOBS, BLOBs and BFILEs can be bound with types
:attr:`cx_Oracle.CLOB`, :attr:`cx_Oracle.NCLOB`, :attr:`cx_Oracle.BLOB` :attr:`cx_Oracle.DB_TYPE_CLOB`, :attr:`cx_Oracle.DB_TYPE_NCLOB`,
and :attr:`cx_Oracle.BFILE` respectively. LOBs fetched from the database or :attr:`cx_Oracle.DB_TYPE_BLOB` and :attr:`cx_Oracle.DB_TYPE_BFILE`
created with :meth:`Connection.createlob()` can also be bound. respectively. LOBs fetched from the database or created with
:meth:`Connection.createlob()` can also be bound.
LOBs may represent Oracle Database persistent LOBs (those stored in tables) or LOBs may represent Oracle Database persistent LOBs (those stored in tables) or
temporary LOBs (such as those created with :meth:`Connection.createlob()` or temporary LOBs (such as those created with :meth:`Connection.createlob()` or
@ -268,12 +269,12 @@ employees with the last name 'Smith' so the result is::
(159, 'Lindsey', 'Smith') (159, 'Lindsey', 'Smith')
(171, 'William', 'Smith') (171, 'William', 'Smith')
To return a REF CURSOR from a PL/SQL function, use ``cx_Oracle.CURSOR`` for the To return a REF CURSOR from a PL/SQL function, use ``cx_Oracle.DB_TYPE_CURSOR`` for the
return type of :meth:`Cursor.callfunc()`: return type of :meth:`Cursor.callfunc()`:
.. code-block:: python .. code-block:: python
refCursor = cursor.callfunc('example_package.f_get_cursor', cx_Oracle.CURSOR) refCursor = cursor.callfunc('example_package.f_get_cursor', cx_Oracle.DB_TYPE_CURSOR)
for row in refCursor: for row in refCursor:
print(row) print(row)
@ -659,7 +660,7 @@ objects seamlessly:
def InputTypeHandler(cursor, value, numElements): def InputTypeHandler(cursor, value, numElements):
if isinstance(value, Building): if isinstance(value, Building):
return cursor.var(cx_Oracle.OBJECT, arraysize = numElements, return cursor.var(cx_Oracle.DB_TYPE_OBJECT, arraysize = numElements,
inconverter = BuildingInConverter, typename = objType.name) inconverter = BuildingInConverter, typename = objType.name)

View File

@ -637,6 +637,25 @@ Upgrading from Older Versions
Review the :ref:`release notes <releasenotes>` for deprecations and modify any Review the :ref:`release notes <releasenotes>` for deprecations and modify any
affected code. affected code.
If you are upgrading from cx_Oracle 7 note these changes:
- Any uses of ``type(var)`` need to be changed to ``var.type``.
- Any uses of ``var.type is not None`` need to be changed to
``isinstance(var.type, cx_Oracle.ObjectType)``
- Note that ``TIMESTAMP WITH TIME ZONE`` columns will now be reported as
:data:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ` instead of
:data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`.
- Note that ``TIMESTAMP WITH LOCAL TIME ZONE`` columns will now be reported
as :data:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ` instead of
:data:`cx_Oracle.TIMESTAMP` in :data:`Cursor.description`.
- Note that ``BINARY_FLOAT`` columns will now be reported as
:data:`cx_Oracle.DB_TYPE_BINARY_FLOAT` instead of
:data:`cx_Oracle.NATIVE_DOUBLE` in :data:`Cursor.description`.
If you are upgrading from cx_Oracle 5 note these installation changes: If you are upgrading from cx_Oracle 5 note these installation changes:
- When using Oracle Instant Client, you should not set ``ORACLE_HOME``. - When using Oracle Instant Client, you should not set ``ORACLE_HOME``.
@ -684,9 +703,8 @@ If installation fails:
or venv? or venv?
- Do you get the error "``No module named pip``"? The pip module is builtin - Do you get the error "``No module named pip``"? The pip module is builtin
to Python from version 2.7.9 but is sometimes removed by the OS. Use the to Python but is sometimes removed by the OS. Use the venv module
venv module (builtin to Python 3.x) or virtualenv module (Python 2.x) (builtin to Python 3.x) or virtualenv module instead.
instead.
- Do you get the error "``fatal error: dpi.h: No such file or directory``" - Do you get the error "``fatal error: dpi.h: No such file or directory``"
when building from source code? Ensure that your source installation has when building from source code? Ensure that your source installation has
@ -723,6 +741,5 @@ If using cx_Oracle fails:
supported on Windows 7. Similar steps shown above for ``DPI-1047`` may supported on Windows 7. Similar steps shown above for ``DPI-1047`` may
help. help.
- If you have both Python 2 and 3 installed, make sure you are - If you have multiple versions of Python installed, make sure you are
using the correct python and pip (or python3 and pip3) using the correct python and pip (or python3 and pip3) executables.
executables.

View File

@ -47,7 +47,7 @@ Features
The cx_Oracle feature highlights are: The cx_Oracle feature highlights are:
* Easily installed from PyPI * Easily installed from PyPI
* Support for Python 2 and 3, and for multiple Oracle Database versions * Support for multiple Oracle Client and Database versions
* Execution of SQL and PL/SQL statements * Execution of SQL and PL/SQL statements
* Extensive Oracle data type support, including large objects (CLOB and * Extensive Oracle data type support, including large objects (CLOB and
BLOB) and binding of SQL objects BLOB) and binding of SQL objects

View File

@ -11,15 +11,16 @@ the size of the tablespace storing it.
There are four types of LOB (large object): There are four types of LOB (large object):
* BLOB - Binary Large Object, used for storing binary data. cx_Oracle uses * BLOB - Binary Large Object, used for storing binary data. cx_Oracle uses
the type :attr:`cx_Oracle.BLOB`. the type :attr:`cx_Oracle.DB_TYPE_BLOB`.
* CLOB - Character Large Object, used for string strings in the database * CLOB - Character Large Object, used for string strings in the database
character set format. cx_Oracle uses the type :attr:`cx_Oracle.CLOB`. character set format. cx_Oracle uses the type
:attr:`cx_Oracle.DB_TYPE_CLOB`.
* NCLOB - National Character Large Object, used for string strings in the * NCLOB - National Character Large Object, used for string strings in the
national character set format. cx_Oracle uses the type national character set format. cx_Oracle uses the type
:attr:`cx_Oracle.NCLOB`. :attr:`cx_Oracle.DB_TYPE_NCLOB`.
* BFILE - External Binary File, used for referencing a file stored on the * BFILE - External Binary File, used for referencing a file stored on the
host operating system outside of the database. cx_Oracle uses the type host operating system outside of the database. cx_Oracle uses the type
:attr:`cx_Oracle.BFILE`. :attr:`cx_Oracle.DB_TYPE_BFILE`.
LOBs can be streamed to, and from, Oracle Database. LOBs can be streamed to, and from, Oracle Database.
@ -76,10 +77,10 @@ to be used as shown in this example:
.. code-block:: python .. code-block:: python
def OutputTypeHandler(cursor, name, defaultType, size, precision, scale): def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
if defaultType == cx_Oracle.CLOB: if defaultType == cx_Oracle.DB_TYPE_CLOB:
return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize) return cursor.var(cx_Oracle.DB_TYPE_LONG, arraysize=cursor.arraysize)
if defaultType == cx_Oracle.BLOB: if defaultType == cx_Oracle.DB_TYPE_BLOB:
return cursor.var(cx_Oracle.LONG_BINARY, arraysize=cursor.arraysize) return cursor.var(cx_Oracle.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
idVal = 1 idVal = 1
textData = "The quick brown fox jumps over the lazy dog" textData = "The quick brown fox jumps over the lazy dog"
@ -161,7 +162,7 @@ in the following code:
.. code-block:: python .. code-block:: python
idVal = 9 idVal = 9
lobVar = cursor.var(cx_Oracle.BLOB) lobVar = cursor.var(cx_Oracle.DB_TYPE_BLOB)
cursor.execute(""" cursor.execute("""
insert into lob_tbl (id, b) insert into lob_tbl (id, b)
values (:1, empty_blob()) values (:1, empty_blob())

View File

@ -225,8 +225,8 @@ can be obtained using :attr:`Cursor.description`:
This could result in metadata like:: This could result in metadata like::
('ID', <class 'cx_Oracle.NUMBER'>, 39, None, 38, 0, 0) ('ID', <class 'cx_Oracle.DB_TYPE_NUMBER'>, 39, None, 38, 0, 0)
('NAME', <class 'cx_Oracle.STRING'>, 20, 20, None, None, 1) ('NAME', <class 'cx_Oracle.DB_TYPE_VARCHAR'>, 20, 20, None, None, 1)
.. _defaultfetchtypes: .. _defaultfetchtypes:
@ -236,8 +236,8 @@ Fetch Data Types
The following table provides a list of all of the data types that cx_Oracle The following table provides a list of all of the data types that cx_Oracle
knows how to fetch. The middle column gives the type that is returned in the knows how to fetch. The middle column gives the type that is returned in the
:ref:`query metadata <querymetadata>`. The last column gives the type of Python :ref:`query metadata <querymetadata>`. The last column gives the type of
object that is returned by default. Python types can be changed with Python object that is returned by default. Python types can be changed with
:ref:`Output Type Handlers <outputtypehandlers>`. :ref:`Output Type Handlers <outputtypehandlers>`.
.. list-table:: .. list-table::
@ -246,91 +246,87 @@ object that is returned by default. Python types can be changed with
:align: left :align: left
* - Oracle Database Type * - Oracle Database Type
- cx_Oracle Type - cx_Oracle Database Type
- Default Python type - Default Python type
* - BFILE * - BFILE
- :attr:`cx_Oracle.BFILE` - :attr:`cx_Oracle.DB_TYPE_BFILE`
- :ref:`cx_Oracle.LOB <lobobj>` - :ref:`cx_Oracle.LOB <lobobj>`
* - BINARY_DOUBLE * - BINARY_DOUBLE
- :attr:`cx_Oracle.NATIVE_FLOAT` - :attr:`cx_Oracle.DB_TYPE_BINARY_DOUBLE`
- float - float
* - BINARY_FLOAT * - BINARY_FLOAT
- :attr:`cx_Oracle.NATIVE_FLOAT` - :attr:`cx_Oracle.DB_TYPE_BINARY_FLOAT`
- float - float
* - BLOB * - BLOB
- :attr:`cx_Oracle.BLOB` - :attr:`cx_Oracle.DB_TYPE_BLOB`
- :ref:`cx_Oracle.LOB <lobobj>` - :ref:`cx_Oracle.LOB <lobobj>`
* - CHAR * - CHAR
- :attr:`cx_Oracle.FIXED_CHAR` - :attr:`cx_Oracle.DB_TYPE_CHAR`
- str - str
* - CLOB * - CLOB
- :attr:`cx_Oracle.CLOB` - :attr:`cx_Oracle.DB_TYPE_CLOB`
- :ref:`cx_Oracle.LOB <lobobj>` - :ref:`cx_Oracle.LOB <lobobj>`
* - CURSOR * - CURSOR
- :attr:`cx_Oracle.CURSOR` - :attr:`cx_Oracle.DB_TYPE_CURSOR`
- :ref:`cx_Oracle.Cursor <cursorobj>` - :ref:`cx_Oracle.Cursor <cursorobj>`
* - DATE * - DATE
- :attr:`cx_Oracle.DATETIME` - :attr:`cx_Oracle.DB_TYPE_DATE`
- datetime.datetime - datetime.datetime
* - INTERVAL DAY TO SECOND * - INTERVAL DAY TO SECOND
- :attr:`cx_Oracle.INTERVAL` - :attr:`cx_Oracle.DB_TYPE_INTERVAL_DS`
- datetime.timedelta - datetime.timedelta
* - LONG * - LONG
- :attr:`cx_Oracle.LONG_STRING` - :attr:`cx_Oracle.DB_TYPE_LONG`
- str - str
* - LONG RAW * - LONG RAW
- :attr:`cx_Oracle.LONG_BINARY` - :attr:`cx_Oracle.DB_TYPE_LONG_RAW`
- bytes [4]_ - bytes
* - NCHAR * - NCHAR
- :attr:`cx_Oracle.FIXED_NCHAR` - :attr:`cx_Oracle.DB_TYPE_NCHAR`
- str [1]_ - str
* - NCLOB * - NCLOB
- :attr:`cx_Oracle.NCLOB` - :attr:`cx_Oracle.DB_TYPE_NCLOB`
- :ref:`cx_Oracle.LOB <lobobj>` - :ref:`cx_Oracle.LOB <lobobj>`
* - NUMBER * - NUMBER
- :attr:`cx_Oracle.NUMBER` - :attr:`cx_Oracle.DB_TYPE_NUMBER`
- float or int [2]_ - float or int [1]_
* - NVARCHAR2 * - NVARCHAR2
- :attr:`cx_Oracle.NCHAR` - :attr:`cx_Oracle.DB_TYPE_NVARCHAR`
- str [1]_ - str
* - OBJECT [5]_ * - OBJECT [3]_
- :attr:`cx_Oracle.OBJECT` - :attr:`cx_Oracle.DB_TYPE_OBJECT`
- :ref:`cx_Oracle.Object <objecttype>` - :ref:`cx_Oracle.Object <objecttype>`
* - RAW * - RAW
- :attr:`cx_Oracle.BINARY` - :attr:`cx_Oracle.DB_TYPE_RAW`
- bytes [4]_ - bytes
* - ROWID * - ROWID
- :attr:`cx_Oracle.ROWID` - :attr:`cx_Oracle.DB_TYPE_ROWID`
- str - str
* - TIMESTAMP * - TIMESTAMP
- :attr:`cx_Oracle.TIMESTAMP` - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP`
- datetime.datetime - datetime.datetime
* - TIMESTAMP WITH LOCAL TIME ZONE * - TIMESTAMP WITH LOCAL TIME ZONE
- :attr:`cx_Oracle.TIMESTAMP` - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_LTZ`
- datetime.datetime [3]_ - datetime.datetime [2]_
* - TIMESTAMP WITH TIME ZONE * - TIMESTAMP WITH TIME ZONE
- :attr:`cx_Oracle.TIMESTAMP` - :attr:`cx_Oracle.DB_TYPE_TIMESTAMP_TZ`
- datetime.datetime [3]_ - datetime.datetime [2]_
* - UROWID * - UROWID
- :attr:`cx_Oracle.ROWID` - :attr:`cx_Oracle.DB_TYPE_ROWID`
- str - str
* - VARCHAR2 * - VARCHAR2
- :attr:`cx_Oracle.STRING` - :attr:`cx_Oracle.DB_TYPE_VARCHAR`
- str - str
.. [1] In Python 2 these are fetched as unicode objects. .. [1] If the precision and scale obtained from query column metadata indicate
.. [2] If the precision and scale obtained from query column metadata indicate
that the value can be expressed as an integer, the value will be that the value can be expressed as an integer, the value will be
returned as an int. If the column is unconstrained (no precision and returned as an int. If the column is unconstrained (no precision and
scale specified), the value will be returned as a float or an int scale specified), the value will be returned as a float or an int
depending on whether the value itself is an integer. In all other cases depending on whether the value itself is an integer. In all other cases
the value is returned as a float. Note that in Python 2, values returned the value is returned as a float.
as integers will be int or long depending on the size of the integer. .. [2] The timestamps returned are naive timestamps without any time zone
.. [3] The timestamps returned are naive timestamps without any time zone
information present. information present.
.. [4] In Python 2 these are identical to str objects since Python 2 doesn't .. [3] These include all user-defined types such as VARRAY, NESTED TABLE, etc.
have a native bytes object.
.. [5] These include all user-defined types such as VARRAY, NESTED TABLE, etc.
.. _outputtypehandlers: .. _outputtypehandlers:
@ -399,7 +395,7 @@ Using Python decimal objects, however, there is no loss of precision:
import decimal import decimal
def NumberToDecimal(cursor, name, defaultType, size, precision, scale): def NumberToDecimal(cursor, name, defaultType, size, precision, scale):
if defaultType == cx_Oracle.NUMBER: if defaultType == cx_Oracle.DB_TYPE_NUMBER:
return cursor.var(decimal.Decimal, arraysize=cursor.arraysize) return cursor.var(decimal.Decimal, arraysize=cursor.arraysize)
cur = connection.cursor() cur = connection.cursor()
@ -432,7 +428,7 @@ For example, to make queries return empty strings instead of NULLs:
return value return value
def OutputTypeHandler(cursor, name, defaultType, size, precision, scale): def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
if defaultType in (cx_Oracle.STRING, cx_Oracle.FIXED_CHAR): if defaultType in (cx_Oracle.DB_TYPE_VARCHAR, cx_Oracle.DB_TYPE_CHAR):
return cursor.var(str, size, cur.arraysize, outconverter=OutConverter) return cursor.var(str, size, cur.arraysize, outconverter=OutConverter)
connection.outputtypehandler = OutputTypeHandler connection.outputtypehandler = OutputTypeHandler

View File

@ -38,7 +38,7 @@ as shown:
.. code-block:: python .. code-block:: python
clob = connection.createlob(cx_Oracle.CLOB) clob = connection.createlob(cx_Oracle.DB_TYPE_CLOB)
clob.write(xmlData) clob.write(xmlData)
cursor.execute("insert into xml_table values (:id, sys.xmltype(:xml))", cursor.execute("insert into xml_table values (:id, sys.xmltype(:xml))",
id=2, xml=clob) id=2, xml=clob)

76
src/cxoApiType.c Normal file
View File

@ -0,0 +1,76 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// cxoApiType.c
// Defines the objects used for identifying types defined by the Python
// Database API.
//-----------------------------------------------------------------------------
#include "cxoModule.h"
//-----------------------------------------------------------------------------
// declaration of functions
//-----------------------------------------------------------------------------
static void cxoApiType_free(cxoApiType*);
static PyObject *cxoApiType_repr(cxoApiType*);
//-----------------------------------------------------------------------------
// declaration of members
//-----------------------------------------------------------------------------
static PyMemberDef cxoMembers[] = {
{ "name", T_STRING, offsetof(cxoApiType, name), READONLY },
{ NULL }
};
//-----------------------------------------------------------------------------
// Python type declaration
//-----------------------------------------------------------------------------
PyTypeObject cxoPyTypeApiType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "cx_Oracle.ApiType",
.tp_basicsize = sizeof(cxoApiType),
.tp_dealloc = (destructor) cxoApiType_free,
.tp_repr = (reprfunc) cxoApiType_repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_members = cxoMembers
};
//-----------------------------------------------------------------------------
// cxoApiType_free()
// Free the API type object.
//-----------------------------------------------------------------------------
static void cxoApiType_free(cxoApiType *apiType)
{
Py_CLEAR(apiType->dbTypes);
Py_TYPE(apiType)->tp_free((PyObject*) apiType);
}
//-----------------------------------------------------------------------------
// cxoApiType_repr()
// Return a string representation of a queue.
//-----------------------------------------------------------------------------
static PyObject *cxoApiType_repr(cxoApiType *apiType)
{
PyObject *module, *name, *apiTypeName, *result;
apiTypeName = PyUnicode_DecodeASCII(apiType->name, strlen(apiType->name),
NULL);
if (!apiTypeName)
return NULL;
if (cxoUtils_getModuleAndName(Py_TYPE(apiType), &module, &name) < 0) {
Py_DECREF(apiTypeName);
return NULL;
}
result = cxoUtils_formatString("<%s.%s %s>",
PyTuple_Pack(3, module, name, apiTypeName));
Py_DECREF(module);
Py_DECREF(name);
Py_DECREF(apiTypeName);
return result;
}

View File

@ -347,7 +347,7 @@ static int cxoConnectionParams_processShardingKeyValue(
dpiNativeTypeNum nativeTypeNum; dpiNativeTypeNum nativeTypeNum;
cxoTransformNum transformNum; cxoTransformNum transformNum;
transformNum = cxoTransform_getNumFromValue(value, 0); transformNum = cxoTransform_getNumFromPythonValue(value, 0);
if (cxoTransform_fromPython(transformNum, &nativeTypeNum, value, if (cxoTransform_fromPython(transformNum, &nativeTypeNum, value,
&column->value, buffer, params->encoding, params->nencoding, NULL, &column->value, buffer, params->encoding, params->nencoding, NULL,
0) < 0) 0) < 0)
@ -1011,7 +1011,7 @@ static PyObject *cxoConnection_getType(cxoConnection *conn, PyObject *nameObj)
static PyObject *cxoConnection_createLob(cxoConnection *conn, static PyObject *cxoConnection_createLob(cxoConnection *conn,
PyObject *lobType) PyObject *lobType)
{ {
dpiOracleTypeNum oracleTypeNum; cxoDbType *dbType;
dpiLob *handle; dpiLob *handle;
PyObject *lob; PyObject *lob;
@ -1020,23 +1020,20 @@ static PyObject *cxoConnection_createLob(cxoConnection *conn,
return NULL; return NULL;
// verify the LOB type // verify the LOB type
if (lobType == (PyObject*) &cxoPyTypeClobVar) if (lobType != (PyObject*) cxoDbTypeClob &&
oracleTypeNum = DPI_ORACLE_TYPE_CLOB; lobType != (PyObject*) cxoDbTypeBlob &&
else if (lobType == (PyObject*) &cxoPyTypeBlobVar) lobType != (PyObject*) cxoDbTypeNclob) {
oracleTypeNum = DPI_ORACLE_TYPE_BLOB;
else if (lobType == (PyObject*) &cxoPyTypeNclobVar)
oracleTypeNum = DPI_ORACLE_TYPE_NCLOB;
else {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"parameter should be one of cx_Oracle.CLOB, cx_Oracle.BLOB " "parameter should be one of cx_Oracle.DB_TYPE_CLOB, "
"or cx_Oracle.NCLOB"); "cx_Oracle.DB_TYPE_BLOB or cx_Oracle.DB_TYPE_NCLOB");
return NULL; return NULL;
} }
// create a temporary LOB // create a temporary LOB
if (dpiConn_newTempLob(conn->handle, oracleTypeNum, &handle) < 0) dbType = (cxoDbType*) lobType;
if (dpiConn_newTempLob(conn->handle, dbType->num, &handle) < 0)
return cxoError_raiseAndReturnNull(); return cxoError_raiseAndReturnNull();
lob = cxoLob_new(conn, oracleTypeNum, handle); lob = cxoLob_new(conn, dbType, handle);
if (!lob) if (!lob)
dpiLob_release(handle); dpiLob_release(handle);
return lob; return lob;

View File

@ -329,10 +329,12 @@ static int cxoCursor_fetchRow(cxoCursor *cursor, int *found,
static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns) static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
{ {
PyObject *outputTypeHandler, *result; PyObject *outputTypeHandler, *result;
cxoTransformNum transformNum;
cxoObjectType *objectType; cxoObjectType *objectType;
cxoVarType *varType;
dpiQueryInfo queryInfo; dpiQueryInfo queryInfo;
uint32_t pos, size; uint32_t pos, size;
cxoDbType *dbType;
char message[120];
cxoVar *var; cxoVar *var;
// initialize fetching variables; these are used to reduce the number of // initialize fetching variables; these are used to reduce the number of
@ -372,9 +374,17 @@ static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
return -1; return -1;
} }
// determine the default type // determine the default types to use
varType = cxoVarType_fromDataTypeInfo(&queryInfo.typeInfo); transformNum =
if (!varType) cxoTransform_getNumFromDataTypeInfo(&queryInfo.typeInfo);
if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message), "Oracle type %d not supported.",
queryInfo.typeInfo.oracleTypeNum);
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
return -1;
}
dbType = cxoDbType_fromTransformNum(transformNum);
if (!dbType)
return -1; return -1;
// see if an output type handler should be used // see if an output type handler should be used
@ -390,7 +400,7 @@ static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
if (outputTypeHandler) { if (outputTypeHandler) {
result = PyObject_CallFunction(outputTypeHandler, "Os#Oiii", result = PyObject_CallFunction(outputTypeHandler, "Os#Oiii",
cursor, queryInfo.name, (Py_ssize_t) queryInfo.nameLength, cursor, queryInfo.name, (Py_ssize_t) queryInfo.nameLength,
varType->pythonType, size, queryInfo.typeInfo.precision, dbType, size, queryInfo.typeInfo.precision,
queryInfo.typeInfo.scale); queryInfo.typeInfo.scale);
if (!result) { if (!result) {
Py_XDECREF(objectType); Py_XDECREF(objectType);
@ -418,8 +428,8 @@ static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
// if no variable created yet, use the database metadata // if no variable created yet, use the database metadata
if (!var) { if (!var) {
var = cxoVar_new(cursor, cursor->fetchArraySize, varType, size, 0, var = cxoVar_new(cursor, cursor->fetchArraySize, transformNum,
objectType); size, 0, objectType);
if (!var) { if (!var) {
Py_XDECREF(objectType); Py_XDECREF(objectType);
return -1; return -1;
@ -444,16 +454,16 @@ static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyObject *cxoCursor_itemDescription(cxoCursor *cursor, uint32_t pos) static PyObject *cxoCursor_itemDescription(cxoCursor *cursor, uint32_t pos)
{ {
cxoVarType *varType;
int displaySize, index; int displaySize, index;
dpiQueryInfo queryInfo; dpiQueryInfo queryInfo;
PyObject *tuple, *temp; PyObject *tuple, *temp;
cxoDbType *dbType;
// get information about the column position // get information about the column position
if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0) if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0)
return NULL; return NULL;
varType = cxoVarType_fromDataTypeInfo(&queryInfo.typeInfo); dbType = cxoDbType_fromDataTypeInfo(&queryInfo.typeInfo);
if (!varType) if (!dbType)
return NULL; return NULL;
// set display size based on data type // set display size based on data type
@ -496,8 +506,8 @@ static PyObject *cxoCursor_itemDescription(cxoCursor *cursor, uint32_t pos)
PyTuple_SET_ITEM(tuple, 0, PyUnicode_Decode(queryInfo.name, PyTuple_SET_ITEM(tuple, 0, PyUnicode_Decode(queryInfo.name,
queryInfo.nameLength, cursor->connection->encodingInfo.encoding, queryInfo.nameLength, cursor->connection->encodingInfo.encoding,
NULL)); NULL));
Py_INCREF(varType->pythonType); Py_INCREF(dbType);
PyTuple_SET_ITEM(tuple, 1, (PyObject*) varType->pythonType); PyTuple_SET_ITEM(tuple, 1, (PyObject*) dbType);
if (displaySize) if (displaySize)
PyTuple_SET_ITEM(tuple, 2, PyLong_FromLong(displaySize)); PyTuple_SET_ITEM(tuple, 2, PyLong_FromLong(displaySize));
else { else {
@ -673,7 +683,7 @@ static int cxoCursor_setBindVariableHelper(cxoCursor *cursor,
// can happen if all of the values in a previous invocation of // can happen if all of the values in a previous invocation of
// executemany() were None) and there is now a value; in this case, // executemany() were None) and there is now a value; in this case,
// discard the original variable and have a new one created // discard the original variable and have a new one created
if (origVar->type->transformNum == CXO_TRANSFORM_NONE && if (origVar->transformNum == CXO_TRANSFORM_NONE &&
value != Py_None) { value != Py_None) {
origVar = NULL; origVar = NULL;
varToSet = NULL; varToSet = NULL;
@ -682,8 +692,9 @@ static int cxoCursor_setBindVariableHelper(cxoCursor *cursor,
// variable this is only necessary for executemany() since // variable this is only necessary for executemany() since
// execute() always passes a value of 1 for the number of elements // execute() always passes a value of 1 for the number of elements
} else if (numElements > origVar->allocatedElements) { } else if (numElements > origVar->allocatedElements) {
*newVar = cxoVar_new(cursor, numElements, origVar->type, *newVar = cxoVar_new(cursor, numElements,
origVar->size, origVar->isArray, origVar->objectType); origVar->transformNum, origVar->size, origVar->isArray,
origVar->objectType);
if (!*newVar) if (!*newVar)
return -1; return -1;
varToSet = *newVar; varToSet = *newVar;
@ -1826,9 +1837,9 @@ static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
"inconverter", "outconverter", "typename", "encodingErrors", "inconverter", "outconverter", "typename", "encodingErrors",
NULL }; NULL };
PyObject *inConverter, *outConverter, *typeNameObj; PyObject *inConverter, *outConverter, *typeNameObj;
cxoTransformNum transformNum;
const char *encodingErrors; const char *encodingErrors;
cxoObjectType *objType; cxoObjectType *objType;
cxoVarType *varType;
int size, arraySize; int size, arraySize;
PyObject *type; PyObject *type;
cxoVar *var; cxoVar *var;
@ -1844,12 +1855,9 @@ static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
return NULL; return NULL;
// determine the type of variable // determine the type of variable
varType = cxoVarType_fromPythonType(type, &objType); if (cxoTransform_getNumFromType(type, &transformNum, &objType) < 0)
if (!varType)
return NULL; return NULL;
Py_XINCREF(objType); Py_XINCREF(objType);
if (size == 0)
size = varType->size;
if (typeNameObj && typeNameObj != Py_None && !objType) { if (typeNameObj && typeNameObj != Py_None && !objType) {
objType = cxoObjectType_newByName(cursor->connection, typeNameObj); objType = cxoObjectType_newByName(cursor->connection, typeNameObj);
if (!objType) if (!objType)
@ -1857,7 +1865,7 @@ static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
} }
// create the variable // create the variable
var = cxoVar_new(cursor, arraySize, varType, size, 0, objType); var = cxoVar_new(cursor, arraySize, transformNum, size, 0, objType);
Py_XDECREF(objType); Py_XDECREF(objType);
if (!var) if (!var)
return NULL; return NULL;
@ -1886,23 +1894,20 @@ static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyObject *cxoCursor_arrayVar(cxoCursor *cursor, PyObject *args) static PyObject *cxoCursor_arrayVar(cxoCursor *cursor, PyObject *args)
{ {
cxoTransformNum transformNum;
uint32_t size, numElements; uint32_t size, numElements;
PyObject *type, *value; PyObject *type, *value;
cxoObjectType *objType; cxoObjectType *objType;
cxoVarType *varType;
cxoVar *var; cxoVar *var;
// parse arguments // parse arguments
size = 0; size = 0;
if (!PyArg_ParseTuple(args, "O!O|i", &PyType_Type, &type, &value, &size)) if (!PyArg_ParseTuple(args, "OO|i", &type, &value, &size))
return NULL; return NULL;
// determine the type of variable // determine the transform to use for the variable
varType = cxoVarType_fromPythonType(type, &objType); if (cxoTransform_getNumFromType(type, &transformNum, &objType) < 0)
if (!varType)
return NULL; return NULL;
if (size == 0)
size = varType->size;
// determine the number of elements to create // determine the number of elements to create
if (PyList_Check(value)) if (PyList_Check(value))
@ -1918,7 +1923,7 @@ static PyObject *cxoCursor_arrayVar(cxoCursor *cursor, PyObject *args)
} }
// create the variable // create the variable
var = cxoVar_new(cursor, numElements, varType, size, 1, objType); var = cxoVar_new(cursor, numElements, transformNum, size, 1, objType);
if (!var) if (!var)
return NULL; return NULL;

263
src/cxoDbType.c Normal file
View File

@ -0,0 +1,263 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// cxoDbType.c
// Defines the objects used for identifying all types used by the database.
//-----------------------------------------------------------------------------
#include "cxoModule.h"
//-----------------------------------------------------------------------------
// declaration of functions
//-----------------------------------------------------------------------------
static void cxoDbType_free(cxoDbType*);
static PyObject *cxoDbType_repr(cxoDbType*);
static PyObject *cxoDbType_richCompare(cxoDbType*, PyObject*, int);
//-----------------------------------------------------------------------------
// declaration of members
//-----------------------------------------------------------------------------
static PyMemberDef cxoMembers[] = {
{ "name", T_STRING, offsetof(cxoDbType, name), READONLY },
{ NULL }
};
//-----------------------------------------------------------------------------
// Python type declaration
//-----------------------------------------------------------------------------
PyTypeObject cxoPyTypeDbType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "cx_Oracle.DbType",
.tp_basicsize = sizeof(cxoDbType),
.tp_dealloc = (destructor) cxoDbType_free,
.tp_repr = (reprfunc) cxoDbType_repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_members = cxoMembers,
.tp_richcompare = (richcmpfunc) cxoDbType_richCompare
};
//-----------------------------------------------------------------------------
// cxoDbType_free()
// Free the database type object.
//-----------------------------------------------------------------------------
static void cxoDbType_free(cxoDbType *dbType)
{
Py_TYPE(dbType)->tp_free((PyObject*) dbType);
}
//-----------------------------------------------------------------------------
// cxoDbType_repr()
// Return a string representation of a queue.
//-----------------------------------------------------------------------------
static PyObject *cxoDbType_repr(cxoDbType *dbType)
{
PyObject *module, *name, *dbTypeName, *result;
dbTypeName = PyUnicode_DecodeASCII(dbType->name, strlen(dbType->name),
NULL);
if (!dbTypeName)
return NULL;
if (cxoUtils_getModuleAndName(Py_TYPE(dbType), &module, &name) < 0) {
Py_DECREF(dbTypeName);
return NULL;
}
result = cxoUtils_formatString("<%s.%s %s>",
PyTuple_Pack(3, module, name, dbTypeName));
Py_DECREF(module);
Py_DECREF(name);
Py_DECREF(dbTypeName);
return result;
}
//-----------------------------------------------------------------------------
// cxoDbType_richCompare()
// Peforms a comparison between the database type and another Python object.
// Equality (and inequality) are used to match database API types with their
// associated database types.
//-----------------------------------------------------------------------------
static PyObject *cxoDbType_richCompare(cxoDbType* dbType, PyObject* obj,
int op)
{
cxoApiType *apiType;
int status, equal;
// only equality and inequality can be checked
if (op != Py_EQ && op != Py_NE) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
// check for exact object
equal = 0;
if (obj == (PyObject*) dbType) {
equal = 1;
// check for API type
} else {
status = PyObject_IsInstance(obj, (PyObject*) &cxoPyTypeApiType);
if (status < 0)
return NULL;
if (status == 1) {
apiType = (cxoApiType*) obj;
status = PySequence_Contains(apiType->dbTypes, (PyObject*) dbType);
if (status < 0)
return NULL;
equal = (status == 1) ? 1 : 0;
}
}
// determine return value
if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
}
//-----------------------------------------------------------------------------
// cxoDbType_fromDataTypeInfo()
// Return the database type given the data type info available from ODPI-C.
//-----------------------------------------------------------------------------
cxoDbType *cxoDbType_fromDataTypeInfo(dpiDataTypeInfo *info)
{
char message[120];
switch (info->oracleTypeNum) {
case DPI_ORACLE_TYPE_VARCHAR:
return cxoDbTypeVarchar;
case DPI_ORACLE_TYPE_NVARCHAR:
return cxoDbTypeNvarchar;
case DPI_ORACLE_TYPE_CHAR:
return cxoDbTypeChar;
case DPI_ORACLE_TYPE_NCHAR:
return cxoDbTypeNchar;
case DPI_ORACLE_TYPE_ROWID:
return cxoDbTypeRowid;
case DPI_ORACLE_TYPE_RAW:
return cxoDbTypeRaw;
case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
return cxoDbTypeBinaryDouble;
case DPI_ORACLE_TYPE_NATIVE_FLOAT:
return cxoDbTypeBinaryFloat;
case DPI_ORACLE_TYPE_NATIVE_INT:
return cxoDbTypeBinaryInteger;
case DPI_ORACLE_TYPE_NUMBER:
return cxoDbTypeNumber;
case DPI_ORACLE_TYPE_DATE:
return cxoDbTypeDate;
case DPI_ORACLE_TYPE_TIMESTAMP:
return cxoDbTypeTimestamp;
case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
return cxoDbTypeTimestampTZ;
case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
return cxoDbTypeTimestampLTZ;
case DPI_ORACLE_TYPE_INTERVAL_DS:
return cxoDbTypeIntervalDS;
case DPI_ORACLE_TYPE_INTERVAL_YM:
return cxoDbTypeIntervalYM;
case DPI_ORACLE_TYPE_CLOB:
return cxoDbTypeClob;
case DPI_ORACLE_TYPE_NCLOB:
return cxoDbTypeNclob;
case DPI_ORACLE_TYPE_BLOB:
return cxoDbTypeBlob;
case DPI_ORACLE_TYPE_BFILE:
return cxoDbTypeBfile;
case DPI_ORACLE_TYPE_STMT:
return cxoDbTypeCursor;
case DPI_ORACLE_TYPE_OBJECT:
return cxoDbTypeObject;
case DPI_ORACLE_TYPE_LONG_VARCHAR:
return cxoDbTypeLong;
case DPI_ORACLE_TYPE_LONG_RAW:
return cxoDbTypeLongRaw;
case DPI_ORACLE_TYPE_BOOLEAN:
return cxoDbTypeBoolean;
default:
break;
}
snprintf(message, sizeof(message), "Oracle type %d not supported.",
info->oracleTypeNum);
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
return NULL;
}
//-----------------------------------------------------------------------------
// cxoDbType_fromTransformNum()
// Return the database type given the transformation number.
//-----------------------------------------------------------------------------
cxoDbType *cxoDbType_fromTransformNum(cxoTransformNum transformNum)
{
char message[120];
switch (transformNum) {
case CXO_TRANSFORM_BINARY:
return cxoDbTypeRaw;
case CXO_TRANSFORM_BFILE:
return cxoDbTypeBfile;
case CXO_TRANSFORM_BLOB:
return cxoDbTypeBlob;
case CXO_TRANSFORM_BOOLEAN:
return cxoDbTypeBoolean;
case CXO_TRANSFORM_CLOB:
return cxoDbTypeClob;
case CXO_TRANSFORM_CURSOR:
return cxoDbTypeCursor;
case CXO_TRANSFORM_DATE:
case CXO_TRANSFORM_DATETIME:
return cxoDbTypeDate;
case CXO_TRANSFORM_DECIMAL:
case CXO_TRANSFORM_FLOAT:
case CXO_TRANSFORM_INT:
return cxoDbTypeNumber;
case CXO_TRANSFORM_FIXED_CHAR:
return cxoDbTypeChar;
case CXO_TRANSFORM_FIXED_NCHAR:
return cxoDbTypeNchar;
case CXO_TRANSFORM_LONG_BINARY:
return cxoDbTypeLongRaw;
case CXO_TRANSFORM_LONG_STRING:
return cxoDbTypeLong;
case CXO_TRANSFORM_NATIVE_DOUBLE:
return cxoDbTypeBinaryDouble;
case CXO_TRANSFORM_NATIVE_FLOAT:
return cxoDbTypeBinaryFloat;
case CXO_TRANSFORM_NATIVE_INT:
return cxoDbTypeBinaryInteger;
case CXO_TRANSFORM_NCLOB:
return cxoDbTypeNclob;
case CXO_TRANSFORM_NSTRING:
return cxoDbTypeNvarchar;
case CXO_TRANSFORM_OBJECT:
return cxoDbTypeObject;
case CXO_TRANSFORM_ROWID:
return cxoDbTypeRowid;
case CXO_TRANSFORM_NONE:
case CXO_TRANSFORM_STRING:
return cxoDbTypeVarchar;
case CXO_TRANSFORM_TIMEDELTA:
return cxoDbTypeIntervalDS;
case CXO_TRANSFORM_TIMESTAMP:
return cxoDbTypeTimestamp;
case CXO_TRANSFORM_TIMESTAMP_LTZ:
return cxoDbTypeTimestampLTZ;
case CXO_TRANSFORM_TIMESTAMP_TZ:
return cxoDbTypeTimestampTZ;
default:
break;
}
snprintf(message, sizeof(message), "transform %d not supported.",
transformNum);
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
return NULL;
}

View File

@ -34,7 +34,7 @@ static PyObject *cxoLob_reduce(cxoLob*);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// declaration of methods for Python type "LOB" // declaration of methods
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyMethodDef cxoLobMethods[] = { static PyMethodDef cxoLobMethods[] = {
{ "size", (PyCFunction) cxoLob_size, METH_NOARGS }, { "size", (PyCFunction) cxoLob_size, METH_NOARGS },
@ -53,6 +53,15 @@ static PyMethodDef cxoLobMethods[] = {
}; };
//-----------------------------------------------------------------------------
// declaration of members
//-----------------------------------------------------------------------------
static PyMemberDef cxoMembers[] = {
{ "type", T_OBJECT, offsetof(cxoLob, dbType), READONLY },
{ NULL }
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Python type declaration // Python type declaration
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -63,7 +72,8 @@ PyTypeObject cxoPyTypeLob = {
.tp_dealloc = (destructor) cxoLob_free, .tp_dealloc = (destructor) cxoLob_free,
.tp_str = (reprfunc) cxoLob_str, .tp_str = (reprfunc) cxoLob_str,
.tp_flags = Py_TPFLAGS_DEFAULT, .tp_flags = Py_TPFLAGS_DEFAULT,
.tp_methods = cxoLobMethods .tp_methods = cxoLobMethods,
.tp_members = cxoMembers
}; };
@ -71,7 +81,7 @@ PyTypeObject cxoPyTypeLob = {
// cxoLob_new() // cxoLob_new()
// Create a new LOB. // Create a new LOB.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum, PyObject *cxoLob_new(cxoConnection *connection, cxoDbType *dbType,
dpiLob *handle) dpiLob *handle)
{ {
cxoLob *lob; cxoLob *lob;
@ -80,9 +90,10 @@ PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
if (!lob) if (!lob)
return NULL; return NULL;
lob->handle = handle; lob->handle = handle;
lob->oracleTypeNum = oracleTypeNum;
Py_INCREF(connection); Py_INCREF(connection);
lob->connection = connection; lob->connection = connection;
Py_INCREF(dbType);
lob->dbType = dbType;
return (PyObject*) lob; return (PyObject*) lob;
} }
@ -97,6 +108,7 @@ static void cxoLob_free(cxoLob *lob)
dpiLob_release(lob->handle); dpiLob_release(lob->handle);
lob->handle = NULL; lob->handle = NULL;
} }
Py_CLEAR(lob->dbType);
Py_CLEAR(lob->connection); Py_CLEAR(lob->connection);
Py_TYPE(lob)->tp_free((PyObject*) lob); Py_TYPE(lob)->tp_free((PyObject*) lob);
} }
@ -141,13 +153,15 @@ static PyObject *cxoLob_internalRead(cxoLob *lob, uint64_t offset,
} }
// return the result // return the result
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_NCLOB) if (lob->dbType == cxoDbTypeNclob) {
result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize, result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize,
lob->connection->encodingInfo.nencoding, NULL); lob->connection->encodingInfo.nencoding, NULL);
else if (lob->oracleTypeNum == DPI_ORACLE_TYPE_CLOB) } else if (lob->dbType == cxoDbTypeClob) {
result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize, result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize,
lob->connection->encodingInfo.encoding, NULL); lob->connection->encodingInfo.encoding, NULL);
else result = PyBytes_FromStringAndSize(buffer, (Py_ssize_t) bufferSize); } else {
result = PyBytes_FromStringAndSize(buffer, (Py_ssize_t) bufferSize);
}
PyMem_Free(buffer); PyMem_Free(buffer);
return result; return result;
} }
@ -164,7 +178,7 @@ static int cxoLob_internalWrite(cxoLob *lob, PyObject *dataObj,
cxoBuffer buffer; cxoBuffer buffer;
int status; int status;
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_NCLOB) if (lob->dbType == cxoDbTypeNclob)
encoding = lob->connection->encodingInfo.nencoding; encoding = lob->connection->encodingInfo.nencoding;
else encoding = lob->connection->encodingInfo.encoding; else encoding = lob->connection->encodingInfo.encoding;
if (cxoBuffer_fromObject(&buffer, dataObj, encoding) < 0) if (cxoBuffer_fromObject(&buffer, dataObj, encoding) < 0)

View File

@ -21,6 +21,21 @@
if (PyModule_AddIntConstant(module, name, value) < 0) \ if (PyModule_AddIntConstant(module, name, value) < 0) \
return NULL; return NULL;
// define macro for adding Python Database API types
#define CXO_ADD_API_TYPE(name, transformNum, typeObj) \
if (cxoModule_addApiType(module, name, transformNum, typeObj) < 0) \
return NULL;
// define macro for adding database types
#define CXO_ADD_DB_TYPE(num, name, transformNum, typeObj) \
if (cxoModule_addDbType(module, num, name, transformNum, typeObj) < 0) \
return NULL;
// define macro for associating database types with Database API types
#define CXO_ASSOCIATE_DB_TYPE(apiType, dbType) \
if (PyList_Append(apiType->dbTypes, (PyObject*) dbType) < 0) \
return NULL;
// define macro for adding type objects // define macro for adding type objects
#define CXO_ADD_TYPE_OBJECT(name, type) \ #define CXO_ADD_TYPE_OBJECT(name, type) \
Py_INCREF(type); \ Py_INCREF(type); \
@ -48,10 +63,98 @@ PyObject *cxoProgrammingErrorException = NULL;
PyObject *cxoNotSupportedErrorException = NULL; PyObject *cxoNotSupportedErrorException = NULL;
PyObject *cxoJsonDumpFunction = NULL; PyObject *cxoJsonDumpFunction = NULL;
PyObject *cxoJsonLoadFunction = NULL; PyObject *cxoJsonLoadFunction = NULL;
cxoDbType *cxoDbTypeBfile = NULL;
cxoDbType *cxoDbTypeBinaryDouble = NULL;
cxoDbType *cxoDbTypeBinaryFloat = NULL;
cxoDbType *cxoDbTypeBinaryInteger = NULL;
cxoDbType *cxoDbTypeBlob = NULL;
cxoDbType *cxoDbTypeBoolean = NULL;
cxoDbType *cxoDbTypeChar = NULL;
cxoDbType *cxoDbTypeClob = NULL;
cxoDbType *cxoDbTypeCursor = NULL;
cxoDbType *cxoDbTypeDate = NULL;
cxoDbType *cxoDbTypeIntervalDS = NULL;
cxoDbType *cxoDbTypeIntervalYM = NULL;
cxoDbType *cxoDbTypeLong = NULL;
cxoDbType *cxoDbTypeLongRaw = NULL;
cxoDbType *cxoDbTypeNchar = NULL;
cxoDbType *cxoDbTypeNclob = NULL;
cxoDbType *cxoDbTypeNumber = NULL;
cxoDbType *cxoDbTypeNvarchar = NULL;
cxoDbType *cxoDbTypeObject = NULL;
cxoDbType *cxoDbTypeRaw = NULL;
cxoDbType *cxoDbTypeRowid = NULL;
cxoDbType *cxoDbTypeTimestamp = NULL;
cxoDbType *cxoDbTypeTimestampLTZ = NULL;
cxoDbType *cxoDbTypeTimestampTZ = NULL;
cxoDbType *cxoDbTypeVarchar = NULL;
cxoApiType *cxoApiTypeBinary = NULL;
cxoApiType *cxoApiTypeDatetime = NULL;
cxoApiType *cxoApiTypeNumber = NULL;
cxoApiType *cxoApiTypeRowid = NULL;
cxoApiType *cxoApiTypeString = NULL;
cxoFuture *cxoFutureObj = NULL; cxoFuture *cxoFutureObj = NULL;
dpiContext *cxoDpiContext = NULL; dpiContext *cxoDpiContext = NULL;
dpiVersionInfo cxoClientVersionInfo; dpiVersionInfo cxoClientVersionInfo;
//-----------------------------------------------------------------------------
// cxoModule_addApiType()
// Create a Python Database API type and add it to the module.
//-----------------------------------------------------------------------------
static int cxoModule_addApiType(PyObject *module, const char *name,
cxoTransformNum defaultTransformNum, cxoApiType **apiType)
{
cxoApiType *tempApiType;
tempApiType =
(cxoApiType*) cxoPyTypeApiType.tp_alloc(&cxoPyTypeApiType, 0);
if (!tempApiType)
return -1;
tempApiType->name = name;
tempApiType->defaultTransformNum = defaultTransformNum;
tempApiType->dbTypes = PyList_New(0);
if (!tempApiType->dbTypes) {
Py_DECREF(tempApiType);
return -1;
}
if (PyModule_AddObject(module, name, (PyObject*) tempApiType) < 0) {
Py_DECREF(tempApiType);
return -1;
}
*apiType = tempApiType;
return 0;
}
//-----------------------------------------------------------------------------
// cxoModule_addDbType()
// Create a database type and add it to the module.
//-----------------------------------------------------------------------------
static int cxoModule_addDbType(PyObject *module, uint32_t num,
const char *name, cxoTransformNum defaultTransformNum,
cxoDbType **dbType)
{
cxoDbType *tempDbType;
tempDbType = (cxoDbType*) cxoPyTypeDbType.tp_alloc(&cxoPyTypeDbType, 0);
if (!tempDbType)
return -1;
tempDbType->num = num;
tempDbType->name = name;
tempDbType->defaultTransformNum = defaultTransformNum;
if (PyModule_AddObject(module, name, (PyObject*) tempDbType) < 0) {
Py_DECREF(tempDbType);
return -1;
}
*dbType = tempDbType;
return 0;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoModule_setException() // cxoModule_setException()
// Create an exception and set it in the provided dictionary. // Create an exception and set it in the provided dictionary.
@ -242,50 +345,32 @@ static PyObject *cxoModule_initialize(void)
return NULL; return NULL;
// prepare the types for use by the module // prepare the types for use by the module
CXO_MAKE_TYPE_READY(&cxoPyTypeBfileVar); CXO_MAKE_TYPE_READY(&cxoPyTypeApiType);
CXO_MAKE_TYPE_READY(&cxoPyTypeBinaryVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeBlobVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeBooleanVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeClobVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeConnection); CXO_MAKE_TYPE_READY(&cxoPyTypeConnection);
CXO_MAKE_TYPE_READY(&cxoPyTypeCursor); CXO_MAKE_TYPE_READY(&cxoPyTypeCursor);
CXO_MAKE_TYPE_READY(&cxoPyTypeCursorVar); CXO_MAKE_TYPE_READY(&cxoPyTypeDbType);
CXO_MAKE_TYPE_READY(&cxoPyTypeDateTimeVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeDeqOptions); CXO_MAKE_TYPE_READY(&cxoPyTypeDeqOptions);
CXO_MAKE_TYPE_READY(&cxoPyTypeEnqOptions); CXO_MAKE_TYPE_READY(&cxoPyTypeEnqOptions);
CXO_MAKE_TYPE_READY(&cxoPyTypeError); CXO_MAKE_TYPE_READY(&cxoPyTypeError);
CXO_MAKE_TYPE_READY(&cxoPyTypeFixedCharVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeFixedNcharVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeFuture); CXO_MAKE_TYPE_READY(&cxoPyTypeFuture);
CXO_MAKE_TYPE_READY(&cxoPyTypeIntervalVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeLob); CXO_MAKE_TYPE_READY(&cxoPyTypeLob);
CXO_MAKE_TYPE_READY(&cxoPyTypeLongBinaryVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeLongStringVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeMsgProps); CXO_MAKE_TYPE_READY(&cxoPyTypeMsgProps);
CXO_MAKE_TYPE_READY(&cxoPyTypeMessage); CXO_MAKE_TYPE_READY(&cxoPyTypeMessage);
CXO_MAKE_TYPE_READY(&cxoPyTypeMessageQuery); CXO_MAKE_TYPE_READY(&cxoPyTypeMessageQuery);
CXO_MAKE_TYPE_READY(&cxoPyTypeMessageRow); CXO_MAKE_TYPE_READY(&cxoPyTypeMessageRow);
CXO_MAKE_TYPE_READY(&cxoPyTypeMessageTable); CXO_MAKE_TYPE_READY(&cxoPyTypeMessageTable);
CXO_MAKE_TYPE_READY(&cxoPyTypeNativeFloatVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeNativeIntVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeNcharVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeNclobVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeNumberVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeObjectAttr); CXO_MAKE_TYPE_READY(&cxoPyTypeObjectAttr);
CXO_MAKE_TYPE_READY(&cxoPyTypeObject); CXO_MAKE_TYPE_READY(&cxoPyTypeObject);
CXO_MAKE_TYPE_READY(&cxoPyTypeObjectType); CXO_MAKE_TYPE_READY(&cxoPyTypeObjectType);
CXO_MAKE_TYPE_READY(&cxoPyTypeObjectVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeQueue); CXO_MAKE_TYPE_READY(&cxoPyTypeQueue);
CXO_MAKE_TYPE_READY(&cxoPyTypeRowidVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeSessionPool); CXO_MAKE_TYPE_READY(&cxoPyTypeSessionPool);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaCollection); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaCollection);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDatabase); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDatabase);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDoc); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDoc);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDocCursor); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDocCursor);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaOperation); CXO_MAKE_TYPE_READY(&cxoPyTypeSodaOperation);
CXO_MAKE_TYPE_READY(&cxoPyTypeStringVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeSubscr); CXO_MAKE_TYPE_READY(&cxoPyTypeSubscr);
CXO_MAKE_TYPE_READY(&cxoPyTypeTimestampVar); CXO_MAKE_TYPE_READY(&cxoPyTypeVar);
// initialize module and retrieve the dictionary // initialize module and retrieve the dictionary
module = PyModule_Create(&cxoModuleDef); module = PyModule_Create(&cxoModuleDef);
@ -325,50 +410,129 @@ static PyObject *cxoModule_initialize(void)
return NULL; return NULL;
// set up the types that are available // set up the types that are available
CXO_ADD_TYPE_OBJECT("ApiType", &cxoPyTypeApiType)
CXO_ADD_TYPE_OBJECT("Binary", &PyBytes_Type) CXO_ADD_TYPE_OBJECT("Binary", &PyBytes_Type)
CXO_ADD_TYPE_OBJECT("Connection", &cxoPyTypeConnection) CXO_ADD_TYPE_OBJECT("Connection", &cxoPyTypeConnection)
CXO_ADD_TYPE_OBJECT("Cursor", &cxoPyTypeCursor) CXO_ADD_TYPE_OBJECT("Cursor", &cxoPyTypeCursor)
CXO_ADD_TYPE_OBJECT("Timestamp", cxoPyTypeDateTime)
CXO_ADD_TYPE_OBJECT("Date", cxoPyTypeDate) CXO_ADD_TYPE_OBJECT("Date", cxoPyTypeDate)
CXO_ADD_TYPE_OBJECT("SessionPool", &cxoPyTypeSessionPool) CXO_ADD_TYPE_OBJECT("DbType", &cxoPyTypeDbType)
CXO_ADD_TYPE_OBJECT("DeqOptions", &cxoPyTypeDeqOptions)
CXO_ADD_TYPE_OBJECT("EnqOptions", &cxoPyTypeEnqOptions)
CXO_ADD_TYPE_OBJECT("_Error", &cxoPyTypeError) CXO_ADD_TYPE_OBJECT("_Error", &cxoPyTypeError)
CXO_ADD_TYPE_OBJECT("LOB", &cxoPyTypeLob)
CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps)
CXO_ADD_TYPE_OBJECT("Object", &cxoPyTypeObject) CXO_ADD_TYPE_OBJECT("Object", &cxoPyTypeObject)
CXO_ADD_TYPE_OBJECT("ObjectType", &cxoPyTypeObjectType) CXO_ADD_TYPE_OBJECT("ObjectType", &cxoPyTypeObjectType)
CXO_ADD_TYPE_OBJECT("EnqOptions", &cxoPyTypeEnqOptions) CXO_ADD_TYPE_OBJECT("SessionPool", &cxoPyTypeSessionPool)
CXO_ADD_TYPE_OBJECT("DeqOptions", &cxoPyTypeDeqOptions)
CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps)
CXO_ADD_TYPE_OBJECT("SodaCollection", &cxoPyTypeSodaCollection) CXO_ADD_TYPE_OBJECT("SodaCollection", &cxoPyTypeSodaCollection)
CXO_ADD_TYPE_OBJECT("SodaDatabase", &cxoPyTypeSodaDatabase) CXO_ADD_TYPE_OBJECT("SodaDatabase", &cxoPyTypeSodaDatabase)
CXO_ADD_TYPE_OBJECT("SodaDoc", &cxoPyTypeSodaDoc) CXO_ADD_TYPE_OBJECT("SodaDoc", &cxoPyTypeSodaDoc)
CXO_ADD_TYPE_OBJECT("SodaDocCursor", &cxoPyTypeSodaDocCursor) CXO_ADD_TYPE_OBJECT("SodaDocCursor", &cxoPyTypeSodaDocCursor)
CXO_ADD_TYPE_OBJECT("SodaOperation", &cxoPyTypeSodaOperation) CXO_ADD_TYPE_OBJECT("SodaOperation", &cxoPyTypeSodaOperation)
CXO_ADD_TYPE_OBJECT("Timestamp", cxoPyTypeDateTime)
CXO_ADD_TYPE_OBJECT("Var", &cxoPyTypeVar)
// the name "connect" is required by the DB API // the name "connect" is required by the DB API
CXO_ADD_TYPE_OBJECT("connect", &cxoPyTypeConnection) CXO_ADD_TYPE_OBJECT("connect", &cxoPyTypeConnection)
// create the basic data types for setting input sizes // create the database types (preferred names)
CXO_ADD_TYPE_OBJECT("BINARY", &cxoPyTypeBinaryVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BFILE, "DB_TYPE_BFILE",
CXO_ADD_TYPE_OBJECT("BFILE", &cxoPyTypeBfileVar) CXO_TRANSFORM_BFILE, &cxoDbTypeBfile)
CXO_ADD_TYPE_OBJECT("BLOB", &cxoPyTypeBlobVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_DOUBLE, "DB_TYPE_BINARY_DOUBLE",
CXO_ADD_TYPE_OBJECT("CLOB", &cxoPyTypeClobVar) CXO_TRANSFORM_NATIVE_DOUBLE, &cxoDbTypeBinaryDouble)
CXO_ADD_TYPE_OBJECT("CURSOR", &cxoPyTypeCursorVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_FLOAT, "DB_TYPE_BINARY_FLOAT",
CXO_ADD_TYPE_OBJECT("OBJECT", &cxoPyTypeObjectVar) CXO_TRANSFORM_NATIVE_FLOAT, &cxoDbTypeBinaryFloat)
CXO_ADD_TYPE_OBJECT("DATETIME", &cxoPyTypeDateTimeVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_INT, "DB_TYPE_BINARY_INTEGER",
CXO_ADD_TYPE_OBJECT("FIXED_CHAR", &cxoPyTypeFixedCharVar) CXO_TRANSFORM_NATIVE_INT, &cxoDbTypeBinaryInteger)
CXO_ADD_TYPE_OBJECT("FIXED_NCHAR", &cxoPyTypeFixedNcharVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BLOB, "DB_TYPE_BLOB",
CXO_ADD_TYPE_OBJECT("NCHAR", &cxoPyTypeNcharVar) CXO_TRANSFORM_BLOB, &cxoDbTypeBlob)
CXO_ADD_TYPE_OBJECT("INTERVAL", &cxoPyTypeIntervalVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BOOLEAN, "DB_TYPE_BOOLEAN",
CXO_ADD_TYPE_OBJECT("LOB", &cxoPyTypeLob) CXO_TRANSFORM_BOOLEAN, &cxoDbTypeBoolean)
CXO_ADD_TYPE_OBJECT("LONG_BINARY", &cxoPyTypeLongBinaryVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_CHAR, "DB_TYPE_CHAR",
CXO_ADD_TYPE_OBJECT("LONG_STRING", &cxoPyTypeLongStringVar) CXO_TRANSFORM_FIXED_CHAR, &cxoDbTypeChar)
CXO_ADD_TYPE_OBJECT("NCLOB", &cxoPyTypeNclobVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_CLOB, "DB_TYPE_CLOB",
CXO_ADD_TYPE_OBJECT("NUMBER", &cxoPyTypeNumberVar) CXO_TRANSFORM_CLOB, &cxoDbTypeClob)
CXO_ADD_TYPE_OBJECT("ROWID", &cxoPyTypeRowidVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_STMT, "DB_TYPE_CURSOR",
CXO_ADD_TYPE_OBJECT("STRING", &cxoPyTypeStringVar) CXO_TRANSFORM_CURSOR, &cxoDbTypeCursor)
CXO_ADD_TYPE_OBJECT("TIMESTAMP", &cxoPyTypeTimestampVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_DATE, "DB_TYPE_DATE",
CXO_ADD_TYPE_OBJECT("NATIVE_INT", &cxoPyTypeNativeIntVar) CXO_TRANSFORM_DATETIME, &cxoDbTypeDate)
CXO_ADD_TYPE_OBJECT("NATIVE_FLOAT", &cxoPyTypeNativeFloatVar) CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_INTERVAL_DS, "DB_TYPE_INTERVAL_DS",
CXO_ADD_TYPE_OBJECT("BOOLEAN", &cxoPyTypeBooleanVar) CXO_TRANSFORM_TIMEDELTA, &cxoDbTypeIntervalDS)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_INTERVAL_YM, "DB_TYPE_INTERVAL_YM",
CXO_TRANSFORM_UNSUPPORTED, &cxoDbTypeIntervalYM)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_LONG_VARCHAR, "DB_TYPE_LONG",
CXO_TRANSFORM_LONG_STRING, &cxoDbTypeLong)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_LONG_RAW, "DB_TYPE_LONG_RAW",
CXO_TRANSFORM_LONG_BINARY, &cxoDbTypeLongRaw)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NCHAR, "DB_TYPE_NCHAR",
CXO_TRANSFORM_FIXED_NCHAR, &cxoDbTypeNchar)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NCLOB, "DB_TYPE_NCLOB",
CXO_TRANSFORM_NCLOB, &cxoDbTypeNclob)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NUMBER, "DB_TYPE_NUMBER",
CXO_TRANSFORM_FLOAT, &cxoDbTypeNumber)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NVARCHAR, "DB_TYPE_NVARCHAR",
CXO_TRANSFORM_NSTRING, &cxoDbTypeNvarchar)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_OBJECT, "DB_TYPE_OBJECT",
CXO_TRANSFORM_OBJECT, &cxoDbTypeObject)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_RAW, "DB_TYPE_RAW",
CXO_TRANSFORM_BINARY, &cxoDbTypeRaw)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_ROWID, "DB_TYPE_ROWID",
CXO_TRANSFORM_ROWID, &cxoDbTypeRowid)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP, "DB_TYPE_TIMESTAMP",
CXO_TRANSFORM_TIMESTAMP, &cxoDbTypeTimestamp)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP_LTZ, "DB_TYPE_TIMESTAMP_LTZ",
CXO_TRANSFORM_TIMESTAMP_LTZ, &cxoDbTypeTimestampLTZ)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_TIMESTAMP_TZ, "DB_TYPE_TIMESTAMP_TZ",
CXO_TRANSFORM_TIMESTAMP_TZ, &cxoDbTypeTimestampTZ)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_VARCHAR, "DB_TYPE_VARCHAR",
CXO_TRANSFORM_STRING, &cxoDbTypeVarchar)
// create the synonyms for database types (deprecated names)
CXO_ADD_TYPE_OBJECT("BFILE", cxoDbTypeBfile)
CXO_ADD_TYPE_OBJECT("BLOB", cxoDbTypeBlob)
CXO_ADD_TYPE_OBJECT("CLOB", cxoDbTypeClob)
CXO_ADD_TYPE_OBJECT("CURSOR", cxoDbTypeCursor)
CXO_ADD_TYPE_OBJECT("OBJECT", cxoDbTypeObject)
CXO_ADD_TYPE_OBJECT("FIXED_CHAR", cxoDbTypeChar)
CXO_ADD_TYPE_OBJECT("FIXED_NCHAR", cxoDbTypeNchar)
CXO_ADD_TYPE_OBJECT("NCHAR", cxoDbTypeNvarchar)
CXO_ADD_TYPE_OBJECT("INTERVAL", cxoDbTypeIntervalDS)
CXO_ADD_TYPE_OBJECT("LONG_BINARY", cxoDbTypeLongRaw)
CXO_ADD_TYPE_OBJECT("LONG_STRING", cxoDbTypeLong)
CXO_ADD_TYPE_OBJECT("NCLOB", cxoDbTypeNclob)
CXO_ADD_TYPE_OBJECT("TIMESTAMP", cxoDbTypeTimestamp)
CXO_ADD_TYPE_OBJECT("NATIVE_INT", cxoDbTypeBinaryInteger)
CXO_ADD_TYPE_OBJECT("NATIVE_FLOAT", cxoDbTypeBinaryDouble)
CXO_ADD_TYPE_OBJECT("BOOLEAN", cxoDbTypeBoolean)
// create the Python Database API types
CXO_ADD_API_TYPE("BINARY", CXO_TRANSFORM_BINARY, &cxoApiTypeBinary)
CXO_ADD_API_TYPE("DATETIME", CXO_TRANSFORM_DATETIME, &cxoApiTypeDatetime)
CXO_ADD_API_TYPE("NUMBER", CXO_TRANSFORM_FLOAT, &cxoApiTypeNumber)
CXO_ADD_API_TYPE("ROWID", CXO_TRANSFORM_ROWID, &cxoApiTypeRowid)
CXO_ADD_API_TYPE("STRING", CXO_TRANSFORM_STRING, &cxoApiTypeString)
// associate the Python Database API types with the database types
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeBinary, cxoDbTypeBfile)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeBinary, cxoDbTypeBlob)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeBinary, cxoDbTypeLongRaw)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeBinary, cxoDbTypeRaw)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeDate)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestamp)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestampLTZ)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeDatetime, cxoDbTypeTimestampTZ)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryDouble)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryFloat)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeBinaryInteger)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeNumber, cxoDbTypeNumber)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeRowid, cxoDbTypeRowid)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeChar)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeClob)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeLong)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeNchar)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeNclob)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeNvarchar)
CXO_ASSOCIATE_DB_TYPE(cxoApiTypeString, cxoDbTypeVarchar)
// create constants required by Python DB API 2.0 // create constants required by Python DB API 2.0
if (PyModule_AddStringConstant(module, "apilevel", "2.0") < 0) if (PyModule_AddStringConstant(module, "apilevel", "2.0") < 0)

View File

@ -1,5 +1,5 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
// //
// Licensed under BSD license (see LICENSE.txt). // Licensed under BSD license (see LICENSE.txt).
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -29,12 +29,14 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Forward Declarations // Forward Declarations
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
typedef struct cxoApiType cxoApiType;
typedef struct cxoBuffer cxoBuffer; typedef struct cxoBuffer cxoBuffer;
typedef struct cxoError cxoError;
typedef struct cxoConnection cxoConnection; typedef struct cxoConnection cxoConnection;
typedef struct cxoCursor cxoCursor; typedef struct cxoCursor cxoCursor;
typedef struct cxoDbType cxoDbType;
typedef struct cxoDeqOptions cxoDeqOptions; typedef struct cxoDeqOptions cxoDeqOptions;
typedef struct cxoEnqOptions cxoEnqOptions; typedef struct cxoEnqOptions cxoEnqOptions;
typedef struct cxoError cxoError;
typedef struct cxoFuture cxoFuture; typedef struct cxoFuture cxoFuture;
typedef struct cxoLob cxoLob; typedef struct cxoLob cxoLob;
typedef struct cxoMessage cxoMessage; typedef struct cxoMessage cxoMessage;
@ -54,7 +56,6 @@ typedef struct cxoSodaDocCursor cxoSodaDocCursor;
typedef struct cxoSodaOperation cxoSodaOperation; typedef struct cxoSodaOperation cxoSodaOperation;
typedef struct cxoSubscr cxoSubscr; typedef struct cxoSubscr cxoSubscr;
typedef struct cxoVar cxoVar; typedef struct cxoVar cxoVar;
typedef struct cxoVarType cxoVarType;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -74,55 +75,71 @@ extern PyObject *cxoProgrammingErrorException;
extern PyObject *cxoNotSupportedErrorException; extern PyObject *cxoNotSupportedErrorException;
// type objects // type objects
extern PyTypeObject cxoPyTypeBfileVar; extern PyTypeObject cxoPyTypeApiType;
extern PyTypeObject cxoPyTypeBinaryVar;
extern PyTypeObject cxoPyTypeBlobVar;
extern PyTypeObject cxoPyTypeBooleanVar;
extern PyTypeObject cxoPyTypeClobVar;
extern PyTypeObject cxoPyTypeConnection; extern PyTypeObject cxoPyTypeConnection;
extern PyTypeObject cxoPyTypeCursor; extern PyTypeObject cxoPyTypeCursor;
extern PyTypeObject cxoPyTypeCursorVar; extern PyTypeObject cxoPyTypeDbType;
extern PyTypeObject cxoPyTypeDateTimeVar;
extern PyTypeObject cxoPyTypeDeqOptions; extern PyTypeObject cxoPyTypeDeqOptions;
extern PyTypeObject cxoPyTypeEnqOptions; extern PyTypeObject cxoPyTypeEnqOptions;
extern PyTypeObject cxoPyTypeError; extern PyTypeObject cxoPyTypeError;
extern PyTypeObject cxoPyTypeFixedCharVar;
extern PyTypeObject cxoPyTypeFixedNcharVar;
extern PyTypeObject cxoPyTypeFuture; extern PyTypeObject cxoPyTypeFuture;
extern PyTypeObject cxoPyTypeIntervalVar;
extern PyTypeObject cxoPyTypeLob; extern PyTypeObject cxoPyTypeLob;
extern PyTypeObject cxoPyTypeLongBinaryVar;
extern PyTypeObject cxoPyTypeLongStringVar;
extern PyTypeObject cxoPyTypeMsgProps; extern PyTypeObject cxoPyTypeMsgProps;
extern PyTypeObject cxoPyTypeMessage; extern PyTypeObject cxoPyTypeMessage;
extern PyTypeObject cxoPyTypeMessageQuery; extern PyTypeObject cxoPyTypeMessageQuery;
extern PyTypeObject cxoPyTypeMessageRow; extern PyTypeObject cxoPyTypeMessageRow;
extern PyTypeObject cxoPyTypeMessageTable; extern PyTypeObject cxoPyTypeMessageTable;
extern PyTypeObject cxoPyTypeNativeFloatVar;
extern PyTypeObject cxoPyTypeNativeIntVar;
extern PyTypeObject cxoPyTypeNcharVar;
extern PyTypeObject cxoPyTypeNclobVar;
extern PyTypeObject cxoPyTypeNumberVar;
extern PyTypeObject cxoPyTypeObject; extern PyTypeObject cxoPyTypeObject;
extern PyTypeObject cxoPyTypeObjectAttr; extern PyTypeObject cxoPyTypeObjectAttr;
extern PyTypeObject cxoPyTypeObjectType; extern PyTypeObject cxoPyTypeObjectType;
extern PyTypeObject cxoPyTypeObjectVar;
extern PyTypeObject cxoPyTypeQueue; extern PyTypeObject cxoPyTypeQueue;
extern PyTypeObject cxoPyTypeRowidVar;
extern PyTypeObject cxoPyTypeSessionPool; extern PyTypeObject cxoPyTypeSessionPool;
extern PyTypeObject cxoPyTypeSodaCollection; extern PyTypeObject cxoPyTypeSodaCollection;
extern PyTypeObject cxoPyTypeSodaDatabase; extern PyTypeObject cxoPyTypeSodaDatabase;
extern PyTypeObject cxoPyTypeSodaDoc; extern PyTypeObject cxoPyTypeSodaDoc;
extern PyTypeObject cxoPyTypeSodaDocCursor; extern PyTypeObject cxoPyTypeSodaDocCursor;
extern PyTypeObject cxoPyTypeSodaOperation; extern PyTypeObject cxoPyTypeSodaOperation;
extern PyTypeObject cxoPyTypeStringVar;
extern PyTypeObject cxoPyTypeSubscr; extern PyTypeObject cxoPyTypeSubscr;
extern PyTypeObject cxoPyTypeTimestampVar; extern PyTypeObject cxoPyTypeVar;
// datetime types // datetime types
extern PyTypeObject *cxoPyTypeDate; extern PyTypeObject *cxoPyTypeDate;
extern PyTypeObject *cxoPyTypeDateTime; extern PyTypeObject *cxoPyTypeDateTime;
// database types
extern cxoDbType *cxoDbTypeBfile;
extern cxoDbType *cxoDbTypeBinaryDouble;
extern cxoDbType *cxoDbTypeBinaryFloat;
extern cxoDbType *cxoDbTypeBinaryInteger;
extern cxoDbType *cxoDbTypeBlob;
extern cxoDbType *cxoDbTypeBoolean;
extern cxoDbType *cxoDbTypeChar;
extern cxoDbType *cxoDbTypeClob;
extern cxoDbType *cxoDbTypeCursor;
extern cxoDbType *cxoDbTypeDate;
extern cxoDbType *cxoDbTypeIntervalDS;
extern cxoDbType *cxoDbTypeIntervalYM;
extern cxoDbType *cxoDbTypeLong;
extern cxoDbType *cxoDbTypeLongRaw;
extern cxoDbType *cxoDbTypeNchar;
extern cxoDbType *cxoDbTypeNclob;
extern cxoDbType *cxoDbTypeNumber;
extern cxoDbType *cxoDbTypeNvarchar;
extern cxoDbType *cxoDbTypeObject;
extern cxoDbType *cxoDbTypeRaw;
extern cxoDbType *cxoDbTypeRowid;
extern cxoDbType *cxoDbTypeTimestamp;
extern cxoDbType *cxoDbTypeTimestampLTZ;
extern cxoDbType *cxoDbTypeTimestampTZ;
extern cxoDbType *cxoDbTypeVarchar;
// database API types
extern cxoApiType *cxoApiTypeBinary;
extern cxoApiType *cxoApiTypeDatetime;
extern cxoApiType *cxoApiTypeNumber;
extern cxoApiType *cxoApiTypeRowid;
extern cxoApiType *cxoApiTypeString;
// JSON dump and load functions for use with SODA // JSON dump and load functions for use with SODA
extern PyObject *cxoJsonDumpFunction; extern PyObject *cxoJsonDumpFunction;
extern PyObject *cxoJsonLoadFunction; extern PyObject *cxoJsonLoadFunction;
@ -166,6 +183,7 @@ typedef enum {
CXO_TRANSFORM_TIMEDELTA, CXO_TRANSFORM_TIMEDELTA,
CXO_TRANSFORM_TIMESTAMP, CXO_TRANSFORM_TIMESTAMP,
CXO_TRANSFORM_TIMESTAMP_LTZ, CXO_TRANSFORM_TIMESTAMP_LTZ,
CXO_TRANSFORM_TIMESTAMP_TZ,
CXO_TRANSFORM_UNSUPPORTED CXO_TRANSFORM_UNSUPPORTED
} cxoTransformNum; } cxoTransformNum;
@ -173,6 +191,13 @@ typedef enum {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Structures // Structures
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
struct cxoApiType {
PyObject_HEAD
const char *name;
PyObject *dbTypes;
cxoTransformNum defaultTransformNum;
};
struct cxoBuffer { struct cxoBuffer {
const char *ptr; const char *ptr;
uint32_t numCharacters; uint32_t numCharacters;
@ -228,6 +253,13 @@ struct cxoCursor {
int isOpen; int isOpen;
}; };
struct cxoDbType {
PyObject_HEAD
uint32_t num;
const char *name;
cxoTransformNum defaultTransformNum;
};
struct cxoDeqOptions { struct cxoDeqOptions {
PyObject_HEAD PyObject_HEAD
dpiDeqOptions *handle; dpiDeqOptions *handle;
@ -247,7 +279,7 @@ struct cxoFuture {
struct cxoLob { struct cxoLob {
PyObject_HEAD PyObject_HEAD
cxoConnection *connection; cxoConnection *connection;
dpiOracleTypeNum oracleTypeNum; cxoDbType *dbType;
dpiLob *handle; dpiLob *handle;
}; };
@ -303,7 +335,8 @@ struct cxoObjectAttr {
dpiObjectAttr *handle; dpiObjectAttr *handle;
dpiOracleTypeNum oracleTypeNum; dpiOracleTypeNum oracleTypeNum;
cxoTransformNum transformNum; cxoTransformNum transformNum;
cxoObjectType *type; cxoObjectType *objectType;
cxoDbType *dbType;
}; };
struct cxoObjectType { struct cxoObjectType {
@ -316,7 +349,8 @@ struct cxoObjectType {
cxoConnection *connection; cxoConnection *connection;
dpiOracleTypeNum elementOracleTypeNum; dpiOracleTypeNum elementOracleTypeNum;
cxoTransformNum elementTransformNum; cxoTransformNum elementTransformNum;
PyObject *elementType; cxoObjectType *elementObjectType;
cxoDbType *elementDbType;
char isCollection; char isCollection;
}; };
@ -418,13 +452,9 @@ struct cxoVar {
int isArray; int isArray;
int isValueSet; int isValueSet;
int getReturnedData; int getReturnedData;
cxoVarType *type;
};
struct cxoVarType {
cxoTransformNum transformNum; cxoTransformNum transformNum;
PyTypeObject *pythonType; dpiNativeTypeNum nativeTypeNum;
uint32_t size; cxoDbType *dbType;
}; };
@ -441,6 +471,9 @@ int cxoCursor_performBind(cxoCursor *cursor);
int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters, int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters,
unsigned numElements, unsigned arrayPos, int deferTypeAssignment); unsigned numElements, unsigned arrayPos, int deferTypeAssignment);
cxoDbType *cxoDbType_fromDataTypeInfo(dpiDataTypeInfo *info);
cxoDbType *cxoDbType_fromTransformNum(cxoTransformNum transformNum);
cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection, cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection,
dpiDeqOptions *handle); dpiDeqOptions *handle);
@ -454,7 +487,7 @@ int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo);
PyObject *cxoError_raiseFromString(PyObject *exceptionType, PyObject *cxoError_raiseFromString(PyObject *exceptionType,
const char *message); const char *message);
PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum, PyObject *cxoLob_new(cxoConnection *connection, cxoDbType *dbType,
dpiLob *handle); dpiLob *handle);
cxoMsgProps *cxoMsgProps_new(cxoConnection*, dpiMsgProps *handle); cxoMsgProps *cxoMsgProps_new(cxoConnection*, dpiMsgProps *handle);
@ -491,9 +524,15 @@ int cxoTransform_fromPython(cxoTransformNum transformNum,
dpiNativeTypeNum *nativeTypeNum, PyObject *pyValue, dpiNativeTypeNum *nativeTypeNum, PyObject *pyValue,
dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding, dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding,
const char *nencoding, cxoVar *var, uint32_t arrayPos); const char *nencoding, cxoVar *var, uint32_t arrayPos);
uint32_t cxoTransform_getDefaultSize(cxoTransformNum transformNum);
cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info); cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info);
cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type); cxoTransformNum cxoTransform_getNumFromPythonValue(PyObject *value,
cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql); int plsql);
int cxoTransform_getNumFromType(PyObject *type, cxoTransformNum *transformNum,
cxoObjectType **objType);
int cxoTransform_getNumFromValue(PyObject *value, int *isArray,
Py_ssize_t *size, Py_ssize_t *numElements, int plsql,
cxoTransformNum *transformNum);
void cxoTransform_getTypeInfo(cxoTransformNum transformNum, void cxoTransform_getTypeInfo(cxoTransformNum transformNum,
dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum); dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum);
int cxoTransform_init(void); int cxoTransform_init(void);
@ -512,20 +551,15 @@ int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer);
int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg, int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg,
dpiSodaDoc **handle); dpiSodaDoc **handle);
cxoVarType *cxoVarType_fromDataTypeInfo(dpiDataTypeInfo *info);
cxoVarType *cxoVarType_fromPythonType(PyObject *type, cxoObjectType **objType);
cxoVarType *cxoVarType_fromPythonValue(PyObject *value, int *isArray,
Py_ssize_t *size, Py_ssize_t *numElements, int plsql);
int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos); int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos);
int cxoVar_check(PyObject *object); int cxoVar_check(PyObject *object);
PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos); PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos);
PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos); PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos);
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type, cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements,
Py_ssize_t size, int isArray, cxoObjectType *objType); cxoTransformNum transformNum, Py_ssize_t size, int isArray,
cxoObjectType *objType);
cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value, cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
uint32_t numElements); uint32_t numElements);
cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value, cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
Py_ssize_t numElements); Py_ssize_t numElements);
int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value); int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value);

View File

@ -211,7 +211,7 @@ static PyObject *cxoObject_getAttributeValue(cxoObject *obj,
nativeTypeNum, &data) < 0) nativeTypeNum, &data) < 0)
return cxoError_raiseAndReturnNull(); return cxoError_raiseAndReturnNull();
return cxoObject_convertToPython(obj, attribute->transformNum, &data, return cxoObject_convertToPython(obj, attribute->transformNum, &data,
attribute->type); attribute->objectType);
} }
@ -367,7 +367,7 @@ static PyObject *cxoObject_internalGetElementByIndex(cxoObject *obj,
&data) < 0) &data) < 0)
return cxoError_raiseAndReturnNull(); return cxoError_raiseAndReturnNull();
return cxoObject_convertToPython(obj, obj->objectType->elementTransformNum, return cxoObject_convertToPython(obj, obj->objectType->elementTransformNum,
&data, (cxoObjectType*) obj->objectType->elementType); &data, obj->objectType->elementObjectType);
} }

View File

@ -14,17 +14,27 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static void cxoObjectAttr_free(cxoObjectAttr*); static void cxoObjectAttr_free(cxoObjectAttr*);
static PyObject *cxoObjectAttr_repr(cxoObjectAttr*); static PyObject *cxoObjectAttr_repr(cxoObjectAttr*);
static PyObject *cxoObjectAttr_getType(cxoObjectAttr*, void*);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// declaration of members for Python type "ObjectAttribute" // declaration of members
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyMemberDef cxoObjectAttrMembers[] = { static PyMemberDef cxoMembers[] = {
{ "name", T_OBJECT, offsetof(cxoObjectAttr, name), READONLY }, { "name", T_OBJECT, offsetof(cxoObjectAttr, name), READONLY },
{ NULL } { NULL }
}; };
//-----------------------------------------------------------------------------
// declaration of calculated members
//-----------------------------------------------------------------------------
static PyGetSetDef cxoCalcMembers[] = {
{ "type", (getter) cxoObjectAttr_getType, 0, 0, 0 },
{ NULL }
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Python type declaration // Python type declaration
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -35,7 +45,8 @@ PyTypeObject cxoPyTypeObjectAttr = {
.tp_dealloc = (destructor) cxoObjectAttr_free, .tp_dealloc = (destructor) cxoObjectAttr_free,
.tp_repr = (reprfunc) cxoObjectAttr_repr, .tp_repr = (reprfunc) cxoObjectAttr_repr,
.tp_flags = Py_TPFLAGS_DEFAULT, .tp_flags = Py_TPFLAGS_DEFAULT,
.tp_members = cxoObjectAttrMembers .tp_members = cxoMembers,
.tp_getset = cxoCalcMembers
}; };
@ -51,15 +62,19 @@ static int cxoObjectAttr_initialize(cxoObjectAttr *attr,
if (dpiObjectAttr_getInfo(attr->handle, &info) < 0) if (dpiObjectAttr_getInfo(attr->handle, &info) < 0)
return cxoError_raiseAndReturnInt(); return cxoError_raiseAndReturnInt();
attr->transformNum = cxoTransform_getNumFromDataTypeInfo(&info.typeInfo); attr->transformNum = cxoTransform_getNumFromDataTypeInfo(&info.typeInfo);
attr->dbType = cxoDbType_fromTransformNum(attr->transformNum);
if (!attr->dbType)
return -1;
Py_INCREF(attr->dbType);
attr->oracleTypeNum = info.typeInfo.oracleTypeNum; attr->oracleTypeNum = info.typeInfo.oracleTypeNum;
attr->name = PyUnicode_Decode(info.name, info.nameLength, attr->name = PyUnicode_Decode(info.name, info.nameLength,
connection->encodingInfo.encoding, NULL); connection->encodingInfo.encoding, NULL);
if (!attr->name) if (!attr->name)
return -1; return -1;
if (info.typeInfo.objectType) { if (info.typeInfo.objectType) {
attr->type = cxoObjectType_new(connection, attr->objectType = cxoObjectType_new(connection,
info.typeInfo.objectType); info.typeInfo.objectType);
if (!attr->type) if (!attr->objectType)
return -1; return -1;
} }
@ -103,11 +118,29 @@ static void cxoObjectAttr_free(cxoObjectAttr *attr)
attr->handle = NULL; attr->handle = NULL;
} }
Py_CLEAR(attr->name); Py_CLEAR(attr->name);
Py_CLEAR(attr->type); Py_CLEAR(attr->objectType);
Py_CLEAR(attr->dbType);
Py_TYPE(attr)->tp_free((PyObject*) attr); Py_TYPE(attr)->tp_free((PyObject*) attr);
} }
//-----------------------------------------------------------------------------
// cxoObjectAttr_getType()
// Return the type associated with the attribute. This is either an object
// type or one of the database type constants.
//-----------------------------------------------------------------------------
static PyObject *cxoObjectAttr_getType(cxoObjectAttr *attr, void *unused)
{
if (attr->objectType) {
Py_INCREF(attr->objectType);
return (PyObject*) attr->objectType;
}
Py_INCREF(attr->dbType);
return (PyObject*) attr->dbType;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoObjectAttr_repr() // cxoObjectAttr_repr()
// Return a string representation of the object attribute. // Return a string representation of the object attribute.

View File

@ -20,12 +20,14 @@
static void cxoObjectType_free(cxoObjectType*); static void cxoObjectType_free(cxoObjectType*);
static PyObject *cxoObjectType_repr(cxoObjectType*); static PyObject *cxoObjectType_repr(cxoObjectType*);
static PyObject *cxoObjectType_newObject(cxoObjectType*, PyObject*, PyObject*); static PyObject *cxoObjectType_newObject(cxoObjectType*, PyObject*, PyObject*);
static PyObject *cxoObjectType_richCompare(cxoObjectType*, PyObject*, int);
static PyObject *cxoObjectType_getElementType(cxoObjectType*, void*);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// declaration of methods for Python type "ObjectType" // declaration of methods
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyMethodDef cxoObjectTypeMethods[] = { static PyMethodDef cxoMethods[] = {
{ "newobject", (PyCFunction) cxoObjectType_newObject, { "newobject", (PyCFunction) cxoObjectType_newObject,
METH_VARARGS | METH_KEYWORDS }, METH_VARARGS | METH_KEYWORDS },
{ NULL } { NULL }
@ -33,20 +35,27 @@ static PyMethodDef cxoObjectTypeMethods[] = {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// declaration of members for Python type "ObjectType" // declaration of members
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyMemberDef cxoObjectTypeMembers[] = { static PyMemberDef cxoMembers[] = {
{ "schema", T_OBJECT, offsetof(cxoObjectType, schema), READONLY }, { "schema", T_OBJECT, offsetof(cxoObjectType, schema), READONLY },
{ "name", T_OBJECT, offsetof(cxoObjectType, name), READONLY }, { "name", T_OBJECT, offsetof(cxoObjectType, name), READONLY },
{ "attributes", T_OBJECT, offsetof(cxoObjectType, attributes), READONLY }, { "attributes", T_OBJECT, offsetof(cxoObjectType, attributes), READONLY },
{ "elementType", T_OBJECT, offsetof(cxoObjectType, elementType),
READONLY },
{ "iscollection", T_BOOL, offsetof(cxoObjectType, isCollection), { "iscollection", T_BOOL, offsetof(cxoObjectType, isCollection),
READONLY }, READONLY },
{ NULL } { NULL }
}; };
//-----------------------------------------------------------------------------
// declaration of calculated members
//-----------------------------------------------------------------------------
static PyGetSetDef cxoCalcMembers[] = {
{ "element_type", (getter) cxoObjectType_getElementType, 0, 0, 0 },
{ NULL }
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Python type declarations // Python type declarations
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -58,8 +67,10 @@ PyTypeObject cxoPyTypeObjectType = {
.tp_repr = (reprfunc) cxoObjectType_repr, .tp_repr = (reprfunc) cxoObjectType_repr,
.tp_call = (ternaryfunc) cxoObjectType_newObject, .tp_call = (ternaryfunc) cxoObjectType_newObject,
.tp_flags = Py_TPFLAGS_DEFAULT, .tp_flags = Py_TPFLAGS_DEFAULT,
.tp_methods = cxoObjectTypeMethods, .tp_methods = cxoMethods,
.tp_members = cxoObjectTypeMembers, .tp_members = cxoMembers,
.tp_getset = cxoCalcMembers,
.tp_richcompare = (richcmpfunc) cxoObjectType_richCompare
}; };
@ -89,14 +100,21 @@ static int cxoObjectType_initialize(cxoObjectType *objType,
if (!objType->name) if (!objType->name)
return -1; return -1;
objType->isCollection = info.isCollection; objType->isCollection = info.isCollection;
objType->elementOracleTypeNum = info.elementTypeInfo.oracleTypeNum; if (info.isCollection) {
objType->elementTransformNum = objType->elementOracleTypeNum = info.elementTypeInfo.oracleTypeNum;
cxoTransform_getNumFromDataTypeInfo(&info.elementTypeInfo); objType->elementTransformNum =
if (info.elementTypeInfo.objectType) { cxoTransform_getNumFromDataTypeInfo(&info.elementTypeInfo);
objType->elementType = (PyObject*) cxoObjectType_new(connection, objType->elementDbType =
info.elementTypeInfo.objectType); cxoDbType_fromTransformNum(objType->elementTransformNum);
if (!objType->elementType) if (!objType->elementDbType)
return -1; return -1;
Py_INCREF(objType->elementDbType);
if (info.elementTypeInfo.objectType) {
objType->elementObjectType = cxoObjectType_new(connection,
info.elementTypeInfo.objectType);
if (!objType->elementObjectType)
return -1;
}
} }
// allocate the attribute list (temporary and permanent) and dictionary // allocate the attribute list (temporary and permanent) and dictionary
@ -205,11 +223,34 @@ static void cxoObjectType_free(cxoObjectType *objType)
Py_CLEAR(objType->name); Py_CLEAR(objType->name);
Py_CLEAR(objType->attributes); Py_CLEAR(objType->attributes);
Py_CLEAR(objType->attributesByName); Py_CLEAR(objType->attributesByName);
Py_CLEAR(objType->elementType); Py_CLEAR(objType->elementObjectType);
Py_CLEAR(objType->elementDbType);
Py_TYPE(objType)->tp_free((PyObject*) objType); Py_TYPE(objType)->tp_free((PyObject*) objType);
} }
//-----------------------------------------------------------------------------
// cxoObjectType_getElementType()
// Return the element type associated with a collection. This is either an
// object type or one of the database type constants. If the object type is not
// a collection, None is returned.
//-----------------------------------------------------------------------------
static PyObject *cxoObjectType_getElementType(cxoObjectType *type,
void *unused)
{
if (type->elementObjectType) {
Py_INCREF(type->elementObjectType);
return (PyObject*) type->elementObjectType;
}
if (type->elementDbType) {
Py_INCREF(type->elementDbType);
return (PyObject*) type->elementDbType;
}
Py_RETURN_NONE;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoObjectType_repr() // cxoObjectType_repr()
// Return a string representation of the object type. // Return a string representation of the object type.
@ -228,6 +269,54 @@ static PyObject *cxoObjectType_repr(cxoObjectType *objType)
} }
//-----------------------------------------------------------------------------
// cxoObjectType_richCompare()
// Peforms a comparison between the object type and another Python object.
// Equality (and inequality) are suppported to match object types; no other
// operations are supported.
//-----------------------------------------------------------------------------
static PyObject *cxoObjectType_richCompare(cxoObjectType* objType,
PyObject* otherObj, int op)
{
cxoObjectType *otherObjType;
int status, equal = 0;
// only equality and inequality can be checked
if (op != Py_EQ && op != Py_NE) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
// check to see if the other object is an object type, too
status = PyObject_IsInstance(otherObj, (PyObject*) &cxoPyTypeObjectType);
if (status < 0)
return NULL;
if (status == 1) {
otherObjType = (cxoObjectType*) otherObj;
if (otherObjType->connection == objType->connection ||
otherObjType->connection->sessionPool ==
objType->connection->sessionPool) {
equal = PyObject_RichCompareBool(otherObjType->schema,
objType->schema, Py_EQ);
if (equal < 0)
return NULL;
if (equal) {
equal = PyObject_RichCompareBool(otherObjType->name,
objType->name, Py_EQ);
if (equal < 0)
return NULL;
}
}
}
// determine return value
if ((equal && op == Py_EQ) || (!equal && op == Py_NE)) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoObjectType_newObject() // cxoObjectType_newObject()
// Factory function for creating objects of the type which can be bound. // Factory function for creating objects of the type which can be bound.

View File

@ -1,5 +1,5 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -24,6 +24,12 @@
#define PyDateTime_DELTA_GET_MICROSECONDS(x) ((x)->microseconds) #define PyDateTime_DELTA_GET_MICROSECONDS(x) ((x)->microseconds)
#endif #endif
// forward declarations
static Py_ssize_t cxoTransform_calculateSize(PyObject *value,
cxoTransformNum transformNum);
static cxoTransformNum cxoTransform_getNumFromPythonType(PyTypeObject *type);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Types // Types
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -175,10 +181,39 @@ static const cxoTransform cxoAllTransforms[] = {
CXO_TRANSFORM_TIMESTAMP_LTZ, CXO_TRANSFORM_TIMESTAMP_LTZ,
DPI_ORACLE_TYPE_TIMESTAMP_LTZ, DPI_ORACLE_TYPE_TIMESTAMP_LTZ,
DPI_NATIVE_TYPE_TIMESTAMP DPI_NATIVE_TYPE_TIMESTAMP
},
{
CXO_TRANSFORM_TIMESTAMP_TZ,
DPI_ORACLE_TYPE_TIMESTAMP_TZ,
DPI_NATIVE_TYPE_TIMESTAMP
} }
}; };
//-----------------------------------------------------------------------------
// cxoTransform_calculateSize()
// Calculate the size to use with the specified transform and Python value.
// This function is only called by cxoTransform_getNumFromValue() and no
// attempt is made to verify the value further.
//-----------------------------------------------------------------------------
static Py_ssize_t cxoTransform_calculateSize(PyObject *value,
cxoTransformNum transformNum)
{
switch (transformNum) {
case CXO_TRANSFORM_NONE:
return 1;
case CXO_TRANSFORM_BINARY:
return PyBytes_GET_SIZE(value);
case CXO_TRANSFORM_NSTRING:
case CXO_TRANSFORM_STRING:
return PyUnicode_GET_SIZE(value);
default:
break;
}
return 0;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoTransform_dateFromTicks() // cxoTransform_dateFromTicks()
// Creates a date from ticks (number of seconds since Unix epoch). // Creates a date from ticks (number of seconds since Unix epoch).
@ -320,6 +355,7 @@ int cxoTransform_fromPython(cxoTransformNum transformNum,
case CXO_TRANSFORM_DATETIME: case CXO_TRANSFORM_DATETIME:
case CXO_TRANSFORM_TIMESTAMP: case CXO_TRANSFORM_TIMESTAMP:
case CXO_TRANSFORM_TIMESTAMP_LTZ: case CXO_TRANSFORM_TIMESTAMP_LTZ:
case CXO_TRANSFORM_TIMESTAMP_TZ:
if (PyDateTime_Check(pyValue)) { if (PyDateTime_Check(pyValue)) {
memset(&dbValue->asTimestamp, 0, sizeof(dbValue->asTimestamp)); memset(&dbValue->asTimestamp, 0, sizeof(dbValue->asTimestamp));
dbValue->asTimestamp.year = PyDateTime_GET_YEAR(pyValue); dbValue->asTimestamp.year = PyDateTime_GET_YEAR(pyValue);
@ -368,6 +404,37 @@ int cxoTransform_fromPython(cxoTransformNum transformNum,
} }
//-----------------------------------------------------------------------------
// cxoTransform_getDefaultSize()
// Return the default size for the specified transform.
//-----------------------------------------------------------------------------
uint32_t cxoTransform_getDefaultSize(cxoTransformNum transformNum)
{
switch (transformNum) {
case CXO_TRANSFORM_NONE:
return 1;
case CXO_TRANSFORM_BINARY:
case CXO_TRANSFORM_NSTRING:
case CXO_TRANSFORM_STRING:
return 4000;
case CXO_TRANSFORM_DECIMAL:
case CXO_TRANSFORM_FLOAT:
case CXO_TRANSFORM_INT:
return 1000;
case CXO_TRANSFORM_FIXED_CHAR:
case CXO_TRANSFORM_FIXED_NCHAR:
return 2000;
case CXO_TRANSFORM_LONG_BINARY:
case CXO_TRANSFORM_LONG_STRING:
return 128 * 1024;
default:
break;
}
return 0;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoTransform_getNumFromDataTypeInfo() // cxoTransform_getNumFromDataTypeInfo()
// Get the default transformation to use for the specified data type. // Get the default transformation to use for the specified data type.
@ -403,6 +470,7 @@ cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info)
case DPI_ORACLE_TYPE_TIMESTAMP: case DPI_ORACLE_TYPE_TIMESTAMP:
return CXO_TRANSFORM_TIMESTAMP; return CXO_TRANSFORM_TIMESTAMP;
case DPI_ORACLE_TYPE_TIMESTAMP_TZ: case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
return CXO_TRANSFORM_TIMESTAMP_TZ;
case DPI_ORACLE_TYPE_TIMESTAMP_LTZ: case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
return CXO_TRANSFORM_TIMESTAMP_LTZ; return CXO_TRANSFORM_TIMESTAMP_LTZ;
case DPI_ORACLE_TYPE_INTERVAL_DS: case DPI_ORACLE_TYPE_INTERVAL_DS:
@ -433,83 +501,39 @@ cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoTransform_getNumFromType() // cxoTransform_getNumFromPythonType()
// Get the appropriate transformation to use for the specified Python type. // Get the appropriate transformation to use for the specified Python type.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type) static cxoTransformNum cxoTransform_getNumFromPythonType(PyTypeObject *type)
{ {
if (type == &PyUnicode_Type) if (type == &PyUnicode_Type)
return CXO_TRANSFORM_STRING; return CXO_TRANSFORM_STRING;
if (type == &cxoPyTypeStringVar)
return CXO_TRANSFORM_STRING;
if (type == &cxoPyTypeFixedCharVar)
return CXO_TRANSFORM_FIXED_CHAR;
if (type == &cxoPyTypeNcharVar)
return CXO_TRANSFORM_NSTRING;
if (type == &cxoPyTypeFixedNcharVar)
return CXO_TRANSFORM_FIXED_NCHAR;
if (type == &cxoPyTypeRowidVar)
return CXO_TRANSFORM_ROWID;
if (type == &PyBytes_Type) if (type == &PyBytes_Type)
return CXO_TRANSFORM_BINARY; return CXO_TRANSFORM_BINARY;
if (type == &cxoPyTypeBinaryVar)
return CXO_TRANSFORM_BINARY;
if (type == &PyFloat_Type) if (type == &PyFloat_Type)
return CXO_TRANSFORM_FLOAT; return CXO_TRANSFORM_FLOAT;
if (type == &PyLong_Type) if (type == &PyLong_Type)
return CXO_TRANSFORM_INT; return CXO_TRANSFORM_INT;
if (type == cxoPyTypeDecimal) if (type == cxoPyTypeDecimal)
return CXO_TRANSFORM_DECIMAL; return CXO_TRANSFORM_DECIMAL;
if (type == &cxoPyTypeNumberVar)
return CXO_TRANSFORM_FLOAT;
if (type == &cxoPyTypeNativeFloatVar)
return CXO_TRANSFORM_NATIVE_DOUBLE;
if (type == &cxoPyTypeNativeIntVar)
return CXO_TRANSFORM_NATIVE_INT;
if (type == &PyBool_Type) if (type == &PyBool_Type)
return CXO_TRANSFORM_BOOLEAN; return CXO_TRANSFORM_BOOLEAN;
if (type == &cxoPyTypeBooleanVar)
return CXO_TRANSFORM_BOOLEAN;
if (type == PyDateTimeAPI->DateType) if (type == PyDateTimeAPI->DateType)
return CXO_TRANSFORM_DATE; return CXO_TRANSFORM_DATE;
if (type == PyDateTimeAPI->DateTimeType) if (type == PyDateTimeAPI->DateTimeType)
return CXO_TRANSFORM_DATETIME; return CXO_TRANSFORM_DATETIME;
if (type == &cxoPyTypeDateTimeVar)
return CXO_TRANSFORM_DATETIME;
if (type == &cxoPyTypeTimestampVar)
return CXO_TRANSFORM_TIMESTAMP;
if (type == PyDateTimeAPI->DeltaType) if (type == PyDateTimeAPI->DeltaType)
return CXO_TRANSFORM_TIMEDELTA; return CXO_TRANSFORM_TIMEDELTA;
if (type == &cxoPyTypeIntervalVar)
return CXO_TRANSFORM_TIMEDELTA;
if (type == &cxoPyTypeObject)
return CXO_TRANSFORM_OBJECT;
if (type == &cxoPyTypeObjectVar)
return CXO_TRANSFORM_OBJECT;
if (type == &cxoPyTypeClobVar)
return CXO_TRANSFORM_CLOB;
if (type == &cxoPyTypeNclobVar)
return CXO_TRANSFORM_NCLOB;
if (type == &cxoPyTypeBlobVar)
return CXO_TRANSFORM_BLOB;
if (type == &cxoPyTypeBfileVar)
return CXO_TRANSFORM_BFILE;
if (type == &cxoPyTypeCursorVar)
return CXO_TRANSFORM_CURSOR;
if (type == &cxoPyTypeLongStringVar)
return CXO_TRANSFORM_LONG_STRING;
if (type == &cxoPyTypeLongBinaryVar)
return CXO_TRANSFORM_LONG_BINARY;
return CXO_TRANSFORM_UNSUPPORTED; return CXO_TRANSFORM_UNSUPPORTED;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoTransform_getNumFromValue() // cxoTransform_getNumFromPythonValue()
// Get the appropriate transformation to use for the specified Python object. // Get the appropriate transformation to use for the specified Python value.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql) cxoTransformNum cxoTransform_getNumFromPythonValue(PyObject *value, int plsql)
{ {
cxoLob *lob; cxoLob *lob;
@ -542,19 +566,144 @@ cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql)
return CXO_TRANSFORM_OBJECT; return CXO_TRANSFORM_OBJECT;
if (PyObject_TypeCheck(value, &cxoPyTypeLob)) { if (PyObject_TypeCheck(value, &cxoPyTypeLob)) {
lob = (cxoLob*) value; lob = (cxoLob*) value;
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_CLOB) return lob->dbType->defaultTransformNum;
return CXO_TRANSFORM_CLOB;
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_NCLOB)
return CXO_TRANSFORM_NCLOB;
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_BLOB)
return CXO_TRANSFORM_BLOB;
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_BFILE)
return CXO_TRANSFORM_BFILE;
} }
return CXO_TRANSFORM_UNSUPPORTED; return CXO_TRANSFORM_UNSUPPORTED;
} }
//-----------------------------------------------------------------------------
// cxoTransform_getNumFromType()
// Get the appropriate transformation to use for the specified type. This
// can be either a database type constant defined at the module level or a
// Python type.
//-----------------------------------------------------------------------------
int cxoTransform_getNumFromType(PyObject *type, cxoTransformNum *transformNum,
cxoObjectType **objType)
{
PyTypeObject *pyType;
cxoApiType *apiType;
cxoDbType *dbType;
char message[250];
int status;
// check to see if a database type constant has been specified
status = PyObject_IsInstance(type, (PyObject*) &cxoPyTypeDbType);
if (status < 0)
return -1;
if (status == 1) {
dbType = (cxoDbType*) type;
*transformNum = dbType->defaultTransformNum;
*objType = NULL;
return 0;
}
// check to see if a DB API type constant has been specified
status = PyObject_IsInstance(type, (PyObject*) &cxoPyTypeApiType);
if (status < 0)
return -1;
if (status == 1) {
apiType = (cxoApiType*) type;
*transformNum = apiType->defaultTransformNum;
*objType = NULL;
return 0;
}
// check to see if an object type has been specified
if (Py_TYPE(type) == &cxoPyTypeObjectType) {
*transformNum = CXO_TRANSFORM_OBJECT;
*objType = (cxoObjectType*) type;
return 0;
}
// check to see if a Python type has been specified
if (Py_TYPE(type) != &PyType_Type) {
PyErr_SetString(PyExc_TypeError, "expecting type");
return -1;
}
// check to see if the Python type is a supported type
pyType = (PyTypeObject*) type;
*objType = NULL;
*transformNum = cxoTransform_getNumFromPythonType(pyType);
if (*transformNum != CXO_TRANSFORM_UNSUPPORTED)
return 0;
// no valid type specified
snprintf(message, sizeof(message), "Python type %s not supported.",
pyType->tp_name);
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
return -1;
}
//-----------------------------------------------------------------------------
// cxoTransform_getNumFromValue()
// Get the appropriate transformation to use for the specified value. If the
// value is an array, determine the transformation that can be used for all of
// the elements in that array.
//-----------------------------------------------------------------------------
int cxoTransform_getNumFromValue(PyObject *value, int *isArray,
Py_ssize_t *size, Py_ssize_t *numElements, int plsql,
cxoTransformNum *transformNum)
{
cxoTransformNum tempTransformNum;
PyObject *elementValue;
Py_ssize_t i, tempSize;
char message[250];
// initialization (except numElements which always has a valid value and is
// only overridden when a an array is encountered)
*size = 0;
*isArray = 0;
// handle arrays
if (PyList_Check(value)) {
*transformNum = CXO_TRANSFORM_NONE;
for (i = 0; i < PyList_GET_SIZE(value); i++) {
elementValue = PyList_GET_ITEM(value, i);
tempTransformNum = cxoTransform_getNumFromPythonValue(elementValue,
1);
if (tempTransformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message),
"element %u value of type %s is not supported",
(unsigned) i, Py_TYPE(value)->tp_name);
cxoError_raiseFromString(cxoNotSupportedErrorException,
message);
return -1;
} else if (*transformNum == CXO_TRANSFORM_NONE) {
*transformNum = tempTransformNum;
} else if (*transformNum != tempTransformNum) {
snprintf(message, sizeof(message),
"element %u value is not the same type as previous "
"elements", (unsigned) i);
cxoError_raiseFromString(cxoNotSupportedErrorException,
message);
return -1;
}
tempSize = cxoTransform_calculateSize(elementValue, *transformNum);
if (tempSize > *size)
*size = tempSize;
}
*isArray = 1;
*numElements = PyList_GET_SIZE(value);
return 0;
}
// handle scalar values
*transformNum = cxoTransform_getNumFromPythonValue(value, plsql);
if (*transformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message),
"Python value of type %s not supported.",
Py_TYPE(value)->tp_name);
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
return -1;
}
*size = cxoTransform_calculateSize(value, *transformNum);
return 0;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoTransform_getTypeInfo() // cxoTransform_getTypeInfo()
// Get type information for the specified transform. The transform number is // Get type information for the specified transform. The transform number is
@ -618,17 +767,16 @@ PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
cxoConnection *connection, cxoObjectType *objType, cxoConnection *connection, cxoObjectType *objType,
dpiDataBuffer *dbValue, const char *encodingErrors) dpiDataBuffer *dbValue, const char *encodingErrors)
{ {
const cxoTransform *transform;
PyObject *stringObj, *result; PyObject *stringObj, *result;
dpiIntervalDS *intervalDS; dpiIntervalDS *intervalDS;
dpiTimestamp *timestamp; dpiTimestamp *timestamp;
uint32_t rowidLength; uint32_t rowidLength;
cxoDbType *dbType;
const char *rowid; const char *rowid;
cxoCursor *cursor; cxoCursor *cursor;
dpiBytes *bytes; dpiBytes *bytes;
int32_t seconds; int32_t seconds;
transform = &cxoAllTransforms[transformNum];
switch (transformNum) { switch (transformNum) {
case CXO_TRANSFORM_BINARY: case CXO_TRANSFORM_BINARY:
case CXO_TRANSFORM_LONG_BINARY: case CXO_TRANSFORM_LONG_BINARY:
@ -638,8 +786,8 @@ PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
case CXO_TRANSFORM_BLOB: case CXO_TRANSFORM_BLOB:
case CXO_TRANSFORM_CLOB: case CXO_TRANSFORM_CLOB:
case CXO_TRANSFORM_NCLOB: case CXO_TRANSFORM_NCLOB:
return cxoLob_new(connection, transform->oracleTypeNum, dbType = cxoDbType_fromTransformNum(transformNum);
dbValue->asLOB); return cxoLob_new(connection, dbType, dbValue->asLOB);
case CXO_TRANSFORM_BOOLEAN: case CXO_TRANSFORM_BOOLEAN:
if (dbValue->asBoolean) if (dbValue->asBoolean)
Py_RETURN_TRUE; Py_RETURN_TRUE;
@ -660,6 +808,7 @@ PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
case CXO_TRANSFORM_DATETIME: case CXO_TRANSFORM_DATETIME:
case CXO_TRANSFORM_TIMESTAMP: case CXO_TRANSFORM_TIMESTAMP:
case CXO_TRANSFORM_TIMESTAMP_LTZ: case CXO_TRANSFORM_TIMESTAMP_LTZ:
case CXO_TRANSFORM_TIMESTAMP_TZ:
timestamp = &dbValue->asTimestamp; timestamp = &dbValue->asTimestamp;
return PyDateTime_FromDateAndTime(timestamp->year, return PyDateTime_FromDateAndTime(timestamp->year,
timestamp->month, timestamp->day, timestamp->hour, timestamp->month, timestamp->day, timestamp->hour,

View File

@ -15,7 +15,7 @@
#include "cxoModule.h" #include "cxoModule.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Declaration of common variable functions. // forward declaration of functions
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static void cxoVar_free(cxoVar*); static void cxoVar_free(cxoVar*);
static PyObject *cxoVar_repr(cxoVar*); static PyObject *cxoVar_repr(cxoVar*);
@ -24,35 +24,36 @@ static PyObject *cxoVar_externalSetValue(cxoVar*, PyObject*);
static PyObject *cxoVar_externalGetValue(cxoVar*, PyObject*, PyObject*); static PyObject *cxoVar_externalGetValue(cxoVar*, PyObject*, PyObject*);
static PyObject *cxoVar_externalGetActualElements(cxoVar*, void*); static PyObject *cxoVar_externalGetActualElements(cxoVar*, void*);
static PyObject *cxoVar_externalGetValues(cxoVar*, void*); static PyObject *cxoVar_externalGetValues(cxoVar*, void*);
static PyObject *cxoVar_getType(cxoVar*, void*);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// declaration of members for variables // declaration of members
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyMemberDef cxoVarMembers[] = { static PyMemberDef cxoMembers[] = {
{ "bufferSize", T_INT, offsetof(cxoVar, bufferSize), READONLY }, { "bufferSize", T_INT, offsetof(cxoVar, bufferSize), READONLY },
{ "inconverter", T_OBJECT, offsetof(cxoVar, inConverter), 0 }, { "inconverter", T_OBJECT, offsetof(cxoVar, inConverter), 0 },
{ "numElements", T_INT, offsetof(cxoVar, allocatedElements), { "numElements", T_INT, offsetof(cxoVar, allocatedElements),
READONLY }, READONLY },
{ "outconverter", T_OBJECT, offsetof(cxoVar, outConverter), 0 }, { "outconverter", T_OBJECT, offsetof(cxoVar, outConverter), 0 },
{ "size", T_INT, offsetof(cxoVar, size), READONLY }, { "size", T_INT, offsetof(cxoVar, size), READONLY },
{ "type", T_OBJECT, offsetof(cxoVar, objectType), READONLY },
{ NULL } { NULL }
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// declaration of calculated members for variables // declaration of calculated members
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyGetSetDef cxoVarCalcMembers[] = { static PyGetSetDef cxoCalcMembers[] = {
{ "actualElements", (getter) cxoVar_externalGetActualElements, 0, 0, 0 }, { "actualElements", (getter) cxoVar_externalGetActualElements, 0, 0, 0 },
{ "type", (getter) cxoVar_getType, 0, 0, 0 },
{ "values", (getter) cxoVar_externalGetValues, 0, 0, 0 }, { "values", (getter) cxoVar_externalGetValues, 0, 0, 0 },
{ NULL } { NULL }
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// declaration of methods for variables // declaration of methods
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyMethodDef cxoVarMethods[] = { static PyMethodDef cxoVarMethods[] = {
{ "copy", (PyCFunction) cxoVar_externalCopy, METH_VARARGS }, { "copy", (PyCFunction) cxoVar_externalCopy, METH_VARARGS },
@ -64,58 +65,35 @@ static PyMethodDef cxoVarMethods[] = {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// declaration of all variable types // declaration of Python type
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#define DECLARE_VARIABLE_TYPE(INTERNAL_NAME, EXTERNAL_NAME) \ PyTypeObject cxoPyTypeVar = {
PyTypeObject INTERNAL_NAME = { \ PyVarObject_HEAD_INIT(NULL, 0)
PyVarObject_HEAD_INIT(NULL, 0) \ .tp_name = "cx_Oracle.Var",
.tp_name = "cx_Oracle." #EXTERNAL_NAME, \ .tp_basicsize = sizeof(cxoVar),
.tp_basicsize = sizeof(cxoVar), \ .tp_dealloc = (destructor) cxoVar_free,
.tp_dealloc = (destructor) cxoVar_free, \ .tp_repr = (reprfunc) cxoVar_repr,
.tp_repr = (reprfunc) cxoVar_repr, \ .tp_flags = Py_TPFLAGS_DEFAULT,
.tp_flags = Py_TPFLAGS_DEFAULT, \ .tp_methods = cxoVarMethods,
.tp_methods = cxoVarMethods, \ .tp_members = cxoMembers,
.tp_members = cxoVarMembers, \ .tp_getset = cxoCalcMembers
.tp_getset = cxoVarCalcMembers \ };
};
DECLARE_VARIABLE_TYPE(cxoPyTypeBfileVar, BFILE)
DECLARE_VARIABLE_TYPE(cxoPyTypeBinaryVar, BINARY)
DECLARE_VARIABLE_TYPE(cxoPyTypeBlobVar, BLOB)
DECLARE_VARIABLE_TYPE(cxoPyTypeBooleanVar, BOOLEAN)
DECLARE_VARIABLE_TYPE(cxoPyTypeClobVar, CLOB)
DECLARE_VARIABLE_TYPE(cxoPyTypeCursorVar, CURSOR)
DECLARE_VARIABLE_TYPE(cxoPyTypeDateTimeVar, DATETIME)
DECLARE_VARIABLE_TYPE(cxoPyTypeFixedCharVar, FIXED_CHAR)
DECLARE_VARIABLE_TYPE(cxoPyTypeFixedNcharVar, FIXED_NCHAR)
DECLARE_VARIABLE_TYPE(cxoPyTypeIntervalVar, INTERVAL)
DECLARE_VARIABLE_TYPE(cxoPyTypeLongBinaryVar, LONG_BINARY)
DECLARE_VARIABLE_TYPE(cxoPyTypeLongStringVar, LONG_STRING)
DECLARE_VARIABLE_TYPE(cxoPyTypeNativeFloatVar, NATIVE_FLOAT)
DECLARE_VARIABLE_TYPE(cxoPyTypeNativeIntVar, NATIVE_INT)
DECLARE_VARIABLE_TYPE(cxoPyTypeNcharVar, NCHAR)
DECLARE_VARIABLE_TYPE(cxoPyTypeNclobVar, NCLOB)
DECLARE_VARIABLE_TYPE(cxoPyTypeNumberVar, NUMBER)
DECLARE_VARIABLE_TYPE(cxoPyTypeObjectVar, OBJECT)
DECLARE_VARIABLE_TYPE(cxoPyTypeRowidVar, ROWID)
DECLARE_VARIABLE_TYPE(cxoPyTypeStringVar, STRING)
DECLARE_VARIABLE_TYPE(cxoPyTypeTimestampVar, TIMESTAMP)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoVar_new() // cxoVar_new()
// Allocate a new variable. // Allocate a new variable.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type, cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements,
Py_ssize_t size, int isArray, cxoObjectType *objType) cxoTransformNum transformNum, Py_ssize_t size, int isArray,
cxoObjectType *objType)
{ {
dpiObjectType *typeHandle = NULL; dpiObjectType *typeHandle = NULL;
dpiOracleTypeNum oracleTypeNum; dpiOracleTypeNum oracleTypeNum;
dpiNativeTypeNum nativeTypeNum;
cxoVar *var; cxoVar *var;
// attempt to allocate the object // attempt to allocate the object
var = (cxoVar*) type->pythonType->tp_alloc(type->pythonType, 0); var = (cxoVar*) cxoPyTypeVar.tp_alloc(&cxoPyTypeVar, 0);
if (!var) if (!var)
return NULL; return NULL;
@ -130,15 +108,25 @@ cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
if (numElements == 0) if (numElements == 0)
numElements = 1; numElements = 1;
var->allocatedElements = (uint32_t) numElements; var->allocatedElements = (uint32_t) numElements;
var->type = type; var->transformNum = transformNum;
var->size = (size == 0) ? type->size : (uint32_t) size; var->size = (uint32_t) size;
if (var->size == 0)
var->size = cxoTransform_getDefaultSize(transformNum);
var->isArray = isArray; var->isArray = isArray;
// determine database type
var->dbType = cxoDbType_fromTransformNum(var->transformNum);
if (!var->dbType) {
Py_DECREF(var);
return NULL;
}
Py_INCREF(var->dbType);
// acquire and initialize DPI variable // acquire and initialize DPI variable
cxoTransform_getTypeInfo(type->transformNum, &oracleTypeNum, cxoTransform_getTypeInfo(transformNum, &oracleTypeNum,
&nativeTypeNum); &var->nativeTypeNum);
if (dpiConn_newVar(cursor->connection->handle, oracleTypeNum, if (dpiConn_newVar(cursor->connection->handle, oracleTypeNum,
nativeTypeNum, var->allocatedElements, var->size, 0, isArray, var->nativeTypeNum, var->allocatedElements, var->size, 0, isArray,
typeHandle, &var->handle, &var->data) < 0) { typeHandle, &var->handle, &var->data) < 0) {
cxoError_raiseAndReturnNull(); cxoError_raiseAndReturnNull();
Py_DECREF(var); Py_DECREF(var);
@ -174,6 +162,7 @@ static void cxoVar_free(cxoVar *var)
Py_CLEAR(var->inConverter); Py_CLEAR(var->inConverter);
Py_CLEAR(var->outConverter); Py_CLEAR(var->outConverter);
Py_CLEAR(var->objectType); Py_CLEAR(var->objectType);
Py_CLEAR(var->dbType);
Py_TYPE(var)->tp_free((PyObject*) var); Py_TYPE(var)->tp_free((PyObject*) var);
} }
@ -184,29 +173,7 @@ static void cxoVar_free(cxoVar *var)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
int cxoVar_check(PyObject *object) int cxoVar_check(PyObject *object)
{ {
PyTypeObject *objectType = Py_TYPE(object); return (Py_TYPE(object) == &cxoPyTypeVar);
return (objectType == &cxoPyTypeBfileVar ||
objectType == &cxoPyTypeBinaryVar ||
objectType == &cxoPyTypeBlobVar ||
objectType == &cxoPyTypeBooleanVar ||
objectType == &cxoPyTypeClobVar ||
objectType == &cxoPyTypeCursorVar ||
objectType == &cxoPyTypeDateTimeVar ||
objectType == &cxoPyTypeFixedCharVar ||
objectType == &cxoPyTypeFixedNcharVar ||
objectType == &cxoPyTypeIntervalVar ||
objectType == &cxoPyTypeLongBinaryVar ||
objectType == &cxoPyTypeLongStringVar ||
objectType == &cxoPyTypeNativeFloatVar ||
objectType == &cxoPyTypeNativeIntVar ||
objectType == &cxoPyTypeNcharVar ||
objectType == &cxoPyTypeNclobVar ||
objectType == &cxoPyTypeNumberVar ||
objectType == &cxoPyTypeObjectVar ||
objectType == &cxoPyTypeRowidVar ||
objectType == &cxoPyTypeStringVar ||
objectType == &cxoPyTypeTimestampVar);
} }
@ -219,7 +186,7 @@ cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
{ {
PyObject *result, *inputTypeHandler = NULL; PyObject *result, *inputTypeHandler = NULL;
cxoObjectType *objType = NULL; cxoObjectType *objType = NULL;
cxoVarType *varType; cxoTransformNum transformNum;
Py_ssize_t size; Py_ssize_t size;
cxoObject *obj; cxoObject *obj;
int isArray; int isArray;
@ -253,15 +220,15 @@ cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
} }
// default processing // default processing
varType = cxoVarType_fromPythonValue(value, if (cxoTransform_getNumFromValue(value, &isArray, &size, &numElements,
&isArray, &size, &numElements, cursor->stmtInfo.isPLSQL); cursor->stmtInfo.isPLSQL, &transformNum) < 0)
if (!varType)
return NULL; return NULL;
if (varType->transformNum == CXO_TRANSFORM_OBJECT) { if (transformNum == CXO_TRANSFORM_OBJECT) {
obj = (cxoObject*) value; obj = (cxoObject*) value;
objType = obj->objectType; objType = obj->objectType;
} }
return cxoVar_new(cursor, numElements, varType, size, isArray, objType); return cxoVar_new(cursor, numElements, transformNum, size, isArray,
objType);
} }
@ -273,8 +240,8 @@ static cxoVar *cxoVar_newArrayByType(cxoCursor *cursor,
PyObject *value) PyObject *value)
{ {
PyObject *typeObj, *numElementsObj; PyObject *typeObj, *numElementsObj;
cxoTransformNum transformNum;
cxoObjectType *objType; cxoObjectType *objType;
cxoVarType *varType;
uint32_t numElements; uint32_t numElements;
int ok; int ok;
@ -282,9 +249,6 @@ static cxoVar *cxoVar_newArrayByType(cxoCursor *cursor,
ok = (PyList_GET_SIZE(value) == 2); ok = (PyList_GET_SIZE(value) == 2);
if (ok) { if (ok) {
typeObj = PyList_GET_ITEM(value, 0); typeObj = PyList_GET_ITEM(value, 0);
ok = PyType_Check(typeObj);
}
if (ok) {
numElementsObj = PyList_GET_ITEM(value, 1); numElementsObj = PyList_GET_ITEM(value, 1);
ok = PyLong_Check(numElementsObj); ok = PyLong_Check(numElementsObj);
} }
@ -295,13 +259,12 @@ static cxoVar *cxoVar_newArrayByType(cxoCursor *cursor,
} }
// create variable // create variable
varType = cxoVarType_fromPythonType(typeObj, &objType); if (cxoTransform_getNumFromType(typeObj, &transformNum, &objType) < 0)
if (!varType)
return NULL; return NULL;
numElements = PyLong_AsLong(numElementsObj); numElements = PyLong_AsLong(numElementsObj);
if (PyErr_Occurred()) if (PyErr_Occurred())
return NULL; return NULL;
return cxoVar_new(cursor, numElements, varType, varType->size, 1, objType); return cxoVar_new(cursor, numElements, transformNum, 0, 1, objType);
} }
@ -312,8 +275,8 @@ static cxoVar *cxoVar_newArrayByType(cxoCursor *cursor,
cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value, cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
uint32_t numElements) uint32_t numElements)
{ {
cxoTransformNum transformNum;
cxoObjectType *objType; cxoObjectType *objType;
cxoVarType *varType;
long size; long size;
// passing an integer is assumed to be a string // passing an integer is assumed to be a string
@ -321,9 +284,8 @@ cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
size = PyLong_AsLong(value); size = PyLong_AsLong(value);
if (PyErr_Occurred()) if (PyErr_Occurred())
return NULL; return NULL;
varType = cxoVarType_fromPythonType((PyObject*) &PyUnicode_Type, return cxoVar_new(cursor, numElements, CXO_TRANSFORM_STRING, size, 0,
&objType); NULL);
return cxoVar_new(cursor, numElements, varType, size, 0, objType);
} }
// passing an array of two elements to define an array // passing an array of two elements to define an array
@ -336,11 +298,11 @@ cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
return (cxoVar*) value; return (cxoVar*) value;
} }
// everything else ought to be a Python type or object type // everything else ought to be a Python type, database type constant or
varType = cxoVarType_fromPythonType(value, &objType); // object type
if (!varType) if (cxoTransform_getNumFromType(value, &transformNum, &objType) < 0)
return NULL; return NULL;
return cxoVar_new(cursor, numElements, varType, varType->size, 0, objType); return cxoVar_new(cursor, numElements, transformNum, 0, 0, objType);
} }
@ -426,10 +388,10 @@ PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos)
else data = &var->data[arrayPos]; else data = &var->data[arrayPos];
if (data->isNull) if (data->isNull)
Py_RETURN_NONE; Py_RETURN_NONE;
value = cxoTransform_toPython(var->type->transformNum, var->connection, value = cxoTransform_toPython(var->transformNum, var->connection,
var->objectType, &data->value, var->encodingErrors); var->objectType, &data->value, var->encodingErrors);
if (value) { if (value) {
switch (var->type->transformNum) { switch (var->transformNum) {
case CXO_TRANSFORM_BFILE: case CXO_TRANSFORM_BFILE:
case CXO_TRANSFORM_BLOB: case CXO_TRANSFORM_BLOB:
case CXO_TRANSFORM_CLOB: case CXO_TRANSFORM_CLOB:
@ -491,7 +453,7 @@ static int cxoVar_setValueBytes(cxoVar *var, uint32_t pos, dpiData *data,
int status; int status;
if (buffer->size > var->bufferSize) { if (buffer->size > var->bufferSize) {
cxoTransform_getTypeInfo(var->type->transformNum, &oracleTypeNum, cxoTransform_getTypeInfo(var->transformNum, &oracleTypeNum,
&nativeTypeNum); &nativeTypeNum);
if (dpiConn_newVar(var->connection->handle, oracleTypeNum, if (dpiConn_newVar(var->connection->handle, oracleTypeNum,
nativeTypeNum, var->allocatedElements, buffer->size, 0, nativeTypeNum, var->allocatedElements, buffer->size, 0,
@ -605,18 +567,18 @@ static int cxoVar_setSingleValue(cxoVar *var, uint32_t arrayPos,
data = &var->data[arrayPos]; data = &var->data[arrayPos];
data->isNull = (value == Py_None); data->isNull = (value == Py_None);
if (!data->isNull) { if (!data->isNull) {
if (var->type->transformNum == CXO_TRANSFORM_CURSOR) if (var->transformNum == CXO_TRANSFORM_CURSOR)
result = cxoVar_setValueCursor(var, arrayPos, data, value); result = cxoVar_setValueCursor(var, arrayPos, data, value);
else { else {
cxoBuffer_init(&buffer); cxoBuffer_init(&buffer);
if (var->type->size > 0) if (var->nativeTypeNum == DPI_NATIVE_TYPE_BYTES)
dbValue = &tempDbValue; dbValue = &tempDbValue;
else dbValue = &data->value; else dbValue = &data->value;
result = cxoTransform_fromPython(var->type->transformNum, result = cxoTransform_fromPython(var->transformNum,
&nativeTypeNum, value, dbValue, &buffer, &nativeTypeNum, value, dbValue, &buffer,
var->connection->encodingInfo.encoding, var->connection->encodingInfo.encoding,
var->connection->encodingInfo.nencoding, var, arrayPos); var->connection->encodingInfo.nencoding, var, arrayPos);
if (result == 0 && var->type->size > 0) if (result == 0 && var->nativeTypeNum == DPI_NATIVE_TYPE_BYTES)
result = cxoVar_setValueBytes(var, arrayPos, data, &buffer); result = cxoVar_setValueBytes(var, arrayPos, data, &buffer);
cxoBuffer_clear(&buffer); cxoBuffer_clear(&buffer);
} }
@ -762,13 +724,30 @@ static PyObject *cxoVar_externalGetValues(cxoVar *var, void *unused)
} }
//-----------------------------------------------------------------------------
// cxoVar_getType()
// Return the type associated with the variable. This is either an object
// type or one of the database type constants.
//-----------------------------------------------------------------------------
static PyObject *cxoVar_getType(cxoVar *var, void *unused)
{
if (var->objectType) {
Py_INCREF(var->objectType);
return (PyObject*) var->objectType;
}
Py_INCREF(var->dbType);
return (PyObject*) var->dbType;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// cxoVar_repr() // cxoVar_repr()
// Return a string representation of the variable. // Return a string representation of the variable.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static PyObject *cxoVar_repr(cxoVar *var) static PyObject *cxoVar_repr(cxoVar *var)
{ {
PyObject *value, *module, *name, *result; PyObject *value, *module, *name, *result, *typeName;
uint32_t numElements; uint32_t numElements;
if (var->isArray) { if (var->isArray) {
@ -780,14 +759,22 @@ static PyObject *cxoVar_repr(cxoVar *var)
else value = cxoVar_getArrayValue(var, var->allocatedElements, NULL); else value = cxoVar_getArrayValue(var, var->allocatedElements, NULL);
if (!value) if (!value)
return NULL; return NULL;
if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) { typeName = PyUnicode_DecodeASCII(var->dbType->name,
strlen(var->dbType->name), NULL);
if (!typeName) {
Py_DECREF(value); Py_DECREF(value);
return NULL; return NULL;
} }
result = cxoUtils_formatString("<%s.%s with value %r>", if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) {
PyTuple_Pack(3, module, name, value)); Py_DECREF(typeName);
Py_DECREF(value);
return NULL;
}
result = cxoUtils_formatString("<%s.%s of type %s with value %r>",
PyTuple_Pack(4, module, name, typeName, value));
Py_DECREF(module); Py_DECREF(module);
Py_DECREF(name); Py_DECREF(name);
Py_DECREF(value); Py_DECREF(value);
Py_DECREF(typeName);
return result; return result;
} }

View File

@ -1,296 +0,0 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// cxoVarType.c
// Defines variable types for various transformations.
//-----------------------------------------------------------------------------
#include "cxoModule.h"
// define variable types for each of the supported transforms
static cxoVarType cxoAllVarTypes[] = {
{
CXO_TRANSFORM_NONE,
&cxoPyTypeStringVar,
1
},
{
CXO_TRANSFORM_BINARY,
&cxoPyTypeBinaryVar,
4000
},
{
CXO_TRANSFORM_BFILE,
&cxoPyTypeBfileVar,
0
},
{
CXO_TRANSFORM_BLOB,
&cxoPyTypeBlobVar,
0
},
{
CXO_TRANSFORM_BOOLEAN,
&cxoPyTypeBooleanVar,
0
},
{
CXO_TRANSFORM_CLOB,
&cxoPyTypeClobVar,
0
},
{
CXO_TRANSFORM_CURSOR,
&cxoPyTypeCursorVar,
0
},
{
CXO_TRANSFORM_DATE,
&cxoPyTypeDateTimeVar,
0
},
{
CXO_TRANSFORM_DATETIME,
&cxoPyTypeDateTimeVar,
0
},
{
CXO_TRANSFORM_DECIMAL,
&cxoPyTypeNumberVar,
1000
},
{
CXO_TRANSFORM_FIXED_CHAR,
&cxoPyTypeFixedCharVar,
2000
},
{
CXO_TRANSFORM_FIXED_NCHAR,
&cxoPyTypeFixedNcharVar,
2000
},
{
CXO_TRANSFORM_FLOAT,
&cxoPyTypeNumberVar,
1000
},
{
CXO_TRANSFORM_INT,
&cxoPyTypeNumberVar,
1000
},
{
CXO_TRANSFORM_LONG_BINARY,
&cxoPyTypeLongBinaryVar,
128 * 1024
},
{
CXO_TRANSFORM_LONG_STRING,
&cxoPyTypeLongStringVar,
128 * 1024
},
{
CXO_TRANSFORM_NATIVE_DOUBLE,
&cxoPyTypeNativeFloatVar,
0
},
{
CXO_TRANSFORM_NATIVE_FLOAT,
&cxoPyTypeNativeFloatVar,
0
},
{
CXO_TRANSFORM_NATIVE_INT,
&cxoPyTypeNativeIntVar,
0
},
{
CXO_TRANSFORM_NCLOB,
&cxoPyTypeNclobVar,
0
},
{
CXO_TRANSFORM_NSTRING,
&cxoPyTypeNcharVar,
4000
},
{
CXO_TRANSFORM_OBJECT,
&cxoPyTypeObjectVar,
0
},
{
CXO_TRANSFORM_ROWID,
&cxoPyTypeRowidVar,
0
},
{
CXO_TRANSFORM_STRING,
&cxoPyTypeStringVar,
4000
},
{
CXO_TRANSFORM_TIMEDELTA,
&cxoPyTypeIntervalVar,
0
},
{
CXO_TRANSFORM_TIMESTAMP,
&cxoPyTypeTimestampVar,
0
},
{
CXO_TRANSFORM_TIMESTAMP_LTZ,
&cxoPyTypeTimestampVar,
0
}
};
//-----------------------------------------------------------------------------
// cxoVarType_fromDataTypeInfo()
// Return a variable type given query metadata, or NULL indicating that the
// data indicated by the query metadata is not supported.
//-----------------------------------------------------------------------------
cxoVarType *cxoVarType_fromDataTypeInfo(dpiDataTypeInfo *info)
{
cxoTransformNum transformNum;
char message[120];
transformNum = cxoTransform_getNumFromDataTypeInfo(info);
if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message), "Oracle type %d not supported.",
info->oracleTypeNum);
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
return NULL;
}
return &cxoAllVarTypes[transformNum];
}
//-----------------------------------------------------------------------------
// cxoVarType_fromPythonType()
// Return a variable type given a Python type object or NULL if the Python
// type does not have a corresponding variable type. If the type provided is an
// object type, return that as well.
//-----------------------------------------------------------------------------
cxoVarType *cxoVarType_fromPythonType(PyObject *type, cxoObjectType **objType)
{
cxoTransformNum transformNum;
PyTypeObject *pyType;
char message[250];
if (Py_TYPE(type) == &cxoPyTypeObjectType) {
transformNum = CXO_TRANSFORM_OBJECT;
*objType = (cxoObjectType*) type;
} else if (Py_TYPE(type) != &PyType_Type) {
PyErr_SetString(PyExc_TypeError, "expecting type");
return NULL;
} else {
*objType = NULL;
pyType = (PyTypeObject*) type;
transformNum = cxoTransform_getNumFromType(pyType);
if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message), "Python type %s not supported.",
pyType->tp_name);
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
return NULL;
}
}
return &cxoAllVarTypes[transformNum];
}
//-----------------------------------------------------------------------------
// cxoVarType_calculateSize()
// Calculate the size to use with the specified transform and Python value.
// This function is only called by cxoVarType_fromPythonValue() and no attempt
// is made to verify the value further.
//-----------------------------------------------------------------------------
static Py_ssize_t cxoVarType_calculateSize(PyObject *value,
cxoTransformNum transformNum)
{
Py_ssize_t size = 0;
switch (transformNum) {
case CXO_TRANSFORM_NONE:
return 1;
case CXO_TRANSFORM_BINARY:
return PyBytes_GET_SIZE(value);
case CXO_TRANSFORM_NSTRING:
size = PyUnicode_GET_SIZE(value);
return (size == 0) ? 1 : size;
case CXO_TRANSFORM_STRING:
size = PyUnicode_GET_SIZE(value);
return (size == 0) ? 1 : size;
default:
break;
}
return 0;
}
//-----------------------------------------------------------------------------
// cxoVarType_fromPythonValue()
// Return a variable type given a Python object or NULL if the Python object
// does not have a corresponding variable type.
//-----------------------------------------------------------------------------
cxoVarType *cxoVarType_fromPythonValue(PyObject *value, int *isArray,
Py_ssize_t *size, Py_ssize_t *numElements, int plsql)
{
cxoTransformNum transformNum, tempTransformNum;
PyObject *elementValue;
Py_ssize_t i, tempSize;
char message[250];
// initialization (except numElements which always has a valid value and is
// only overridden when a an array is encountered)
*size = 0;
*isArray = 0;
// handle arrays
if (PyList_Check(value)) {
transformNum = CXO_TRANSFORM_NONE;
for (i = 0; i < PyList_GET_SIZE(value); i++) {
elementValue = PyList_GET_ITEM(value, i);
tempTransformNum = cxoTransform_getNumFromValue(elementValue, 1);
if (tempTransformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message),
"element %u value is unsupported", (unsigned) i);
cxoError_raiseFromString(cxoNotSupportedErrorException,
message);
return NULL;
} else if (transformNum == CXO_TRANSFORM_NONE) {
transformNum = tempTransformNum;
} else if (transformNum != tempTransformNum) {
snprintf(message, sizeof(message),
"element %u value is not the same type as previous "
"elements", (unsigned) i);
cxoError_raiseFromString(cxoNotSupportedErrorException,
message);
return NULL;
}
tempSize = cxoVarType_calculateSize(elementValue,
tempTransformNum);
if (tempSize > *size)
*size = tempSize;
}
*isArray = 1;
*numElements = PyList_GET_SIZE(value);
return &cxoAllVarTypes[transformNum];
}
// handle scalar values
transformNum = cxoTransform_getNumFromValue(value, plsql);
if (transformNum == CXO_TRANSFORM_UNSUPPORTED) {
snprintf(message, sizeof(message),
"Python value of type %s not supported.",
Py_TYPE(value)->tp_name);
cxoError_raiseFromString(cxoNotSupportedErrorException, message);
return NULL;
}
*size = cxoVarType_calculateSize(value, transformNum);
return &cxoAllVarTypes[transformNum];
}

View File

@ -31,7 +31,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutFalse(self): def testBindOutFalse(self):
"test binding out a boolean value (False)" "test binding out a boolean value (False)"
result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10", result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10",
cx_Oracle.BOOLEAN, (15,)) cx_Oracle.DB_TYPE_BOOLEAN, (15,))
self.assertEqual(result, False) self.assertEqual(result, False)
def testBindOutTrue(self): def testBindOutTrue(self):

View File

@ -191,7 +191,7 @@ class TestCase(TestEnv.BaseTestCase):
encoding="UTF-8", nencoding="UTF-16") encoding="UTF-8", nencoding="UTF-16")
value = u"\u03b4\u4e2a" value = u"\u03b4\u4e2a"
cursor = connection.cursor() cursor = connection.cursor()
ncharVar = cursor.var(cx_Oracle.NCHAR, 100) ncharVar = cursor.var(cx_Oracle.DB_TYPE_NVARCHAR, 100)
ncharVar.setvalue(0, value) ncharVar.setvalue(0, value)
cursor.execute("select :value from dual", value = ncharVar) cursor.execute("select :value from dual", value = ncharVar)
result, = cursor.fetchone() result, = cursor.fetchone()

View File

@ -613,7 +613,8 @@ class TestCase(TestEnv.BaseTestCase):
self.cursor.parse(sql) self.cursor.parse(sql)
self.assertEqual(self.cursor.statement, sql) self.assertEqual(self.cursor.statement, sql)
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('LONGINTCOL', cx_Oracle.NUMBER, 17, None, 16, 0, 0) ]) [ ('LONGINTCOL', cx_Oracle.DB_TYPE_NUMBER, 17, None, 16, 0,
0) ])
def testSetOutputSize(self): def testSetOutputSize(self):
"test cursor.setoutputsize() does not fail (but does nothing)" "test cursor.setoutputsize() does not fail (but does nothing)"

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
# #
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
# #
@ -26,7 +26,7 @@ class TestCase(TestEnv.BaseTestCase):
end;""", end;""",
cursor = cursor) cursor = cursor)
self.assertEqual(cursor.description, self.assertEqual(cursor.description,
[ ('STRINGVALUE', cx_Oracle.FIXED_CHAR, 1, [ ('STRINGVALUE', cx_Oracle.DB_TYPE_CHAR, 1,
TestEnv.GetCharSetRatio(), None, None, 1) ]) TestEnv.GetCharSetRatio(), None, None, 1) ])
self.assertEqual(cursor.fetchall(), [('X',)]) self.assertEqual(cursor.fetchall(), [('X',)])
@ -36,8 +36,8 @@ class TestCase(TestEnv.BaseTestCase):
self.assertEqual(cursor.description, None) self.assertEqual(cursor.description, None)
self.cursor.callproc("pkg_TestRefCursors.TestOutCursor", (2, cursor)) self.cursor.callproc("pkg_TestRefCursors.TestOutCursor", (2, cursor))
self.assertEqual(cursor.description, self.assertEqual(cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('STRINGCOL', cx_Oracle.STRING, 20, 20 * ('STRINGCOL', cx_Oracle.DB_TYPE_VARCHAR, 20, 20 *
TestEnv.GetCharSetRatio(), None, None, 0) ]) TestEnv.GetCharSetRatio(), None, None, 0) ])
self.assertEqual(cursor.fetchall(), self.assertEqual(cursor.fetchall(),
[ (1, 'String 1'), (2, 'String 2') ]) [ (1, 'String 1'), (2, 'String 2') ])
@ -80,9 +80,9 @@ class TestCase(TestEnv.BaseTestCase):
from TestNumbers from TestNumbers
order by IntCol""") order by IntCol""")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('CURSORVALUE', cx_Oracle.CURSOR, None, None, None, None, ('CURSORVALUE', cx_Oracle.DB_TYPE_CURSOR, None, None, None,
1) ]) None, 1) ])
for i in range(1, 11): for i in range(1, 11):
number, cursor = self.cursor.fetchone() number, cursor = self.cursor.fetchone()
self.assertEqual(number, i) self.assertEqual(number, i)
@ -90,4 +90,3 @@ class TestCase(TestEnv.BaseTestCase):
if __name__ == "__main__": if __name__ == "__main__":
TestEnv.RunTestCases() TestEnv.RunTestCases()

View File

@ -174,7 +174,8 @@ class TestCase(TestEnv.BaseTestCase):
stringValue = "The string that will be verified" stringValue = "The string that will be verified"
obj = typeObj.newobject() obj = typeObj.newobject()
obj.STRINGVALUE = stringValue obj.STRINGVALUE = stringValue
outVar = self.cursor.var(cx_Oracle.OBJECT, typename = "UDT_OBJECT") outVar = self.cursor.var(cx_Oracle.DB_TYPE_OBJECT,
typename = "UDT_OBJECT")
self.cursor.execute(""" self.cursor.execute("""
insert into TestObjects (IntCol, ObjectCol) insert into TestObjects (IntCol, ObjectCol)
values (4, :obj) values (4, :obj)

View File

@ -215,9 +215,9 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate" "test cursor description is accurate"
self.cursor.execute("select * from TestDates") self.cursor.execute("select * from TestDates")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('DATECOL', cx_Oracle.DATETIME, 23, None, None, None, 0), ('DATECOL', cx_Oracle.DB_TYPE_DATE, 23, None, None, None, 0),
('NULLABLECOL', cx_Oracle.DATETIME, 23, None, None, None, ('NULLABLECOL', cx_Oracle.DB_TYPE_DATE, 23, None, None, None,
1) ]) 1) ])
def testFetchAll(self): def testFetchAll(self):

136
test/DbTypes.py Normal file
View File

@ -0,0 +1,136 @@
#------------------------------------------------------------------------------
# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
#------------------------------------------------------------------------------
"""
Module for testing comparisons with database types and API types. This also
contains tests for comparisons with database types and variable types, for
backwards compatibility.
"""
import TestEnv
import cx_Oracle
class TestCase(TestEnv.BaseTestCase):
def __testCompare(self, dbType, apiType):
self.assertEqual(dbType, dbType)
self.assertEqual(dbType, apiType)
self.assertEqual(apiType, dbType)
self.assertNotEqual(dbType, 5)
self.assertNotEqual(dbType, cx_Oracle.DB_TYPE_OBJECT)
def testBfile(self):
"test cx_Oracle.DB_TYPE_BFILE comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_BFILE, cx_Oracle.BINARY)
self.assertEqual(cx_Oracle.DB_TYPE_BFILE, cx_Oracle.BFILE)
def testBinaryDouble(self):
"test cx_Oracle.DB_TYPE_BINARY_DOUBLE comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_BINARY_DOUBLE, cx_Oracle.NUMBER)
self.assertEqual(cx_Oracle.DB_TYPE_BINARY_DOUBLE,
cx_Oracle.NATIVE_FLOAT)
def testBinaryFloat(self):
"test cx_Oracle.DB_TYPE_BINARY_FLOAT comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_BINARY_FLOAT, cx_Oracle.NUMBER)
def testBinaryInteger(self):
"test cx_Oracle.DB_TYPE_BINARY_INTEGER comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_BINARY_INTEGER, cx_Oracle.NUMBER)
self.assertEqual(cx_Oracle.DB_TYPE_BINARY_INTEGER,
cx_Oracle.NATIVE_INT)
def testBlob(self):
"test cx_Oracle.DB_TYPE_BLOB comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_BLOB, cx_Oracle.BINARY)
self.assertEqual(cx_Oracle.DB_TYPE_BLOB, cx_Oracle.BLOB)
def testBoolean(self):
"test cx_Oracle.DB_TYPE_BOOLEAN comparisons"
self.assertEqual(cx_Oracle.DB_TYPE_BOOLEAN, cx_Oracle.BOOLEAN)
def testChar(self):
"test cx_Oracle.DB_TYPE_CHAR comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_CHAR, cx_Oracle.STRING)
self.assertEqual(cx_Oracle.DB_TYPE_CHAR, cx_Oracle.FIXED_CHAR)
def testClob(self):
"test cx_Oracle.DB_TYPE_CLOB comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_CLOB, cx_Oracle.STRING)
self.assertEqual(cx_Oracle.DB_TYPE_CLOB, cx_Oracle.CLOB)
def testCursor(self):
"test cx_Oracle.DB_TYPE_CURSOR comparisons"
self.assertEqual(cx_Oracle.DB_TYPE_CURSOR, cx_Oracle.CURSOR)
def testDate(self):
"test cx_Oracle.DB_TYPE_DATE comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_DATE, cx_Oracle.DATETIME)
def testIntervalDS(self):
"test cx_Oracle.DB_TYPE_INTERVAL_DS comparisons"
self.assertEqual(cx_Oracle.DB_TYPE_INTERVAL_DS, cx_Oracle.INTERVAL)
def testLong(self):
"test cx_Oracle.DB_TYPE_LONG comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_LONG, cx_Oracle.STRING)
self.assertEqual(cx_Oracle.DB_TYPE_LONG, cx_Oracle.LONG_STRING)
def testLongRaw(self):
"test cx_Oracle.DB_TYPE_LONG_RAW comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_LONG_RAW, cx_Oracle.BINARY)
self.assertEqual(cx_Oracle.DB_TYPE_LONG_RAW, cx_Oracle.LONG_BINARY)
def testNchar(self):
"test cx_Oracle.DB_TYPE_NCHAR comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_NCHAR, cx_Oracle.STRING)
self.assertEqual(cx_Oracle.DB_TYPE_NCHAR, cx_Oracle.FIXED_NCHAR)
def testNclob(self):
"test cx_Oracle.DB_TYPE_NCLOB comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_NCLOB, cx_Oracle.STRING)
self.assertEqual(cx_Oracle.DB_TYPE_NCLOB, cx_Oracle.NCLOB)
def testNumber(self):
"test cx_Oracle.DB_TYPE_NUMBER comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_NUMBER, cx_Oracle.NUMBER)
def testNvarchar(self):
"test cx_Oracle.DB_TYPE_NVARCHAR comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_NVARCHAR, cx_Oracle.STRING)
self.assertEqual(cx_Oracle.DB_TYPE_NVARCHAR, cx_Oracle.NCHAR)
def testObject(self):
"test cx_Oracle.DB_TYPE_OBJECT comparisons"
self.assertEqual(cx_Oracle.DB_TYPE_OBJECT, cx_Oracle.OBJECT)
def testRaw(self):
"test cx_Oracle.DB_TYPE_RAW comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_RAW, cx_Oracle.BINARY)
def testRowid(self):
"test cx_Oracle.DB_TYPE_ROWID comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_ROWID, cx_Oracle.ROWID)
def testTimestamp(self):
"test cx_Oracle.DB_TYPE_TIMESTAMP comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_TIMESTAMP, cx_Oracle.DATETIME)
self.assertEqual(cx_Oracle.DB_TYPE_TIMESTAMP, cx_Oracle.TIMESTAMP)
def testTimestampLTZ(self):
"test cx_Oracle.DB_TYPE_TIMESTAMP_LTZ comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_TIMESTAMP_LTZ, cx_Oracle.DATETIME)
def testTimestampTZ(self):
"test cx_Oracle.DB_TYPE_TIMESTAMP_TZ comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_TIMESTAMP_TZ, cx_Oracle.DATETIME)
def testVarchar(self):
"test cx_Oracle.DB_TYPE_VARCHAR comparisons"
self.__testCompare(cx_Oracle.DB_TYPE_VARCHAR, cx_Oracle.STRING)
if __name__ == "__main__":
TestEnv.RunTestCases()

View File

@ -34,7 +34,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInterval(self): def testBindInterval(self):
"test binding in an interval" "test binding in an interval"
self.cursor.setinputsizes(value = cx_Oracle.INTERVAL) self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_INTERVAL_DS)
self.cursor.execute(""" self.cursor.execute("""
select * from TestIntervals select * from TestIntervals
where IntervalCol = :value""", where IntervalCol = :value""",
@ -44,7 +44,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindNull(self): def testBindNull(self):
"test binding in a null" "test binding in a null"
self.cursor.setinputsizes(value = cx_Oracle.INTERVAL) self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_INTERVAL_DS)
self.cursor.execute(""" self.cursor.execute("""
select * from TestIntervals select * from TestIntervals
where IntervalCol = :value""", where IntervalCol = :value""",
@ -53,7 +53,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutSetInputSizes(self): def testBindOutSetInputSizes(self):
"test binding out with set input sizes defined" "test binding out with set input sizes defined"
vars = self.cursor.setinputsizes(value = cx_Oracle.INTERVAL) vars = self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_INTERVAL_DS)
self.cursor.execute(""" self.cursor.execute("""
begin begin
:value := to_dsinterval('8 09:24:18.123789'); :value := to_dsinterval('8 09:24:18.123789');
@ -64,7 +64,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutSetInputSizes(self): def testBindInOutSetInputSizes(self):
"test binding in/out with set input sizes defined" "test binding in/out with set input sizes defined"
vars = self.cursor.setinputsizes(value = cx_Oracle.INTERVAL) vars = self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_INTERVAL_DS)
self.cursor.execute(""" self.cursor.execute("""
begin begin
:value := :value + to_dsinterval('5 08:30:00'); :value := :value + to_dsinterval('5 08:30:00');
@ -75,7 +75,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutFractionalSecond(self): def testBindInOutFractionalSecond(self):
"test binding in/out with set input sizes defined" "test binding in/out with set input sizes defined"
vars = self.cursor.setinputsizes(value = cx_Oracle.INTERVAL) vars = self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_INTERVAL_DS)
self.cursor.execute(""" self.cursor.execute("""
begin begin
:value := :value + to_dsinterval('5 08:30:00'); :value := :value + to_dsinterval('5 08:30:00');
@ -87,7 +87,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutVar(self): def testBindOutVar(self):
"test binding out with cursor.var() method" "test binding out with cursor.var() method"
var = self.cursor.var(cx_Oracle.INTERVAL) var = self.cursor.var(cx_Oracle.DB_TYPE_INTERVAL_DS)
self.cursor.execute(""" self.cursor.execute("""
begin begin
:value := to_dsinterval('15 18:35:45.586'); :value := to_dsinterval('15 18:35:45.586');
@ -99,7 +99,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutVarDirectSet(self): def testBindInOutVarDirectSet(self):
"test binding in/out with cursor.var() method" "test binding in/out with cursor.var() method"
var = self.cursor.var(cx_Oracle.INTERVAL) var = self.cursor.var(cx_Oracle.DB_TYPE_INTERVAL_DS)
var.setvalue(0, datetime.timedelta(days = 1, minutes = 50)) var.setvalue(0, datetime.timedelta(days = 1, minutes = 50))
self.cursor.execute(""" self.cursor.execute("""
begin begin
@ -113,9 +113,11 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate" "test cursor description is accurate"
self.cursor.execute("select * from TestIntervals") self.cursor.execute("select * from TestIntervals")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('INTERVALCOL', cx_Oracle.INTERVAL, None, None, 2, 6, 0), ('INTERVALCOL', cx_Oracle.DB_TYPE_INTERVAL_DS, None, None, 2,
('NULLABLECOL', cx_Oracle.INTERVAL, None, None, 2, 6, 1) ]) 6, 0),
('NULLABLECOL', cx_Oracle.DB_TYPE_INTERVAL_DS, None, None, 2,
6, 1) ])
def testFetchAll(self): def testFetchAll(self):
"test that fetching all of the data returns the correct results" "test that fetching all of the data returns the correct results"

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
# #
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
# #
@ -117,8 +117,10 @@ class TestCase(TestEnv.BaseTestCase):
def __ValidateQuery(self, rows, lobType): def __ValidateQuery(self, rows, lobType):
longString = "" longString = ""
dbType = getattr(cx_Oracle, "DB_TYPE_" + lobType)
for row in rows: for row in rows:
integerValue, lob = row integerValue, lob = row
self.assertEqual(lob.type, dbType)
if integerValue == 0: if integerValue == 0:
self.assertEqual(lob.size(), 0) self.assertEqual(lob.size(), 0)
expectedValue = "" expectedValue = ""
@ -158,16 +160,17 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate for BLOBs" "test cursor description is accurate for BLOBs"
self.cursor.execute("select * from TestBLOBs") self.cursor.execute("select * from TestBLOBs")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('BLOBCOL', cx_Oracle.BLOB, None, None, None, None, 0) ]) ('BLOBCOL', cx_Oracle.DB_TYPE_BLOB, None, None, None, None,
0) ])
def testBLOBsDirect(self): def testBLOBsDirect(self):
"test binding and fetching BLOB data (directly)" "test binding and fetching BLOB data (directly)"
self.__PerformTest("BLOB", cx_Oracle.BLOB) self.__PerformTest("BLOB", cx_Oracle.DB_TYPE_BLOB)
def testBLOBsIndirect(self): def testBLOBsIndirect(self):
"test binding and fetching BLOB data (indirectly)" "test binding and fetching BLOB data (indirectly)"
self.__PerformTest("BLOB", cx_Oracle.LONG_BINARY) self.__PerformTest("BLOB", cx_Oracle.DB_TYPE_LONG_RAW)
def testBLOBOperations(self): def testBLOBOperations(self):
"test operations on BLOBs" "test operations on BLOBs"
@ -177,16 +180,17 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate for CLOBs" "test cursor description is accurate for CLOBs"
self.cursor.execute("select * from TestCLOBs") self.cursor.execute("select * from TestCLOBs")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('CLOBCOL', cx_Oracle.CLOB, None, None, None, None, 0) ]) ('CLOBCOL', cx_Oracle.DB_TYPE_CLOB, None, None, None, None,
0) ])
def testCLOBsDirect(self): def testCLOBsDirect(self):
"test binding and fetching CLOB data (directly)" "test binding and fetching CLOB data (directly)"
self.__PerformTest("CLOB", cx_Oracle.CLOB) self.__PerformTest("CLOB", cx_Oracle.DB_TYPE_CLOB)
def testCLOBsIndirect(self): def testCLOBsIndirect(self):
"test binding and fetching CLOB data (indirectly)" "test binding and fetching CLOB data (indirectly)"
self.__PerformTest("CLOB", cx_Oracle.LONG_STRING) self.__PerformTest("CLOB", cx_Oracle.DB_TYPE_LONG)
def testCLOBOperations(self): def testCLOBOperations(self):
"test operations on CLOBs" "test operations on CLOBs"
@ -215,12 +219,13 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate for NCLOBs" "test cursor description is accurate for NCLOBs"
self.cursor.execute("select * from TestNCLOBs") self.cursor.execute("select * from TestNCLOBs")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('NCLOBCOL', cx_Oracle.NCLOB, None, None, None, None, 0) ]) ('NCLOBCOL', cx_Oracle.DB_TYPE_NCLOB, None, None, None, None,
0) ])
def testNCLOBsDirect(self): def testNCLOBsDirect(self):
"test binding and fetching NCLOB data (directly)" "test binding and fetching NCLOB data (directly)"
self.__PerformTest("NCLOB", cx_Oracle.NCLOB) self.__PerformTest("NCLOB", cx_Oracle.DB_TYPE_NCLOB)
def testNCLOBDifferentEncodings(self): def testNCLOBDifferentEncodings(self):
"test binding and fetching NCLOB data (different encodings)" "test binding and fetching NCLOB data (different encodings)"
@ -230,11 +235,11 @@ class TestCase(TestEnv.BaseTestCase):
value = u"\u03b4\u4e2a" value = u"\u03b4\u4e2a"
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("truncate table TestNCLOBs") cursor.execute("truncate table TestNCLOBs")
cursor.setinputsizes(val = cx_Oracle.NCHAR) cursor.setinputsizes(val = cx_Oracle.DB_TYPE_NVARCHAR)
cursor.execute("insert into TestNCLOBs values (1, :val)", val = value) cursor.execute("insert into TestNCLOBs values (1, :val)", val = value)
cursor.execute("select NCLOBCol from TestNCLOBs") cursor.execute("select NCLOBCol from TestNCLOBs")
nclob, = cursor.fetchone() nclob, = cursor.fetchone()
cursor.setinputsizes(val = cx_Oracle.NCHAR) cursor.setinputsizes(val = cx_Oracle.DB_TYPE_NVARCHAR)
cursor.execute("update TestNCLOBs set NCLOBCol = :val", cursor.execute("update TestNCLOBs set NCLOBCol = :val",
val = nclob.read() + value) val = nclob.read() + value)
cursor.execute("select NCLOBCol from TestNCLOBs") cursor.execute("select NCLOBCol from TestNCLOBs")
@ -243,7 +248,7 @@ class TestCase(TestEnv.BaseTestCase):
def testNCLOBsIndirect(self): def testNCLOBsIndirect(self):
"test binding and fetching NCLOB data (indirectly)" "test binding and fetching NCLOB data (indirectly)"
self.__PerformTest("NCLOB", cx_Oracle.LONG_STRING) self.__PerformTest("NCLOB", cx_Oracle.DB_TYPE_LONG)
def testNCLOBOperations(self): def testNCLOBOperations(self):
"test operations on NCLOBs" "test operations on NCLOBs"
@ -271,7 +276,7 @@ class TestCase(TestEnv.BaseTestCase):
def testAssignStringBeyondArraySize(self): def testAssignStringBeyondArraySize(self):
"test assign string to NCLOB beyond array size" "test assign string to NCLOB beyond array size"
nclobVar = self.cursor.var(cx_Oracle.NCLOB) nclobVar = self.cursor.var(cx_Oracle.DB_TYPE_NCLOB)
self.assertRaises(IndexError, nclobVar.setvalue, 1, "test char") self.assertRaises(IndexError, nclobVar.setvalue, 1, "test char")
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -59,7 +59,7 @@ class TestCase(TestEnv.BaseTestCase):
def testLongs(self): def testLongs(self):
"test binding and fetching long data" "test binding and fetching long data"
self.__PerformTest("Long", cx_Oracle.LONG_STRING) self.__PerformTest("Long", cx_Oracle.DB_TYPE_LONG)
def testLongWithExecuteMany(self): def testLongWithExecuteMany(self):
"test binding long data with executemany()" "test binding long data with executemany()"
@ -77,23 +77,23 @@ class TestCase(TestEnv.BaseTestCase):
def testLongRaws(self): def testLongRaws(self):
"test binding and fetching long raw data" "test binding and fetching long raw data"
self.__PerformTest("LongRaw", cx_Oracle.LONG_BINARY) self.__PerformTest("LongRaw", cx_Oracle.DB_TYPE_LONG_RAW)
def testLongCursorDescription(self): def testLongCursorDescription(self):
"test cursor description is accurate for longs" "test cursor description is accurate for longs"
self.cursor.execute("select * from TestLongs") self.cursor.execute("select * from TestLongs")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('LONGCOL', cx_Oracle.LONG_STRING, None, None, None, None, ('LONGCOL', cx_Oracle.DB_TYPE_LONG, None, None, None, None,
0) ]) 0) ])
def testLongRawCursorDescription(self): def testLongRawCursorDescription(self):
"test cursor description is accurate for long raws" "test cursor description is accurate for long raws"
self.cursor.execute("select * from TestLongRaws") self.cursor.execute("select * from TestLongRaws")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('LONGRAWCOL', cx_Oracle.LONG_BINARY, None, None, None, None, ('LONGRAWCOL', cx_Oracle.DB_TYPE_LONG_RAW, None, None, None,
0) ]) None, 0) ])
def testArraySizeTooLarge(self): def testArraySizeTooLarge(self):
"test array size too large generates an exception" "test array size too large generates an exception"

View File

@ -43,7 +43,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindUnicode(self): def testBindUnicode(self):
"test binding in a unicode" "test binding in a unicode"
self.cursor.setinputsizes(value = cx_Oracle.NCHAR) self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_NVARCHAR)
self.cursor.execute(""" self.cursor.execute("""
select * from TestUnicodes select * from TestUnicodes
where UnicodeCol = :value""", where UnicodeCol = :value""",
@ -52,8 +52,8 @@ class TestCase(TestEnv.BaseTestCase):
def testBindDifferentVar(self): def testBindDifferentVar(self):
"test binding a different variable on second execution" "test binding a different variable on second execution"
retval_1 = self.cursor.var(cx_Oracle.NCHAR, 30) retval_1 = self.cursor.var(cx_Oracle.DB_TYPE_NVARCHAR, 30)
retval_2 = self.cursor.var(cx_Oracle.NCHAR, 30) retval_2 = self.cursor.var(cx_Oracle.DB_TYPE_NVARCHAR, 30)
self.cursor.execute(r"begin :retval := unistr('Called \3042'); end;", self.cursor.execute(r"begin :retval := unistr('Called \3042'); end;",
retval = retval_1) retval = retval_1)
self.assertEqual(retval_1.getvalue(), u"Called \u3042") self.assertEqual(retval_1.getvalue(), u"Called \u3042")
@ -63,7 +63,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindUnicodeAfterNumber(self): def testBindUnicodeAfterNumber(self):
"test binding in a unicode after setting input sizes to a number" "test binding in a unicode after setting input sizes to a number"
unicodeVal = self.cursor.var(cx_Oracle.NCHAR) unicodeVal = self.cursor.var(cx_Oracle.DB_TYPE_NVARCHAR)
unicodeVal.setvalue(0, u"Unicode \u3042 6") unicodeVal.setvalue(0, u"Unicode \u3042 6")
self.cursor.setinputsizes(value = cx_Oracle.NUMBER) self.cursor.setinputsizes(value = cx_Oracle.NUMBER)
self.cursor.execute(""" self.cursor.execute("""
@ -76,7 +76,7 @@ class TestCase(TestEnv.BaseTestCase):
"test binding in a unicode array" "test binding in a unicode array"
returnValue = self.cursor.var(cx_Oracle.NUMBER) returnValue = self.cursor.var(cx_Oracle.NUMBER)
array = [r[1] for r in self.rawData] array = [r[1] for r in self.rawData]
arrayVar = self.cursor.arrayvar(cx_Oracle.NCHAR, array) arrayVar = self.cursor.arrayvar(cx_Oracle.DB_TYPE_NVARCHAR, array)
statement = """ statement = """
begin begin
:retval := pkg_TestUnicodeArrays.TestInArrays( :retval := pkg_TestUnicodeArrays.TestInArrays(
@ -88,7 +88,7 @@ class TestCase(TestEnv.BaseTestCase):
array = arrayVar) array = arrayVar)
self.assertEqual(returnValue.getvalue(), 116) self.assertEqual(returnValue.getvalue(), 116)
array = [ u"Unicode - \u3042 %d" % i for i in range(15) ] array = [ u"Unicode - \u3042 %d" % i for i in range(15) ]
arrayVar = self.cursor.arrayvar(cx_Oracle.NCHAR, array) arrayVar = self.cursor.arrayvar(cx_Oracle.DB_TYPE_NVARCHAR, array)
self.cursor.execute(statement, self.cursor.execute(statement,
integerValue = 8, integerValue = 8,
array = arrayVar) array = arrayVar)
@ -97,7 +97,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindUnicodeArrayBySizes(self): def testBindUnicodeArrayBySizes(self):
"test binding in a unicode array (with setinputsizes)" "test binding in a unicode array (with setinputsizes)"
returnValue = self.cursor.var(cx_Oracle.NUMBER) returnValue = self.cursor.var(cx_Oracle.NUMBER)
self.cursor.setinputsizes(array = [cx_Oracle.NCHAR, 10]) self.cursor.setinputsizes(array = [cx_Oracle.DB_TYPE_NVARCHAR, 10])
array = [r[1] for r in self.rawData] array = [r[1] for r in self.rawData]
self.cursor.execute(""" self.cursor.execute("""
begin begin
@ -112,7 +112,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindUnicodeArrayByVar(self): def testBindUnicodeArrayByVar(self):
"test binding in a unicode array (with arrayvar)" "test binding in a unicode array (with arrayvar)"
returnValue = self.cursor.var(cx_Oracle.NUMBER) returnValue = self.cursor.var(cx_Oracle.NUMBER)
array = self.cursor.arrayvar(cx_Oracle.NCHAR, 10, 20) array = self.cursor.arrayvar(cx_Oracle.DB_TYPE_NVARCHAR, 10, 20)
array.setvalue(0, [r[1] for r in self.rawData]) array.setvalue(0, [r[1] for r in self.rawData])
self.cursor.execute(""" self.cursor.execute("""
begin begin
@ -126,7 +126,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutUnicodeArrayByVar(self): def testBindInOutUnicodeArrayByVar(self):
"test binding in/out a unicode array (with arrayvar)" "test binding in/out a unicode array (with arrayvar)"
array = self.cursor.arrayvar(cx_Oracle.NCHAR, 10, 100) array = self.cursor.arrayvar(cx_Oracle.DB_TYPE_NVARCHAR, 10, 100)
originalData = [r[1] for r in self.rawData] originalData = [r[1] for r in self.rawData]
format = u"Converted element \u3042 # %d originally had length %d" format = u"Converted element \u3042 # %d originally had length %d"
expectedData = [format % (i, len(originalData[i - 1])) \ expectedData = [format % (i, len(originalData[i - 1])) \
@ -142,7 +142,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutUnicodeArrayByVar(self): def testBindOutUnicodeArrayByVar(self):
"test binding out a unicode array (with arrayvar)" "test binding out a unicode array (with arrayvar)"
array = self.cursor.arrayvar(cx_Oracle.NCHAR, 6, 100) array = self.cursor.arrayvar(cx_Oracle.DB_TYPE_NVARCHAR, 6, 100)
format = u"Test out element \u3042 # %d" format = u"Test out element \u3042 # %d"
expectedData = [format % i for i in range(1, 7)] expectedData = [format % i for i in range(1, 7)]
self.cursor.execute(""" self.cursor.execute("""
@ -163,7 +163,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutSetInputSizesByType(self): def testBindOutSetInputSizesByType(self):
"test binding out with set input sizes defined (by type)" "test binding out with set input sizes defined (by type)"
vars = self.cursor.setinputsizes(value = cx_Oracle.NCHAR) vars = self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_NVARCHAR)
self.cursor.execute(r""" self.cursor.execute(r"""
begin begin
:value := unistr('TSI \3042'); :value := unistr('TSI \3042');
@ -172,7 +172,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutSetInputSizesByType(self): def testBindInOutSetInputSizesByType(self):
"test binding in/out with set input sizes defined (by type)" "test binding in/out with set input sizes defined (by type)"
vars = self.cursor.setinputsizes(value = cx_Oracle.NCHAR) vars = self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_NVARCHAR)
self.cursor.execute(r""" self.cursor.execute(r"""
begin begin
:value := :value || unistr(' TSI \3042'); :value := :value || unistr(' TSI \3042');
@ -183,7 +183,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutVar(self): def testBindOutVar(self):
"test binding out with cursor.var() method" "test binding out with cursor.var() method"
var = self.cursor.var(cx_Oracle.NCHAR) var = self.cursor.var(cx_Oracle.DB_TYPE_NVARCHAR)
self.cursor.execute(r""" self.cursor.execute(r"""
begin begin
:value := unistr('TSI (VAR) \3042'); :value := unistr('TSI (VAR) \3042');
@ -193,7 +193,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutVarDirectSet(self): def testBindInOutVarDirectSet(self):
"test binding in/out with cursor.var() method" "test binding in/out with cursor.var() method"
var = self.cursor.var(cx_Oracle.NCHAR) var = self.cursor.var(cx_Oracle.DB_TYPE_NVARCHAR)
var.setvalue(0, u"InVal \u3041") var.setvalue(0, u"InVal \u3041")
self.cursor.execute(r""" self.cursor.execute(r"""
begin begin
@ -206,11 +206,13 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate" "test cursor description is accurate"
self.cursor.execute("select * from TestUnicodes") self.cursor.execute("select * from TestUnicodes")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('UNICODECOL', cx_Oracle.NCHAR, 20, 80, None, None, 0), ('UNICODECOL', cx_Oracle.DB_TYPE_NVARCHAR, 20, 80, None,
('FIXEDUNICODECOL', cx_Oracle.FIXED_NCHAR, 40, 160, None,
None, 0), None, 0),
('NULLABLECOL', cx_Oracle.NCHAR, 50, 200, None, None, 1) ]) ('FIXEDUNICODECOL', cx_Oracle.DB_TYPE_NCHAR, 40, 160, None,
None, 0),
('NULLABLECOL', cx_Oracle.DB_TYPE_NVARCHAR, 50, 200, None,
None, 1) ])
def testFetchAll(self): def testFetchAll(self):
"test that fetching all of the data returns the correct results" "test that fetching all of the data returns the correct results"

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
# #
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
# #
@ -19,7 +19,8 @@ class TestCase(TestEnv.BaseTestCase):
def outputTypeHandlerNativeInt(self, cursor, name, defaultType, size, def outputTypeHandlerNativeInt(self, cursor, name, defaultType, size,
precision, scale): precision, scale):
return cursor.var(cx_Oracle.NATIVE_INT, arraysize=cursor.arraysize) return cursor.var(cx_Oracle.DB_TYPE_BINARY_INTEGER,
arraysize=cursor.arraysize)
def outputTypeHandlerDecimal(self, cursor, name, defaultType, size, def outputTypeHandlerDecimal(self, cursor, name, defaultType, size,
precision, scale): precision, scale):
@ -272,12 +273,15 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate" "test cursor description is accurate"
self.cursor.execute("select * from TestNumbers") self.cursor.execute("select * from TestNumbers")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('LONGINTCOL', cx_Oracle.NUMBER, 17, None, 16, 0, 0), ('LONGINTCOL', cx_Oracle.DB_TYPE_NUMBER, 17, None, 16, 0, 0),
('NUMBERCOL', cx_Oracle.NUMBER, 13, None, 9, 2, 0), ('NUMBERCOL', cx_Oracle.DB_TYPE_NUMBER, 13, None, 9, 2, 0),
('FLOATCOL', cx_Oracle.NUMBER, 127, None, 126, -127, 0), ('FLOATCOL', cx_Oracle.DB_TYPE_NUMBER, 127, None, 126, -127,
('UNCONSTRAINEDCOL', cx_Oracle.NUMBER, 127, None, 0, -127, 0), 0),
('NULLABLECOL', cx_Oracle.NUMBER, 39, None, 38, 0, 1) ]) ('UNCONSTRAINEDCOL', cx_Oracle.DB_TYPE_NUMBER, 127, None, 0,
-127, 0),
('NULLABLECOL', cx_Oracle.DB_TYPE_NUMBER, 39, None, 38, 0,
1) ])
def testFetchAll(self): def testFetchAll(self):
"test that fetching all of the data returns the correct results" "test that fetching all of the data returns the correct results"
@ -369,23 +373,28 @@ class TestCase(TestEnv.BaseTestCase):
def testStringFormat(self): def testStringFormat(self):
"test that string format is returned properly" "test that string format is returned properly"
var = self.cursor.var(cx_Oracle.NUMBER) var = self.cursor.var(cx_Oracle.NUMBER)
self.assertEqual(str(var), "<cx_Oracle.NUMBER with value None>") self.assertEqual(str(var),
"<cx_Oracle.Var of type DB_TYPE_NUMBER with value None>")
var.setvalue(0, 4) var.setvalue(0, 4)
self.assertEqual(str(var), "<cx_Oracle.NUMBER with value 4.0>") self.assertEqual(str(var),
"<cx_Oracle.Var of type DB_TYPE_NUMBER with value 4.0>")
def testBindNativeFloat(self): def testBindNativeFloat(self):
"test that binding native float is possible" "test that binding native float is possible"
self.cursor.setinputsizes(cx_Oracle.NATIVE_FLOAT) self.cursor.setinputsizes(cx_Oracle.DB_TYPE_BINARY_DOUBLE)
self.cursor.execute("select :1 from dual", (5,)) self.cursor.execute("select :1 from dual", (5,))
self.assertEqual(type(self.cursor.bindvars[0]), cx_Oracle.NATIVE_FLOAT) self.assertEqual(self.cursor.bindvars[0].type,
cx_Oracle.DB_TYPE_BINARY_DOUBLE)
value, = self.cursor.fetchone() value, = self.cursor.fetchone()
self.assertEqual(value, 5) self.assertEqual(value, 5)
self.cursor.execute("select :1 from dual", (1.5,)) self.cursor.execute("select :1 from dual", (1.5,))
self.assertEqual(type(self.cursor.bindvars[0]), cx_Oracle.NATIVE_FLOAT) self.assertEqual(self.cursor.bindvars[0].type,
cx_Oracle.DB_TYPE_BINARY_DOUBLE)
value, = self.cursor.fetchone() value, = self.cursor.fetchone()
self.assertEqual(value, 1.5) self.assertEqual(value, 1.5)
self.cursor.execute("select :1 from dual", (decimal.Decimal("NaN"),)) self.cursor.execute("select :1 from dual", (decimal.Decimal("NaN"),))
self.assertEqual(type(self.cursor.bindvars[0]), cx_Oracle.NATIVE_FLOAT) self.assertEqual(self.cursor.bindvars[0].type,
cx_Oracle.DB_TYPE_BINARY_DOUBLE)
value, = self.cursor.fetchone() value, = self.cursor.fetchone()
self.assertEqual(str(value), str(float("NaN"))) self.assertEqual(str(value), str(float("NaN")))
@ -399,4 +408,3 @@ class TestCase(TestEnv.BaseTestCase):
if __name__ == "__main__": if __name__ == "__main__":
TestEnv.RunTestCases() TestEnv.RunTestCases()

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
# #
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
# #
@ -50,7 +50,8 @@ class TestCase(TestEnv.BaseTestCase):
def testBindNullIn(self): def testBindNullIn(self):
"test binding a null value (IN)" "test binding a null value (IN)"
var = self.cursor.var(cx_Oracle.OBJECT, typename = "UDT_OBJECT") var = self.cursor.var(cx_Oracle.DB_TYPE_OBJECT,
typename = "UDT_OBJECT")
result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str, result = self.cursor.callfunc("pkg_TestBindObject.GetStringRep", str,
(var,)) (var,))
self.assertEqual(result, "null") self.assertEqual(result, "null")
@ -122,9 +123,11 @@ class TestCase(TestEnv.BaseTestCase):
from TestObjects from TestObjects
order by IntCol""") order by IntCol""")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('OBJECTCOL', cx_Oracle.OBJECT, None, None, None, None, 1), ('OBJECTCOL', cx_Oracle.DB_TYPE_OBJECT, None, None, None,
('ARRAYCOL', cx_Oracle.OBJECT, None, None, None, None, 1) ]) None, 1),
('ARRAYCOL', cx_Oracle.DB_TYPE_OBJECT, None, None, None,
None, 1) ])
self.__TestData(1, (1, 'First row', 'First ', 'N First Row', self.__TestData(1, (1, 'First row', 'First ', 'N First Row',
'N First ', b'Raw Data 1', 2, 5, 12.125, 0.5, 12.5, 25.25, 'N First ', b'Raw Data 1', 2, 5, 12.125, 0.5, 12.5, 25.25,
50.125, cx_Oracle.Timestamp(2007, 3, 6, 0, 0, 0), 50.125, cx_Oracle.Timestamp(2007, 3, 6, 0, 0, 0),
@ -154,6 +157,8 @@ class TestCase(TestEnv.BaseTestCase):
self.assertEqual(typeObj.iscollection, False) self.assertEqual(typeObj.iscollection, False)
self.assertEqual(typeObj.schema, self.connection.username.upper()) self.assertEqual(typeObj.schema, self.connection.username.upper())
self.assertEqual(typeObj.name, "UDT_OBJECT") self.assertEqual(typeObj.name, "UDT_OBJECT")
subObjectValueType = self.connection.gettype("UDT_SUBOBJECT")
subObjectArrayType = self.connection.gettype("UDT_OBJECTARRAY")
expectedAttributeNames = ["NUMBERVALUE", "STRINGVALUE", expectedAttributeNames = ["NUMBERVALUE", "STRINGVALUE",
"FIXEDCHARVALUE", "NSTRINGVALUE", "NFIXEDCHARVALUE", "FIXEDCHARVALUE", "NSTRINGVALUE", "NFIXEDCHARVALUE",
"RAWVALUE", "INTVALUE", "SMALLINTVALUE", "REALVALUE", "RAWVALUE", "INTVALUE", "SMALLINTVALUE", "REALVALUE",
@ -163,9 +168,23 @@ class TestCase(TestEnv.BaseTestCase):
"NCLOBVALUE", "BLOBVALUE", "SUBOBJECTVALUE", "SUBOBJECTARRAY"] "NCLOBVALUE", "BLOBVALUE", "SUBOBJECTVALUE", "SUBOBJECTARRAY"]
actualAttributeNames = [a.name for a in typeObj.attributes] actualAttributeNames = [a.name for a in typeObj.attributes]
self.assertEqual(actualAttributeNames, expectedAttributeNames) self.assertEqual(actualAttributeNames, expectedAttributeNames)
typeObj = self.connection.gettype("UDT_OBJECTARRAY") expectedAttributeTypes = [cx_Oracle.DB_TYPE_NUMBER,
self.assertEqual(typeObj.iscollection, True) cx_Oracle.DB_TYPE_VARCHAR, cx_Oracle.DB_TYPE_CHAR,
self.assertEqual(typeObj.attributes, []) cx_Oracle.DB_TYPE_NVARCHAR, cx_Oracle.DB_TYPE_NCHAR,
cx_Oracle.DB_TYPE_RAW, cx_Oracle.DB_TYPE_NUMBER,
cx_Oracle.DB_TYPE_NUMBER, cx_Oracle.DB_TYPE_NUMBER,
cx_Oracle.DB_TYPE_NUMBER, cx_Oracle.DB_TYPE_NUMBER,
cx_Oracle.DB_TYPE_BINARY_FLOAT,
cx_Oracle.DB_TYPE_BINARY_DOUBLE,
cx_Oracle.DB_TYPE_DATE, cx_Oracle.DB_TYPE_TIMESTAMP,
cx_Oracle.DB_TYPE_TIMESTAMP_TZ,
cx_Oracle.DB_TYPE_TIMESTAMP_LTZ, cx_Oracle.DB_TYPE_CLOB,
cx_Oracle.DB_TYPE_NCLOB, cx_Oracle.DB_TYPE_BLOB,
subObjectValueType, subObjectArrayType]
actualAttributeTypes = [a.type for a in typeObj.attributes]
self.assertEqual(actualAttributeTypes, expectedAttributeTypes)
self.assertEqual(subObjectArrayType.iscollection, True)
self.assertEqual(subObjectArrayType.attributes, [])
def testObjectType(self): def testObjectType(self):
"test object type data" "test object type data"
@ -316,7 +335,8 @@ class TestCase(TestEnv.BaseTestCase):
"test setting value of object variable to wrong object type" "test setting value of object variable to wrong object type"
wrongObjType = self.connection.gettype("UDT_OBJECTARRAY") wrongObjType = self.connection.gettype("UDT_OBJECTARRAY")
wrongObj = wrongObjType.newobject() wrongObj = wrongObjType.newobject()
var = self.cursor.var(cx_Oracle.OBJECT, typename = "UDT_OBJECT") var = self.cursor.var(cx_Oracle.DB_TYPE_OBJECT,
typename = "UDT_OBJECT")
self.assertRaises(cx_Oracle.DatabaseError, var.setvalue, 0, wrongObj) self.assertRaises(cx_Oracle.DatabaseError, var.setvalue, 0, wrongObj)
def testStringFormat(self): def testStringFormat(self):

View File

@ -272,7 +272,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindLongString(self): def testBindLongString(self):
"test that binding a long string succeeds" "test that binding a long string succeeds"
self.cursor.setinputsizes(bigString = cx_Oracle.LONG_STRING) self.cursor.setinputsizes(bigString = cx_Oracle.DB_TYPE_LONG)
self.cursor.execute(""" self.cursor.execute("""
declare declare
t_Temp varchar2(20000); t_Temp varchar2(20000);
@ -295,15 +295,15 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate" "test cursor description is accurate"
self.cursor.execute("select * from TestStrings") self.cursor.execute("select * from TestStrings")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('STRINGCOL', cx_Oracle.STRING, 20, ('STRINGCOL', cx_Oracle.DB_TYPE_VARCHAR, 20,
20 * TestEnv.GetCharSetRatio(), None, 20 * TestEnv.GetCharSetRatio(), None,
None, 0), None, 0),
('RAWCOL', cx_Oracle.BINARY, 30, 30, None, None, 0), ('RAWCOL', cx_Oracle.DB_TYPE_RAW, 30, 30, None, None, 0),
('FIXEDCHARCOL', cx_Oracle.FIXED_CHAR, 40, ('FIXEDCHARCOL', cx_Oracle.DB_TYPE_CHAR, 40,
40 * TestEnv.GetCharSetRatio(), 40 * TestEnv.GetCharSetRatio(),
None, None, 0), None, None, 0),
('NULLABLECOL', cx_Oracle.STRING, 50, ('NULLABLECOL', cx_Oracle.DB_TYPE_VARCHAR, 50,
50 * TestEnv.GetCharSetRatio(), None, 50 * TestEnv.GetCharSetRatio(), None,
None, 1) ]) None, 1) ])

View File

@ -45,7 +45,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindTimestamp(self): def testBindTimestamp(self):
"test binding in a timestamp" "test binding in a timestamp"
self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP) self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_TIMESTAMP)
self.cursor.execute(""" self.cursor.execute("""
select * from TestTimestamps select * from TestTimestamps
where TimestampCol = :value""", where TimestampCol = :value""",
@ -54,7 +54,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindNull(self): def testBindNull(self):
"test binding in a null" "test binding in a null"
self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP) self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_TIMESTAMP)
self.cursor.execute(""" self.cursor.execute("""
select * from TestTimestamps select * from TestTimestamps
where TimestampCol = :value""", where TimestampCol = :value""",
@ -63,7 +63,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutSetInputSizes(self): def testBindOutSetInputSizes(self):
"test binding out with set input sizes defined" "test binding out with set input sizes defined"
vars = self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP) vars = self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_TIMESTAMP)
self.cursor.execute(""" self.cursor.execute("""
begin begin
:value := to_timestamp('20021209', 'YYYYMMDD'); :value := to_timestamp('20021209', 'YYYYMMDD');
@ -73,7 +73,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutSetInputSizes(self): def testBindInOutSetInputSizes(self):
"test binding in/out with set input sizes defined" "test binding in/out with set input sizes defined"
vars = self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP) vars = self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_TIMESTAMP)
self.cursor.execute(""" self.cursor.execute("""
begin begin
:value := :value + 5.25; :value := :value + 5.25;
@ -84,7 +84,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutVar(self): def testBindOutVar(self):
"test binding out with cursor.var() method" "test binding out with cursor.var() method"
var = self.cursor.var(cx_Oracle.TIMESTAMP) var = self.cursor.var(cx_Oracle.DB_TYPE_TIMESTAMP)
self.cursor.execute(""" self.cursor.execute("""
begin begin
:value := to_date('20021231 12:31:00', :value := to_date('20021231 12:31:00',
@ -96,7 +96,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutVarDirectSet(self): def testBindInOutVarDirectSet(self):
"test binding in/out with cursor.var() method" "test binding in/out with cursor.var() method"
var = self.cursor.var(cx_Oracle.TIMESTAMP) var = self.cursor.var(cx_Oracle.DB_TYPE_TIMESTAMP)
var.setvalue(0, cx_Oracle.Timestamp(2002, 12, 9, 6, 0, 0)) var.setvalue(0, cx_Oracle.Timestamp(2002, 12, 9, 6, 0, 0))
self.cursor.execute(""" self.cursor.execute("""
begin begin
@ -110,9 +110,11 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate" "test cursor description is accurate"
self.cursor.execute("select * from TestTimestamps") self.cursor.execute("select * from TestTimestamps")
self.assertEqual(self.cursor.description, self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0), [ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('TIMESTAMPCOL', cx_Oracle.TIMESTAMP, 23, None, 0, 6, 0), ('TIMESTAMPCOL', cx_Oracle.DB_TYPE_TIMESTAMP, 23, None, 0, 6,
('NULLABLECOL', cx_Oracle.TIMESTAMP, 23, None, 0, 6, 1) ]) 0),
('NULLABLECOL', cx_Oracle.DB_TYPE_TIMESTAMP, 23, None, 0, 6,
1) ])
def testFetchAll(self): def testFetchAll(self):
"test that fetching all of the data returns the correct results" "test that fetching all of the data returns the correct results"

View File

@ -1,5 +1,5 @@
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
# #
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved. # Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
# #
@ -36,6 +36,7 @@ moduleNames = [
"Cursor", "Cursor",
"CursorVar", "CursorVar",
"DateTimeVar", "DateTimeVar",
"DbTypes",
"DMLReturning", "DMLReturning",
"Error", "Error",
"IntervalVar", "IntervalVar",