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
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)
@ -554,10 +555,34 @@ Cursor Object
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 variable. This should be one of the types defined at the module level
(such as :data:`cx_Oracle.STRING`) or a Python type that cx_Oracle knows
how to process (such as str) or an object type returned from the method
:meth:`Connection.gettype()`.
the variable. This should be one of the
:ref:`database type constants <dbtypes>`, :ref:`DB API constants <types>`,
an object type returned from the method :meth:`Connection.gettype()` or one
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
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.
The encodingErrors parameter specifies what should happen when decoding
byte strings fetched from the database into strings (Python 3) or unicode
objects (Python 2). It should be one of the values noted in the builtin
byte strings fetched from the database into strings. It should be one of
the values noted in the builtin
`decode <https://docs.python.org/3/library/stdtypes.html#bytes.decode>`__
function.

View File

@ -79,6 +79,14 @@ See :ref:`lobdata` for more information about using LOBs.
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])
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
=====
DB API Types
------------
.. data:: BINARY
This type object is used to describe columns in a database that contain
binary data. In Oracle this is RAW columns.
.. data:: BFILE
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.
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
type, the database type :data:`DB_TYPE_RAW` will be used.
.. data:: DATETIME
This type object is used to describe columns in a database that are dates.
.. data:: FIXED_CHAR
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.
The database types :data:`DB_TYPE_DATE`, :data:`DB_TYPE_TIMESTAMP`,
:data:`DB_TYPE_TIMESTAMP_LTZ` and :data:`DB_TYPE_TIMESTAMP_TZ` will all
compare equal to this value. If a variable is created with this
type, the database type :data:`DB_TYPE_DATE` will be used.
.. data:: NUMBER
This type object is used to describe columns in a database that are
numbers.
.. data:: OBJECT
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.
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
is created with this type, the database type :data:`DB_TYPE_NUMBER` will be
used.
.. 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
This type object is used to describe columns in a database that are strings
(in Oracle this is VARCHAR2 columns).
This type object is used to describe columns in a database that are
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
This type object is used to describe columns in a database that are
timestamps.
A synonym for :data:`DB_TYPE_TIMESTAMP`.
.. 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:

View File

@ -19,8 +19,8 @@ Object Type Objects
.. attribute:: ObjectType.attributes
This read-only attribute returns a list of the attributes that make up the
object type. Each attribute has a name attribute on it.
This read-only attribute returns a list of the :ref:`attributes
<objectattr>` that make up the object type.
.. attribute:: ObjectType.iscollection
@ -34,6 +34,17 @@ Object Type Objects
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])
Return a new Oracle object of the given type. This object can then be
@ -151,3 +162,29 @@ Object Objects
.. method:: Object.trim(num)
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
This read-only attribute returns the type of the variable for those
variables that bind Oracle objects (it is not present for any other type of
variable).
This read-only attribute returns the type of the variable. 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>`.
.. versionchanged:: 8.0
Database type constants are now used when the variable is not used for
binding Oracle objects.
.. 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
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
and 3.8.
against Oracle Client 19c, 18c, 12c, and 11.2, and Python 3.5, 3.6, 3.7 and
3.8.
**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

View File

@ -9,9 +9,48 @@ Version 8.0 (TBD)
-----------------
#) 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),
as requested
(`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)

View File

@ -214,9 +214,10 @@ LOB Bind Variables
==================
Database CLOBs, NCLOBS, BLOBs and BFILEs can be bound with types
:attr:`cx_Oracle.CLOB`, :attr:`cx_Oracle.NCLOB`, :attr:`cx_Oracle.BLOB`
and :attr:`cx_Oracle.BFILE` respectively. LOBs fetched from the database or
created with :meth:`Connection.createlob()` can also be bound.
:attr:`cx_Oracle.DB_TYPE_CLOB`, :attr:`cx_Oracle.DB_TYPE_NCLOB`,
:attr:`cx_Oracle.DB_TYPE_BLOB` and :attr:`cx_Oracle.DB_TYPE_BFILE`
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
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')
(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()`:
.. 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:
print(row)
@ -659,7 +660,7 @@ objects seamlessly:
def InputTypeHandler(cursor, value, numElements):
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)

View File

@ -637,6 +637,25 @@ Upgrading from Older Versions
Review the :ref:`release notes <releasenotes>` for deprecations and modify any
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:
- When using Oracle Instant Client, you should not set ``ORACLE_HOME``.
@ -684,9 +703,8 @@ If installation fails:
or venv?
- 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
venv module (builtin to Python 3.x) or virtualenv module (Python 2.x)
instead.
to Python but is sometimes removed by the OS. Use the venv module
(builtin to Python 3.x) or virtualenv module instead.
- 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
@ -723,6 +741,5 @@ If using cx_Oracle fails:
supported on Windows 7. Similar steps shown above for ``DPI-1047`` may
help.
- If you have both Python 2 and 3 installed, make sure you are
using the correct python and pip (or python3 and pip3)
executables.
- If you have multiple versions of Python installed, make sure you are
using the correct python and pip (or python3 and pip3) executables.

View File

@ -47,7 +47,7 @@ Features
The cx_Oracle feature highlights are:
* 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
* Extensive Oracle data type support, including large objects (CLOB and
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):
* 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
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
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
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.
@ -76,10 +77,10 @@ to be used as shown in this example:
.. code-block:: python
def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
if defaultType == cx_Oracle.CLOB:
return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize)
if defaultType == cx_Oracle.BLOB:
return cursor.var(cx_Oracle.LONG_BINARY, arraysize=cursor.arraysize)
if defaultType == cx_Oracle.DB_TYPE_CLOB:
return cursor.var(cx_Oracle.DB_TYPE_LONG, arraysize=cursor.arraysize)
if defaultType == cx_Oracle.DB_TYPE_BLOB:
return cursor.var(cx_Oracle.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
idVal = 1
textData = "The quick brown fox jumps over the lazy dog"
@ -161,7 +162,7 @@ in the following code:
.. code-block:: python
idVal = 9
lobVar = cursor.var(cx_Oracle.BLOB)
lobVar = cursor.var(cx_Oracle.DB_TYPE_BLOB)
cursor.execute("""
insert into lob_tbl (id, b)
values (:1, empty_blob())

View File

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

View File

@ -38,7 +38,7 @@ as shown:
.. code-block:: python
clob = connection.createlob(cx_Oracle.CLOB)
clob = connection.createlob(cx_Oracle.DB_TYPE_CLOB)
clob.write(xmlData)
cursor.execute("insert into xml_table values (:id, sys.xmltype(:xml))",
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;
cxoTransformNum transformNum;
transformNum = cxoTransform_getNumFromValue(value, 0);
transformNum = cxoTransform_getNumFromPythonValue(value, 0);
if (cxoTransform_fromPython(transformNum, &nativeTypeNum, value,
&column->value, buffer, params->encoding, params->nencoding, NULL,
0) < 0)
@ -1011,7 +1011,7 @@ static PyObject *cxoConnection_getType(cxoConnection *conn, PyObject *nameObj)
static PyObject *cxoConnection_createLob(cxoConnection *conn,
PyObject *lobType)
{
dpiOracleTypeNum oracleTypeNum;
cxoDbType *dbType;
dpiLob *handle;
PyObject *lob;
@ -1020,23 +1020,20 @@ static PyObject *cxoConnection_createLob(cxoConnection *conn,
return NULL;
// verify the LOB type
if (lobType == (PyObject*) &cxoPyTypeClobVar)
oracleTypeNum = DPI_ORACLE_TYPE_CLOB;
else if (lobType == (PyObject*) &cxoPyTypeBlobVar)
oracleTypeNum = DPI_ORACLE_TYPE_BLOB;
else if (lobType == (PyObject*) &cxoPyTypeNclobVar)
oracleTypeNum = DPI_ORACLE_TYPE_NCLOB;
else {
if (lobType != (PyObject*) cxoDbTypeClob &&
lobType != (PyObject*) cxoDbTypeBlob &&
lobType != (PyObject*) cxoDbTypeNclob) {
PyErr_SetString(PyExc_TypeError,
"parameter should be one of cx_Oracle.CLOB, cx_Oracle.BLOB "
"or cx_Oracle.NCLOB");
"parameter should be one of cx_Oracle.DB_TYPE_CLOB, "
"cx_Oracle.DB_TYPE_BLOB or cx_Oracle.DB_TYPE_NCLOB");
return NULL;
}
// 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();
lob = cxoLob_new(conn, oracleTypeNum, handle);
lob = cxoLob_new(conn, dbType, handle);
if (!lob)
dpiLob_release(handle);
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)
{
PyObject *outputTypeHandler, *result;
cxoTransformNum transformNum;
cxoObjectType *objectType;
cxoVarType *varType;
dpiQueryInfo queryInfo;
uint32_t pos, size;
cxoDbType *dbType;
char message[120];
cxoVar *var;
// 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;
}
// determine the default type
varType = cxoVarType_fromDataTypeInfo(&queryInfo.typeInfo);
if (!varType)
// determine the default types to use
transformNum =
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;
// see if an output type handler should be used
@ -390,7 +400,7 @@ static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
if (outputTypeHandler) {
result = PyObject_CallFunction(outputTypeHandler, "Os#Oiii",
cursor, queryInfo.name, (Py_ssize_t) queryInfo.nameLength,
varType->pythonType, size, queryInfo.typeInfo.precision,
dbType, size, queryInfo.typeInfo.precision,
queryInfo.typeInfo.scale);
if (!result) {
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 (!var) {
var = cxoVar_new(cursor, cursor->fetchArraySize, varType, size, 0,
objectType);
var = cxoVar_new(cursor, cursor->fetchArraySize, transformNum,
size, 0, objectType);
if (!var) {
Py_XDECREF(objectType);
return -1;
@ -444,16 +454,16 @@ static int cxoCursor_performDefine(cxoCursor *cursor, uint32_t numQueryColumns)
//-----------------------------------------------------------------------------
static PyObject *cxoCursor_itemDescription(cxoCursor *cursor, uint32_t pos)
{
cxoVarType *varType;
int displaySize, index;
dpiQueryInfo queryInfo;
PyObject *tuple, *temp;
cxoDbType *dbType;
// get information about the column position
if (dpiStmt_getQueryInfo(cursor->handle, pos, &queryInfo) < 0)
return NULL;
varType = cxoVarType_fromDataTypeInfo(&queryInfo.typeInfo);
if (!varType)
dbType = cxoDbType_fromDataTypeInfo(&queryInfo.typeInfo);
if (!dbType)
return NULL;
// 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,
queryInfo.nameLength, cursor->connection->encodingInfo.encoding,
NULL));
Py_INCREF(varType->pythonType);
PyTuple_SET_ITEM(tuple, 1, (PyObject*) varType->pythonType);
Py_INCREF(dbType);
PyTuple_SET_ITEM(tuple, 1, (PyObject*) dbType);
if (displaySize)
PyTuple_SET_ITEM(tuple, 2, PyLong_FromLong(displaySize));
else {
@ -673,7 +683,7 @@ static int cxoCursor_setBindVariableHelper(cxoCursor *cursor,
// can happen if all of the values in a previous invocation of
// executemany() were None) and there is now a value; in this case,
// 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) {
origVar = NULL;
varToSet = NULL;
@ -682,8 +692,9 @@ static int cxoCursor_setBindVariableHelper(cxoCursor *cursor,
// variable this is only necessary for executemany() since
// execute() always passes a value of 1 for the number of elements
} else if (numElements > origVar->allocatedElements) {
*newVar = cxoVar_new(cursor, numElements, origVar->type,
origVar->size, origVar->isArray, origVar->objectType);
*newVar = cxoVar_new(cursor, numElements,
origVar->transformNum, origVar->size, origVar->isArray,
origVar->objectType);
if (!*newVar)
return -1;
varToSet = *newVar;
@ -1826,9 +1837,9 @@ static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
"inconverter", "outconverter", "typename", "encodingErrors",
NULL };
PyObject *inConverter, *outConverter, *typeNameObj;
cxoTransformNum transformNum;
const char *encodingErrors;
cxoObjectType *objType;
cxoVarType *varType;
int size, arraySize;
PyObject *type;
cxoVar *var;
@ -1844,12 +1855,9 @@ static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
return NULL;
// determine the type of variable
varType = cxoVarType_fromPythonType(type, &objType);
if (!varType)
if (cxoTransform_getNumFromType(type, &transformNum, &objType) < 0)
return NULL;
Py_XINCREF(objType);
if (size == 0)
size = varType->size;
if (typeNameObj && typeNameObj != Py_None && !objType) {
objType = cxoObjectType_newByName(cursor->connection, typeNameObj);
if (!objType)
@ -1857,7 +1865,7 @@ static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
}
// create the variable
var = cxoVar_new(cursor, arraySize, varType, size, 0, objType);
var = cxoVar_new(cursor, arraySize, transformNum, size, 0, objType);
Py_XDECREF(objType);
if (!var)
return NULL;
@ -1886,23 +1894,20 @@ static PyObject *cxoCursor_var(cxoCursor *cursor, PyObject *args,
//-----------------------------------------------------------------------------
static PyObject *cxoCursor_arrayVar(cxoCursor *cursor, PyObject *args)
{
cxoTransformNum transformNum;
uint32_t size, numElements;
PyObject *type, *value;
cxoObjectType *objType;
cxoVarType *varType;
cxoVar *var;
// parse arguments
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;
// determine the type of variable
varType = cxoVarType_fromPythonType(type, &objType);
if (!varType)
// determine the transform to use for the variable
if (cxoTransform_getNumFromType(type, &transformNum, &objType) < 0)
return NULL;
if (size == 0)
size = varType->size;
// determine the number of elements to create
if (PyList_Check(value))
@ -1918,7 +1923,7 @@ static PyObject *cxoCursor_arrayVar(cxoCursor *cursor, PyObject *args)
}
// create the variable
var = cxoVar_new(cursor, numElements, varType, size, 1, objType);
var = cxoVar_new(cursor, numElements, transformNum, size, 1, objType);
if (!var)
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[] = {
{ "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
//-----------------------------------------------------------------------------
@ -63,7 +72,8 @@ PyTypeObject cxoPyTypeLob = {
.tp_dealloc = (destructor) cxoLob_free,
.tp_str = (reprfunc) cxoLob_str,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_methods = cxoLobMethods
.tp_methods = cxoLobMethods,
.tp_members = cxoMembers
};
@ -71,7 +81,7 @@ PyTypeObject cxoPyTypeLob = {
// cxoLob_new()
// Create a new LOB.
//-----------------------------------------------------------------------------
PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
PyObject *cxoLob_new(cxoConnection *connection, cxoDbType *dbType,
dpiLob *handle)
{
cxoLob *lob;
@ -80,9 +90,10 @@ PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
if (!lob)
return NULL;
lob->handle = handle;
lob->oracleTypeNum = oracleTypeNum;
Py_INCREF(connection);
lob->connection = connection;
Py_INCREF(dbType);
lob->dbType = dbType;
return (PyObject*) lob;
}
@ -97,6 +108,7 @@ static void cxoLob_free(cxoLob *lob)
dpiLob_release(lob->handle);
lob->handle = NULL;
}
Py_CLEAR(lob->dbType);
Py_CLEAR(lob->connection);
Py_TYPE(lob)->tp_free((PyObject*) lob);
}
@ -141,13 +153,15 @@ static PyObject *cxoLob_internalRead(cxoLob *lob, uint64_t offset,
}
// return the result
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_NCLOB)
if (lob->dbType == cxoDbTypeNclob) {
result = PyUnicode_Decode(buffer, (Py_ssize_t) bufferSize,
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,
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);
return result;
}
@ -164,7 +178,7 @@ static int cxoLob_internalWrite(cxoLob *lob, PyObject *dataObj,
cxoBuffer buffer;
int status;
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_NCLOB)
if (lob->dbType == cxoDbTypeNclob)
encoding = lob->connection->encodingInfo.nencoding;
else encoding = lob->connection->encodingInfo.encoding;
if (cxoBuffer_fromObject(&buffer, dataObj, encoding) < 0)

View File

@ -21,6 +21,21 @@
if (PyModule_AddIntConstant(module, name, value) < 0) \
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 CXO_ADD_TYPE_OBJECT(name, type) \
Py_INCREF(type); \
@ -48,10 +63,98 @@ PyObject *cxoProgrammingErrorException = NULL;
PyObject *cxoNotSupportedErrorException = NULL;
PyObject *cxoJsonDumpFunction = 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;
dpiContext *cxoDpiContext = NULL;
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()
// Create an exception and set it in the provided dictionary.
@ -242,50 +345,32 @@ static PyObject *cxoModule_initialize(void)
return NULL;
// prepare the types for use by the module
CXO_MAKE_TYPE_READY(&cxoPyTypeBfileVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeBinaryVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeBlobVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeBooleanVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeClobVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeApiType);
CXO_MAKE_TYPE_READY(&cxoPyTypeConnection);
CXO_MAKE_TYPE_READY(&cxoPyTypeCursor);
CXO_MAKE_TYPE_READY(&cxoPyTypeCursorVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeDateTimeVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeDbType);
CXO_MAKE_TYPE_READY(&cxoPyTypeDeqOptions);
CXO_MAKE_TYPE_READY(&cxoPyTypeEnqOptions);
CXO_MAKE_TYPE_READY(&cxoPyTypeError);
CXO_MAKE_TYPE_READY(&cxoPyTypeFixedCharVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeFixedNcharVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeFuture);
CXO_MAKE_TYPE_READY(&cxoPyTypeIntervalVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeLob);
CXO_MAKE_TYPE_READY(&cxoPyTypeLongBinaryVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeLongStringVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeMsgProps);
CXO_MAKE_TYPE_READY(&cxoPyTypeMessage);
CXO_MAKE_TYPE_READY(&cxoPyTypeMessageQuery);
CXO_MAKE_TYPE_READY(&cxoPyTypeMessageRow);
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(&cxoPyTypeObject);
CXO_MAKE_TYPE_READY(&cxoPyTypeObjectType);
CXO_MAKE_TYPE_READY(&cxoPyTypeObjectVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeQueue);
CXO_MAKE_TYPE_READY(&cxoPyTypeRowidVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeSessionPool);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaCollection);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDatabase);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDoc);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDocCursor);
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaOperation);
CXO_MAKE_TYPE_READY(&cxoPyTypeStringVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeSubscr);
CXO_MAKE_TYPE_READY(&cxoPyTypeTimestampVar);
CXO_MAKE_TYPE_READY(&cxoPyTypeVar);
// initialize module and retrieve the dictionary
module = PyModule_Create(&cxoModuleDef);
@ -325,50 +410,129 @@ static PyObject *cxoModule_initialize(void)
return NULL;
// set up the types that are available
CXO_ADD_TYPE_OBJECT("ApiType", &cxoPyTypeApiType)
CXO_ADD_TYPE_OBJECT("Binary", &PyBytes_Type)
CXO_ADD_TYPE_OBJECT("Connection", &cxoPyTypeConnection)
CXO_ADD_TYPE_OBJECT("Cursor", &cxoPyTypeCursor)
CXO_ADD_TYPE_OBJECT("Timestamp", cxoPyTypeDateTime)
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("LOB", &cxoPyTypeLob)
CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps)
CXO_ADD_TYPE_OBJECT("Object", &cxoPyTypeObject)
CXO_ADD_TYPE_OBJECT("ObjectType", &cxoPyTypeObjectType)
CXO_ADD_TYPE_OBJECT("EnqOptions", &cxoPyTypeEnqOptions)
CXO_ADD_TYPE_OBJECT("DeqOptions", &cxoPyTypeDeqOptions)
CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps)
CXO_ADD_TYPE_OBJECT("SessionPool", &cxoPyTypeSessionPool)
CXO_ADD_TYPE_OBJECT("SodaCollection", &cxoPyTypeSodaCollection)
CXO_ADD_TYPE_OBJECT("SodaDatabase", &cxoPyTypeSodaDatabase)
CXO_ADD_TYPE_OBJECT("SodaDoc", &cxoPyTypeSodaDoc)
CXO_ADD_TYPE_OBJECT("SodaDocCursor", &cxoPyTypeSodaDocCursor)
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
CXO_ADD_TYPE_OBJECT("connect", &cxoPyTypeConnection)
// create the basic data types for setting input sizes
CXO_ADD_TYPE_OBJECT("BINARY", &cxoPyTypeBinaryVar)
CXO_ADD_TYPE_OBJECT("BFILE", &cxoPyTypeBfileVar)
CXO_ADD_TYPE_OBJECT("BLOB", &cxoPyTypeBlobVar)
CXO_ADD_TYPE_OBJECT("CLOB", &cxoPyTypeClobVar)
CXO_ADD_TYPE_OBJECT("CURSOR", &cxoPyTypeCursorVar)
CXO_ADD_TYPE_OBJECT("OBJECT", &cxoPyTypeObjectVar)
CXO_ADD_TYPE_OBJECT("DATETIME", &cxoPyTypeDateTimeVar)
CXO_ADD_TYPE_OBJECT("FIXED_CHAR", &cxoPyTypeFixedCharVar)
CXO_ADD_TYPE_OBJECT("FIXED_NCHAR", &cxoPyTypeFixedNcharVar)
CXO_ADD_TYPE_OBJECT("NCHAR", &cxoPyTypeNcharVar)
CXO_ADD_TYPE_OBJECT("INTERVAL", &cxoPyTypeIntervalVar)
CXO_ADD_TYPE_OBJECT("LOB", &cxoPyTypeLob)
CXO_ADD_TYPE_OBJECT("LONG_BINARY", &cxoPyTypeLongBinaryVar)
CXO_ADD_TYPE_OBJECT("LONG_STRING", &cxoPyTypeLongStringVar)
CXO_ADD_TYPE_OBJECT("NCLOB", &cxoPyTypeNclobVar)
CXO_ADD_TYPE_OBJECT("NUMBER", &cxoPyTypeNumberVar)
CXO_ADD_TYPE_OBJECT("ROWID", &cxoPyTypeRowidVar)
CXO_ADD_TYPE_OBJECT("STRING", &cxoPyTypeStringVar)
CXO_ADD_TYPE_OBJECT("TIMESTAMP", &cxoPyTypeTimestampVar)
CXO_ADD_TYPE_OBJECT("NATIVE_INT", &cxoPyTypeNativeIntVar)
CXO_ADD_TYPE_OBJECT("NATIVE_FLOAT", &cxoPyTypeNativeFloatVar)
CXO_ADD_TYPE_OBJECT("BOOLEAN", &cxoPyTypeBooleanVar)
// create the database types (preferred names)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BFILE, "DB_TYPE_BFILE",
CXO_TRANSFORM_BFILE, &cxoDbTypeBfile)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_DOUBLE, "DB_TYPE_BINARY_DOUBLE",
CXO_TRANSFORM_NATIVE_DOUBLE, &cxoDbTypeBinaryDouble)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_FLOAT, "DB_TYPE_BINARY_FLOAT",
CXO_TRANSFORM_NATIVE_FLOAT, &cxoDbTypeBinaryFloat)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_NATIVE_INT, "DB_TYPE_BINARY_INTEGER",
CXO_TRANSFORM_NATIVE_INT, &cxoDbTypeBinaryInteger)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BLOB, "DB_TYPE_BLOB",
CXO_TRANSFORM_BLOB, &cxoDbTypeBlob)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_BOOLEAN, "DB_TYPE_BOOLEAN",
CXO_TRANSFORM_BOOLEAN, &cxoDbTypeBoolean)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_CHAR, "DB_TYPE_CHAR",
CXO_TRANSFORM_FIXED_CHAR, &cxoDbTypeChar)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_CLOB, "DB_TYPE_CLOB",
CXO_TRANSFORM_CLOB, &cxoDbTypeClob)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_STMT, "DB_TYPE_CURSOR",
CXO_TRANSFORM_CURSOR, &cxoDbTypeCursor)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_DATE, "DB_TYPE_DATE",
CXO_TRANSFORM_DATETIME, &cxoDbTypeDate)
CXO_ADD_DB_TYPE(DPI_ORACLE_TYPE_INTERVAL_DS, "DB_TYPE_INTERVAL_DS",
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
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).
//-----------------------------------------------------------------------------
@ -29,12 +29,14 @@
//-----------------------------------------------------------------------------
// Forward Declarations
//-----------------------------------------------------------------------------
typedef struct cxoApiType cxoApiType;
typedef struct cxoBuffer cxoBuffer;
typedef struct cxoError cxoError;
typedef struct cxoConnection cxoConnection;
typedef struct cxoCursor cxoCursor;
typedef struct cxoDbType cxoDbType;
typedef struct cxoDeqOptions cxoDeqOptions;
typedef struct cxoEnqOptions cxoEnqOptions;
typedef struct cxoError cxoError;
typedef struct cxoFuture cxoFuture;
typedef struct cxoLob cxoLob;
typedef struct cxoMessage cxoMessage;
@ -54,7 +56,6 @@ typedef struct cxoSodaDocCursor cxoSodaDocCursor;
typedef struct cxoSodaOperation cxoSodaOperation;
typedef struct cxoSubscr cxoSubscr;
typedef struct cxoVar cxoVar;
typedef struct cxoVarType cxoVarType;
//-----------------------------------------------------------------------------
@ -74,55 +75,71 @@ extern PyObject *cxoProgrammingErrorException;
extern PyObject *cxoNotSupportedErrorException;
// type objects
extern PyTypeObject cxoPyTypeBfileVar;
extern PyTypeObject cxoPyTypeBinaryVar;
extern PyTypeObject cxoPyTypeBlobVar;
extern PyTypeObject cxoPyTypeBooleanVar;
extern PyTypeObject cxoPyTypeClobVar;
extern PyTypeObject cxoPyTypeApiType;
extern PyTypeObject cxoPyTypeConnection;
extern PyTypeObject cxoPyTypeCursor;
extern PyTypeObject cxoPyTypeCursorVar;
extern PyTypeObject cxoPyTypeDateTimeVar;
extern PyTypeObject cxoPyTypeDbType;
extern PyTypeObject cxoPyTypeDeqOptions;
extern PyTypeObject cxoPyTypeEnqOptions;
extern PyTypeObject cxoPyTypeError;
extern PyTypeObject cxoPyTypeFixedCharVar;
extern PyTypeObject cxoPyTypeFixedNcharVar;
extern PyTypeObject cxoPyTypeFuture;
extern PyTypeObject cxoPyTypeIntervalVar;
extern PyTypeObject cxoPyTypeLob;
extern PyTypeObject cxoPyTypeLongBinaryVar;
extern PyTypeObject cxoPyTypeLongStringVar;
extern PyTypeObject cxoPyTypeMsgProps;
extern PyTypeObject cxoPyTypeMessage;
extern PyTypeObject cxoPyTypeMessageQuery;
extern PyTypeObject cxoPyTypeMessageRow;
extern PyTypeObject cxoPyTypeMessageTable;
extern PyTypeObject cxoPyTypeNativeFloatVar;
extern PyTypeObject cxoPyTypeNativeIntVar;
extern PyTypeObject cxoPyTypeNcharVar;
extern PyTypeObject cxoPyTypeNclobVar;
extern PyTypeObject cxoPyTypeNumberVar;
extern PyTypeObject cxoPyTypeObject;
extern PyTypeObject cxoPyTypeObjectAttr;
extern PyTypeObject cxoPyTypeObjectType;
extern PyTypeObject cxoPyTypeObjectVar;
extern PyTypeObject cxoPyTypeQueue;
extern PyTypeObject cxoPyTypeRowidVar;
extern PyTypeObject cxoPyTypeSessionPool;
extern PyTypeObject cxoPyTypeSodaCollection;
extern PyTypeObject cxoPyTypeSodaDatabase;
extern PyTypeObject cxoPyTypeSodaDoc;
extern PyTypeObject cxoPyTypeSodaDocCursor;
extern PyTypeObject cxoPyTypeSodaOperation;
extern PyTypeObject cxoPyTypeStringVar;
extern PyTypeObject cxoPyTypeSubscr;
extern PyTypeObject cxoPyTypeTimestampVar;
extern PyTypeObject cxoPyTypeVar;
// datetime types
extern PyTypeObject *cxoPyTypeDate;
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
extern PyObject *cxoJsonDumpFunction;
extern PyObject *cxoJsonLoadFunction;
@ -166,6 +183,7 @@ typedef enum {
CXO_TRANSFORM_TIMEDELTA,
CXO_TRANSFORM_TIMESTAMP,
CXO_TRANSFORM_TIMESTAMP_LTZ,
CXO_TRANSFORM_TIMESTAMP_TZ,
CXO_TRANSFORM_UNSUPPORTED
} cxoTransformNum;
@ -173,6 +191,13 @@ typedef enum {
//-----------------------------------------------------------------------------
// Structures
//-----------------------------------------------------------------------------
struct cxoApiType {
PyObject_HEAD
const char *name;
PyObject *dbTypes;
cxoTransformNum defaultTransformNum;
};
struct cxoBuffer {
const char *ptr;
uint32_t numCharacters;
@ -228,6 +253,13 @@ struct cxoCursor {
int isOpen;
};
struct cxoDbType {
PyObject_HEAD
uint32_t num;
const char *name;
cxoTransformNum defaultTransformNum;
};
struct cxoDeqOptions {
PyObject_HEAD
dpiDeqOptions *handle;
@ -247,7 +279,7 @@ struct cxoFuture {
struct cxoLob {
PyObject_HEAD
cxoConnection *connection;
dpiOracleTypeNum oracleTypeNum;
cxoDbType *dbType;
dpiLob *handle;
};
@ -303,7 +335,8 @@ struct cxoObjectAttr {
dpiObjectAttr *handle;
dpiOracleTypeNum oracleTypeNum;
cxoTransformNum transformNum;
cxoObjectType *type;
cxoObjectType *objectType;
cxoDbType *dbType;
};
struct cxoObjectType {
@ -316,7 +349,8 @@ struct cxoObjectType {
cxoConnection *connection;
dpiOracleTypeNum elementOracleTypeNum;
cxoTransformNum elementTransformNum;
PyObject *elementType;
cxoObjectType *elementObjectType;
cxoDbType *elementDbType;
char isCollection;
};
@ -418,13 +452,9 @@ struct cxoVar {
int isArray;
int isValueSet;
int getReturnedData;
cxoVarType *type;
};
struct cxoVarType {
cxoTransformNum transformNum;
PyTypeObject *pythonType;
uint32_t size;
dpiNativeTypeNum nativeTypeNum;
cxoDbType *dbType;
};
@ -441,6 +471,9 @@ int cxoCursor_performBind(cxoCursor *cursor);
int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters,
unsigned numElements, unsigned arrayPos, int deferTypeAssignment);
cxoDbType *cxoDbType_fromDataTypeInfo(dpiDataTypeInfo *info);
cxoDbType *cxoDbType_fromTransformNum(cxoTransformNum transformNum);
cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection,
dpiDeqOptions *handle);
@ -454,7 +487,7 @@ int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo);
PyObject *cxoError_raiseFromString(PyObject *exceptionType,
const char *message);
PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
PyObject *cxoLob_new(cxoConnection *connection, cxoDbType *dbType,
dpiLob *handle);
cxoMsgProps *cxoMsgProps_new(cxoConnection*, dpiMsgProps *handle);
@ -491,9 +524,15 @@ int cxoTransform_fromPython(cxoTransformNum transformNum,
dpiNativeTypeNum *nativeTypeNum, PyObject *pyValue,
dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding,
const char *nencoding, cxoVar *var, uint32_t arrayPos);
uint32_t cxoTransform_getDefaultSize(cxoTransformNum transformNum);
cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info);
cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type);
cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql);
cxoTransformNum cxoTransform_getNumFromPythonValue(PyObject *value,
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,
dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum);
int cxoTransform_init(void);
@ -512,20 +551,15 @@ int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer);
int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg,
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_check(PyObject *object);
PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos);
PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos);
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
Py_ssize_t size, int isArray, cxoObjectType *objType);
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements,
cxoTransformNum transformNum, Py_ssize_t size, int isArray,
cxoObjectType *objType);
cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
uint32_t numElements);
cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
Py_ssize_t numElements);
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)
return cxoError_raiseAndReturnNull();
return cxoObject_convertToPython(obj, attribute->transformNum, &data,
attribute->type);
attribute->objectType);
}
@ -367,7 +367,7 @@ static PyObject *cxoObject_internalGetElementByIndex(cxoObject *obj,
&data) < 0)
return cxoError_raiseAndReturnNull();
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 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 },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of calculated members
//-----------------------------------------------------------------------------
static PyGetSetDef cxoCalcMembers[] = {
{ "type", (getter) cxoObjectAttr_getType, 0, 0, 0 },
{ NULL }
};
//-----------------------------------------------------------------------------
// Python type declaration
//-----------------------------------------------------------------------------
@ -35,7 +45,8 @@ PyTypeObject cxoPyTypeObjectAttr = {
.tp_dealloc = (destructor) cxoObjectAttr_free,
.tp_repr = (reprfunc) cxoObjectAttr_repr,
.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)
return cxoError_raiseAndReturnInt();
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->name = PyUnicode_Decode(info.name, info.nameLength,
connection->encodingInfo.encoding, NULL);
if (!attr->name)
return -1;
if (info.typeInfo.objectType) {
attr->type = cxoObjectType_new(connection,
attr->objectType = cxoObjectType_new(connection,
info.typeInfo.objectType);
if (!attr->type)
if (!attr->objectType)
return -1;
}
@ -103,11 +118,29 @@ static void cxoObjectAttr_free(cxoObjectAttr *attr)
attr->handle = NULL;
}
Py_CLEAR(attr->name);
Py_CLEAR(attr->type);
Py_CLEAR(attr->objectType);
Py_CLEAR(attr->dbType);
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()
// Return a string representation of the object attribute.

View File

@ -20,12 +20,14 @@
static void cxoObjectType_free(cxoObjectType*);
static PyObject *cxoObjectType_repr(cxoObjectType*);
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,
METH_VARARGS | METH_KEYWORDS },
{ 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 },
{ "name", T_OBJECT, offsetof(cxoObjectType, name), READONLY },
{ "attributes", T_OBJECT, offsetof(cxoObjectType, attributes), READONLY },
{ "elementType", T_OBJECT, offsetof(cxoObjectType, elementType),
READONLY },
{ "iscollection", T_BOOL, offsetof(cxoObjectType, isCollection),
READONLY },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of calculated members
//-----------------------------------------------------------------------------
static PyGetSetDef cxoCalcMembers[] = {
{ "element_type", (getter) cxoObjectType_getElementType, 0, 0, 0 },
{ NULL }
};
//-----------------------------------------------------------------------------
// Python type declarations
//-----------------------------------------------------------------------------
@ -58,8 +67,10 @@ PyTypeObject cxoPyTypeObjectType = {
.tp_repr = (reprfunc) cxoObjectType_repr,
.tp_call = (ternaryfunc) cxoObjectType_newObject,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_methods = cxoObjectTypeMethods,
.tp_members = cxoObjectTypeMembers,
.tp_methods = cxoMethods,
.tp_members = cxoMembers,
.tp_getset = cxoCalcMembers,
.tp_richcompare = (richcmpfunc) cxoObjectType_richCompare
};
@ -89,14 +100,21 @@ static int cxoObjectType_initialize(cxoObjectType *objType,
if (!objType->name)
return -1;
objType->isCollection = info.isCollection;
objType->elementOracleTypeNum = info.elementTypeInfo.oracleTypeNum;
objType->elementTransformNum =
cxoTransform_getNumFromDataTypeInfo(&info.elementTypeInfo);
if (info.elementTypeInfo.objectType) {
objType->elementType = (PyObject*) cxoObjectType_new(connection,
info.elementTypeInfo.objectType);
if (!objType->elementType)
if (info.isCollection) {
objType->elementOracleTypeNum = info.elementTypeInfo.oracleTypeNum;
objType->elementTransformNum =
cxoTransform_getNumFromDataTypeInfo(&info.elementTypeInfo);
objType->elementDbType =
cxoDbType_fromTransformNum(objType->elementTransformNum);
if (!objType->elementDbType)
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
@ -205,11 +223,34 @@ static void cxoObjectType_free(cxoObjectType *objType)
Py_CLEAR(objType->name);
Py_CLEAR(objType->attributes);
Py_CLEAR(objType->attributesByName);
Py_CLEAR(objType->elementType);
Py_CLEAR(objType->elementObjectType);
Py_CLEAR(objType->elementDbType);
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()
// 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()
// 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)
#endif
// forward declarations
static Py_ssize_t cxoTransform_calculateSize(PyObject *value,
cxoTransformNum transformNum);
static cxoTransformNum cxoTransform_getNumFromPythonType(PyTypeObject *type);
//-----------------------------------------------------------------------------
// Types
//-----------------------------------------------------------------------------
@ -175,10 +181,39 @@ static const cxoTransform cxoAllTransforms[] = {
CXO_TRANSFORM_TIMESTAMP_LTZ,
DPI_ORACLE_TYPE_TIMESTAMP_LTZ,
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()
// 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_TIMESTAMP:
case CXO_TRANSFORM_TIMESTAMP_LTZ:
case CXO_TRANSFORM_TIMESTAMP_TZ:
if (PyDateTime_Check(pyValue)) {
memset(&dbValue->asTimestamp, 0, sizeof(dbValue->asTimestamp));
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()
// 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:
return CXO_TRANSFORM_TIMESTAMP;
case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
return CXO_TRANSFORM_TIMESTAMP_TZ;
case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
return CXO_TRANSFORM_TIMESTAMP_LTZ;
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.
//-----------------------------------------------------------------------------
cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type)
static cxoTransformNum cxoTransform_getNumFromPythonType(PyTypeObject *type)
{
if (type == &PyUnicode_Type)
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)
return CXO_TRANSFORM_BINARY;
if (type == &cxoPyTypeBinaryVar)
return CXO_TRANSFORM_BINARY;
if (type == &PyFloat_Type)
return CXO_TRANSFORM_FLOAT;
if (type == &PyLong_Type)
return CXO_TRANSFORM_INT;
if (type == cxoPyTypeDecimal)
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)
return CXO_TRANSFORM_BOOLEAN;
if (type == &cxoPyTypeBooleanVar)
return CXO_TRANSFORM_BOOLEAN;
if (type == PyDateTimeAPI->DateType)
return CXO_TRANSFORM_DATE;
if (type == PyDateTimeAPI->DateTimeType)
return CXO_TRANSFORM_DATETIME;
if (type == &cxoPyTypeDateTimeVar)
return CXO_TRANSFORM_DATETIME;
if (type == &cxoPyTypeTimestampVar)
return CXO_TRANSFORM_TIMESTAMP;
if (type == PyDateTimeAPI->DeltaType)
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;
}
//-----------------------------------------------------------------------------
// cxoTransform_getNumFromValue()
// Get the appropriate transformation to use for the specified Python object.
// cxoTransform_getNumFromPythonValue()
// 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;
@ -542,19 +566,144 @@ cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql)
return CXO_TRANSFORM_OBJECT;
if (PyObject_TypeCheck(value, &cxoPyTypeLob)) {
lob = (cxoLob*) value;
if (lob->oracleTypeNum == DPI_ORACLE_TYPE_CLOB)
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 lob->dbType->defaultTransformNum;
}
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()
// Get type information for the specified transform. The transform number is
@ -618,17 +767,16 @@ PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
cxoConnection *connection, cxoObjectType *objType,
dpiDataBuffer *dbValue, const char *encodingErrors)
{
const cxoTransform *transform;
PyObject *stringObj, *result;
dpiIntervalDS *intervalDS;
dpiTimestamp *timestamp;
uint32_t rowidLength;
cxoDbType *dbType;
const char *rowid;
cxoCursor *cursor;
dpiBytes *bytes;
int32_t seconds;
transform = &cxoAllTransforms[transformNum];
switch (transformNum) {
case CXO_TRANSFORM_BINARY:
case CXO_TRANSFORM_LONG_BINARY:
@ -638,8 +786,8 @@ PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
case CXO_TRANSFORM_BLOB:
case CXO_TRANSFORM_CLOB:
case CXO_TRANSFORM_NCLOB:
return cxoLob_new(connection, transform->oracleTypeNum,
dbValue->asLOB);
dbType = cxoDbType_fromTransformNum(transformNum);
return cxoLob_new(connection, dbType, dbValue->asLOB);
case CXO_TRANSFORM_BOOLEAN:
if (dbValue->asBoolean)
Py_RETURN_TRUE;
@ -660,6 +808,7 @@ PyObject *cxoTransform_toPython(cxoTransformNum transformNum,
case CXO_TRANSFORM_DATETIME:
case CXO_TRANSFORM_TIMESTAMP:
case CXO_TRANSFORM_TIMESTAMP_LTZ:
case CXO_TRANSFORM_TIMESTAMP_TZ:
timestamp = &dbValue->asTimestamp;
return PyDateTime_FromDateAndTime(timestamp->year,
timestamp->month, timestamp->day, timestamp->hour,

View File

@ -15,7 +15,7 @@
#include "cxoModule.h"
//-----------------------------------------------------------------------------
// Declaration of common variable functions.
// forward declaration of functions
//-----------------------------------------------------------------------------
static void cxoVar_free(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_externalGetActualElements(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 },
{ "inconverter", T_OBJECT, offsetof(cxoVar, inConverter), 0 },
{ "numElements", T_INT, offsetof(cxoVar, allocatedElements),
READONLY },
{ "outconverter", T_OBJECT, offsetof(cxoVar, outConverter), 0 },
{ "size", T_INT, offsetof(cxoVar, size), READONLY },
{ "type", T_OBJECT, offsetof(cxoVar, objectType), READONLY },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of calculated members for variables
// declaration of calculated members
//-----------------------------------------------------------------------------
static PyGetSetDef cxoVarCalcMembers[] = {
static PyGetSetDef cxoCalcMembers[] = {
{ "actualElements", (getter) cxoVar_externalGetActualElements, 0, 0, 0 },
{ "type", (getter) cxoVar_getType, 0, 0, 0 },
{ "values", (getter) cxoVar_externalGetValues, 0, 0, 0 },
{ NULL }
};
//-----------------------------------------------------------------------------
// declaration of methods for variables
// declaration of methods
//-----------------------------------------------------------------------------
static PyMethodDef cxoVarMethods[] = {
{ "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 INTERNAL_NAME = { \
PyVarObject_HEAD_INIT(NULL, 0) \
.tp_name = "cx_Oracle." #EXTERNAL_NAME, \
.tp_basicsize = sizeof(cxoVar), \
.tp_dealloc = (destructor) cxoVar_free, \
.tp_repr = (reprfunc) cxoVar_repr, \
.tp_flags = Py_TPFLAGS_DEFAULT, \
.tp_methods = cxoVarMethods, \
.tp_members = cxoVarMembers, \
.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)
PyTypeObject cxoPyTypeVar = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "cx_Oracle.Var",
.tp_basicsize = sizeof(cxoVar),
.tp_dealloc = (destructor) cxoVar_free,
.tp_repr = (reprfunc) cxoVar_repr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_methods = cxoVarMethods,
.tp_members = cxoMembers,
.tp_getset = cxoCalcMembers
};
//-----------------------------------------------------------------------------
// cxoVar_new()
// Allocate a new variable.
//-----------------------------------------------------------------------------
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
Py_ssize_t size, int isArray, cxoObjectType *objType)
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements,
cxoTransformNum transformNum, Py_ssize_t size, int isArray,
cxoObjectType *objType)
{
dpiObjectType *typeHandle = NULL;
dpiOracleTypeNum oracleTypeNum;
dpiNativeTypeNum nativeTypeNum;
cxoVar *var;
// attempt to allocate the object
var = (cxoVar*) type->pythonType->tp_alloc(type->pythonType, 0);
var = (cxoVar*) cxoPyTypeVar.tp_alloc(&cxoPyTypeVar, 0);
if (!var)
return NULL;
@ -130,15 +108,25 @@ cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
if (numElements == 0)
numElements = 1;
var->allocatedElements = (uint32_t) numElements;
var->type = type;
var->size = (size == 0) ? type->size : (uint32_t) size;
var->transformNum = transformNum;
var->size = (uint32_t) size;
if (var->size == 0)
var->size = cxoTransform_getDefaultSize(transformNum);
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
cxoTransform_getTypeInfo(type->transformNum, &oracleTypeNum,
&nativeTypeNum);
cxoTransform_getTypeInfo(transformNum, &oracleTypeNum,
&var->nativeTypeNum);
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) {
cxoError_raiseAndReturnNull();
Py_DECREF(var);
@ -174,6 +162,7 @@ static void cxoVar_free(cxoVar *var)
Py_CLEAR(var->inConverter);
Py_CLEAR(var->outConverter);
Py_CLEAR(var->objectType);
Py_CLEAR(var->dbType);
Py_TYPE(var)->tp_free((PyObject*) var);
}
@ -184,29 +173,7 @@ static void cxoVar_free(cxoVar *var)
//-----------------------------------------------------------------------------
int cxoVar_check(PyObject *object)
{
PyTypeObject *objectType = Py_TYPE(object);
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);
return (Py_TYPE(object) == &cxoPyTypeVar);
}
@ -219,7 +186,7 @@ cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
{
PyObject *result, *inputTypeHandler = NULL;
cxoObjectType *objType = NULL;
cxoVarType *varType;
cxoTransformNum transformNum;
Py_ssize_t size;
cxoObject *obj;
int isArray;
@ -253,15 +220,15 @@ cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
}
// default processing
varType = cxoVarType_fromPythonValue(value,
&isArray, &size, &numElements, cursor->stmtInfo.isPLSQL);
if (!varType)
if (cxoTransform_getNumFromValue(value, &isArray, &size, &numElements,
cursor->stmtInfo.isPLSQL, &transformNum) < 0)
return NULL;
if (varType->transformNum == CXO_TRANSFORM_OBJECT) {
if (transformNum == CXO_TRANSFORM_OBJECT) {
obj = (cxoObject*) value;
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 *typeObj, *numElementsObj;
cxoTransformNum transformNum;
cxoObjectType *objType;
cxoVarType *varType;
uint32_t numElements;
int ok;
@ -282,9 +249,6 @@ static cxoVar *cxoVar_newArrayByType(cxoCursor *cursor,
ok = (PyList_GET_SIZE(value) == 2);
if (ok) {
typeObj = PyList_GET_ITEM(value, 0);
ok = PyType_Check(typeObj);
}
if (ok) {
numElementsObj = PyList_GET_ITEM(value, 1);
ok = PyLong_Check(numElementsObj);
}
@ -295,13 +259,12 @@ static cxoVar *cxoVar_newArrayByType(cxoCursor *cursor,
}
// create variable
varType = cxoVarType_fromPythonType(typeObj, &objType);
if (!varType)
if (cxoTransform_getNumFromType(typeObj, &transformNum, &objType) < 0)
return NULL;
numElements = PyLong_AsLong(numElementsObj);
if (PyErr_Occurred())
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,
uint32_t numElements)
{
cxoTransformNum transformNum;
cxoObjectType *objType;
cxoVarType *varType;
long size;
// passing an integer is assumed to be a string
@ -321,9 +284,8 @@ cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
size = PyLong_AsLong(value);
if (PyErr_Occurred())
return NULL;
varType = cxoVarType_fromPythonType((PyObject*) &PyUnicode_Type,
&objType);
return cxoVar_new(cursor, numElements, varType, size, 0, objType);
return cxoVar_new(cursor, numElements, CXO_TRANSFORM_STRING, size, 0,
NULL);
}
// passing an array of two elements to define an array
@ -336,11 +298,11 @@ cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
return (cxoVar*) value;
}
// everything else ought to be a Python type or object type
varType = cxoVarType_fromPythonType(value, &objType);
if (!varType)
// everything else ought to be a Python type, database type constant or
// object type
if (cxoTransform_getNumFromType(value, &transformNum, &objType) < 0)
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];
if (data->isNull)
Py_RETURN_NONE;
value = cxoTransform_toPython(var->type->transformNum, var->connection,
value = cxoTransform_toPython(var->transformNum, var->connection,
var->objectType, &data->value, var->encodingErrors);
if (value) {
switch (var->type->transformNum) {
switch (var->transformNum) {
case CXO_TRANSFORM_BFILE:
case CXO_TRANSFORM_BLOB:
case CXO_TRANSFORM_CLOB:
@ -491,7 +453,7 @@ static int cxoVar_setValueBytes(cxoVar *var, uint32_t pos, dpiData *data,
int status;
if (buffer->size > var->bufferSize) {
cxoTransform_getTypeInfo(var->type->transformNum, &oracleTypeNum,
cxoTransform_getTypeInfo(var->transformNum, &oracleTypeNum,
&nativeTypeNum);
if (dpiConn_newVar(var->connection->handle, oracleTypeNum,
nativeTypeNum, var->allocatedElements, buffer->size, 0,
@ -605,18 +567,18 @@ static int cxoVar_setSingleValue(cxoVar *var, uint32_t arrayPos,
data = &var->data[arrayPos];
data->isNull = (value == Py_None);
if (!data->isNull) {
if (var->type->transformNum == CXO_TRANSFORM_CURSOR)
if (var->transformNum == CXO_TRANSFORM_CURSOR)
result = cxoVar_setValueCursor(var, arrayPos, data, value);
else {
cxoBuffer_init(&buffer);
if (var->type->size > 0)
if (var->nativeTypeNum == DPI_NATIVE_TYPE_BYTES)
dbValue = &tempDbValue;
else dbValue = &data->value;
result = cxoTransform_fromPython(var->type->transformNum,
result = cxoTransform_fromPython(var->transformNum,
&nativeTypeNum, value, dbValue, &buffer,
var->connection->encodingInfo.encoding,
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);
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()
// Return a string representation of the variable.
//-----------------------------------------------------------------------------
static PyObject *cxoVar_repr(cxoVar *var)
{
PyObject *value, *module, *name, *result;
PyObject *value, *module, *name, *result, *typeName;
uint32_t numElements;
if (var->isArray) {
@ -780,14 +759,22 @@ static PyObject *cxoVar_repr(cxoVar *var)
else value = cxoVar_getArrayValue(var, var->allocatedElements, NULL);
if (!value)
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);
return NULL;
}
result = cxoUtils_formatString("<%s.%s with value %r>",
PyTuple_Pack(3, module, name, value));
if (cxoUtils_getModuleAndName(Py_TYPE(var), &module, &name) < 0) {
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(name);
Py_DECREF(value);
Py_DECREF(typeName);
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):
"test binding out a boolean value (False)"
result = self.cursor.callfunc("pkg_TestBooleans.IsLessThan10",
cx_Oracle.BOOLEAN, (15,))
cx_Oracle.DB_TYPE_BOOLEAN, (15,))
self.assertEqual(result, False)
def testBindOutTrue(self):

View File

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

View File

@ -613,7 +613,8 @@ class TestCase(TestEnv.BaseTestCase):
self.cursor.parse(sql)
self.assertEqual(self.cursor.statement, sql)
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):
"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.
#
@ -26,7 +26,7 @@ class TestCase(TestEnv.BaseTestCase):
end;""",
cursor = cursor)
self.assertEqual(cursor.description,
[ ('STRINGVALUE', cx_Oracle.FIXED_CHAR, 1,
[ ('STRINGVALUE', cx_Oracle.DB_TYPE_CHAR, 1,
TestEnv.GetCharSetRatio(), None, None, 1) ])
self.assertEqual(cursor.fetchall(), [('X',)])
@ -36,8 +36,8 @@ class TestCase(TestEnv.BaseTestCase):
self.assertEqual(cursor.description, None)
self.cursor.callproc("pkg_TestRefCursors.TestOutCursor", (2, cursor))
self.assertEqual(cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('STRINGCOL', cx_Oracle.STRING, 20, 20 *
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('STRINGCOL', cx_Oracle.DB_TYPE_VARCHAR, 20, 20 *
TestEnv.GetCharSetRatio(), None, None, 0) ])
self.assertEqual(cursor.fetchall(),
[ (1, 'String 1'), (2, 'String 2') ])
@ -80,9 +80,9 @@ class TestCase(TestEnv.BaseTestCase):
from TestNumbers
order by IntCol""")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('CURSORVALUE', cx_Oracle.CURSOR, None, None, None, None,
1) ])
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('CURSORVALUE', cx_Oracle.DB_TYPE_CURSOR, None, None, None,
None, 1) ])
for i in range(1, 11):
number, cursor = self.cursor.fetchone()
self.assertEqual(number, i)
@ -90,4 +90,3 @@ class TestCase(TestEnv.BaseTestCase):
if __name__ == "__main__":
TestEnv.RunTestCases()

View File

@ -174,7 +174,8 @@ class TestCase(TestEnv.BaseTestCase):
stringValue = "The string that will be verified"
obj = typeObj.newobject()
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("""
insert into TestObjects (IntCol, ObjectCol)
values (4, :obj)

View File

@ -215,9 +215,9 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate"
self.cursor.execute("select * from TestDates")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('DATECOL', cx_Oracle.DATETIME, 23, None, None, None, 0),
('NULLABLECOL', cx_Oracle.DATETIME, 23, None, None, None,
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('DATECOL', cx_Oracle.DB_TYPE_DATE, 23, None, None, None, 0),
('NULLABLECOL', cx_Oracle.DB_TYPE_DATE, 23, None, None, None,
1) ])
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):
"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("""
select * from TestIntervals
where IntervalCol = :value""",
@ -44,7 +44,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindNull(self):
"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("""
select * from TestIntervals
where IntervalCol = :value""",
@ -53,7 +53,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutSetInputSizes(self):
"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("""
begin
:value := to_dsinterval('8 09:24:18.123789');
@ -64,7 +64,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutSetInputSizes(self):
"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("""
begin
:value := :value + to_dsinterval('5 08:30:00');
@ -75,7 +75,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutFractionalSecond(self):
"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("""
begin
:value := :value + to_dsinterval('5 08:30:00');
@ -87,7 +87,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutVar(self):
"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("""
begin
:value := to_dsinterval('15 18:35:45.586');
@ -99,7 +99,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutVarDirectSet(self):
"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))
self.cursor.execute("""
begin
@ -113,9 +113,11 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate"
self.cursor.execute("select * from TestIntervals")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('INTERVALCOL', cx_Oracle.INTERVAL, None, None, 2, 6, 0),
('NULLABLECOL', cx_Oracle.INTERVAL, None, None, 2, 6, 1) ])
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('INTERVALCOL', cx_Oracle.DB_TYPE_INTERVAL_DS, None, None, 2,
6, 0),
('NULLABLECOL', cx_Oracle.DB_TYPE_INTERVAL_DS, None, None, 2,
6, 1) ])
def testFetchAll(self):
"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.
#
@ -117,8 +117,10 @@ class TestCase(TestEnv.BaseTestCase):
def __ValidateQuery(self, rows, lobType):
longString = ""
dbType = getattr(cx_Oracle, "DB_TYPE_" + lobType)
for row in rows:
integerValue, lob = row
self.assertEqual(lob.type, dbType)
if integerValue == 0:
self.assertEqual(lob.size(), 0)
expectedValue = ""
@ -158,16 +160,17 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate for BLOBs"
self.cursor.execute("select * from TestBLOBs")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('BLOBCOL', cx_Oracle.BLOB, None, None, None, None, 0) ])
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('BLOBCOL', cx_Oracle.DB_TYPE_BLOB, None, None, None, None,
0) ])
def testBLOBsDirect(self):
"test binding and fetching BLOB data (directly)"
self.__PerformTest("BLOB", cx_Oracle.BLOB)
self.__PerformTest("BLOB", cx_Oracle.DB_TYPE_BLOB)
def testBLOBsIndirect(self):
"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):
"test operations on BLOBs"
@ -177,16 +180,17 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate for CLOBs"
self.cursor.execute("select * from TestCLOBs")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('CLOBCOL', cx_Oracle.CLOB, None, None, None, None, 0) ])
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('CLOBCOL', cx_Oracle.DB_TYPE_CLOB, None, None, None, None,
0) ])
def testCLOBsDirect(self):
"test binding and fetching CLOB data (directly)"
self.__PerformTest("CLOB", cx_Oracle.CLOB)
self.__PerformTest("CLOB", cx_Oracle.DB_TYPE_CLOB)
def testCLOBsIndirect(self):
"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):
"test operations on CLOBs"
@ -215,12 +219,13 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate for NCLOBs"
self.cursor.execute("select * from TestNCLOBs")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('NCLOBCOL', cx_Oracle.NCLOB, None, None, None, None, 0) ])
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('NCLOBCOL', cx_Oracle.DB_TYPE_NCLOB, None, None, None, None,
0) ])
def testNCLOBsDirect(self):
"test binding and fetching NCLOB data (directly)"
self.__PerformTest("NCLOB", cx_Oracle.NCLOB)
self.__PerformTest("NCLOB", cx_Oracle.DB_TYPE_NCLOB)
def testNCLOBDifferentEncodings(self):
"test binding and fetching NCLOB data (different encodings)"
@ -230,11 +235,11 @@ class TestCase(TestEnv.BaseTestCase):
value = u"\u03b4\u4e2a"
cursor = connection.cursor()
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("select NCLOBCol from TestNCLOBs")
nclob, = cursor.fetchone()
cursor.setinputsizes(val = cx_Oracle.NCHAR)
cursor.setinputsizes(val = cx_Oracle.DB_TYPE_NVARCHAR)
cursor.execute("update TestNCLOBs set NCLOBCol = :val",
val = nclob.read() + value)
cursor.execute("select NCLOBCol from TestNCLOBs")
@ -243,7 +248,7 @@ class TestCase(TestEnv.BaseTestCase):
def testNCLOBsIndirect(self):
"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):
"test operations on NCLOBs"
@ -271,7 +276,7 @@ class TestCase(TestEnv.BaseTestCase):
def testAssignStringBeyondArraySize(self):
"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")
if __name__ == "__main__":

View File

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

View File

@ -43,7 +43,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindUnicode(self):
"test binding in a unicode"
self.cursor.setinputsizes(value = cx_Oracle.NCHAR)
self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_NVARCHAR)
self.cursor.execute("""
select * from TestUnicodes
where UnicodeCol = :value""",
@ -52,8 +52,8 @@ class TestCase(TestEnv.BaseTestCase):
def testBindDifferentVar(self):
"test binding a different variable on second execution"
retval_1 = self.cursor.var(cx_Oracle.NCHAR, 30)
retval_2 = 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.DB_TYPE_NVARCHAR, 30)
self.cursor.execute(r"begin :retval := unistr('Called \3042'); end;",
retval = retval_1)
self.assertEqual(retval_1.getvalue(), u"Called \u3042")
@ -63,7 +63,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindUnicodeAfterNumber(self):
"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")
self.cursor.setinputsizes(value = cx_Oracle.NUMBER)
self.cursor.execute("""
@ -76,7 +76,7 @@ class TestCase(TestEnv.BaseTestCase):
"test binding in a unicode array"
returnValue = self.cursor.var(cx_Oracle.NUMBER)
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 = """
begin
:retval := pkg_TestUnicodeArrays.TestInArrays(
@ -88,7 +88,7 @@ class TestCase(TestEnv.BaseTestCase):
array = arrayVar)
self.assertEqual(returnValue.getvalue(), 116)
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,
integerValue = 8,
array = arrayVar)
@ -97,7 +97,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindUnicodeArrayBySizes(self):
"test binding in a unicode array (with setinputsizes)"
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]
self.cursor.execute("""
begin
@ -112,7 +112,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindUnicodeArrayByVar(self):
"test binding in a unicode array (with arrayvar)"
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])
self.cursor.execute("""
begin
@ -126,7 +126,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutUnicodeArrayByVar(self):
"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]
format = u"Converted element \u3042 # %d originally had length %d"
expectedData = [format % (i, len(originalData[i - 1])) \
@ -142,7 +142,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutUnicodeArrayByVar(self):
"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"
expectedData = [format % i for i in range(1, 7)]
self.cursor.execute("""
@ -163,7 +163,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutSetInputSizesByType(self):
"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"""
begin
:value := unistr('TSI \3042');
@ -172,7 +172,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutSetInputSizesByType(self):
"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"""
begin
:value := :value || unistr(' TSI \3042');
@ -183,7 +183,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutVar(self):
"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"""
begin
:value := unistr('TSI (VAR) \3042');
@ -193,7 +193,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutVarDirectSet(self):
"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")
self.cursor.execute(r"""
begin
@ -206,11 +206,13 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate"
self.cursor.execute("select * from TestUnicodes")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('UNICODECOL', cx_Oracle.NCHAR, 20, 80, None, None, 0),
('FIXEDUNICODECOL', cx_Oracle.FIXED_NCHAR, 40, 160, None,
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('UNICODECOL', cx_Oracle.DB_TYPE_NVARCHAR, 20, 80, None,
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):
"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.
#
@ -19,7 +19,8 @@ class TestCase(TestEnv.BaseTestCase):
def outputTypeHandlerNativeInt(self, cursor, name, defaultType, size,
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,
precision, scale):
@ -272,12 +273,15 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate"
self.cursor.execute("select * from TestNumbers")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('LONGINTCOL', cx_Oracle.NUMBER, 17, None, 16, 0, 0),
('NUMBERCOL', cx_Oracle.NUMBER, 13, None, 9, 2, 0),
('FLOATCOL', cx_Oracle.NUMBER, 127, None, 126, -127, 0),
('UNCONSTRAINEDCOL', cx_Oracle.NUMBER, 127, None, 0, -127, 0),
('NULLABLECOL', cx_Oracle.NUMBER, 39, None, 38, 0, 1) ])
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('LONGINTCOL', cx_Oracle.DB_TYPE_NUMBER, 17, None, 16, 0, 0),
('NUMBERCOL', cx_Oracle.DB_TYPE_NUMBER, 13, None, 9, 2, 0),
('FLOATCOL', cx_Oracle.DB_TYPE_NUMBER, 127, None, 126, -127,
0),
('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):
"test that fetching all of the data returns the correct results"
@ -369,23 +373,28 @@ class TestCase(TestEnv.BaseTestCase):
def testStringFormat(self):
"test that string format is returned properly"
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)
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):
"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.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()
self.assertEqual(value, 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()
self.assertEqual(value, 1.5)
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()
self.assertEqual(str(value), str(float("NaN")))
@ -399,4 +408,3 @@ class TestCase(TestEnv.BaseTestCase):
if __name__ == "__main__":
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.
#
@ -50,7 +50,8 @@ class TestCase(TestEnv.BaseTestCase):
def testBindNullIn(self):
"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,
(var,))
self.assertEqual(result, "null")
@ -122,9 +123,11 @@ class TestCase(TestEnv.BaseTestCase):
from TestObjects
order by IntCol""")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('OBJECTCOL', cx_Oracle.OBJECT, None, None, None, None, 1),
('ARRAYCOL', cx_Oracle.OBJECT, None, None, None, None, 1) ])
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('OBJECTCOL', cx_Oracle.DB_TYPE_OBJECT, None, None, None,
None, 1),
('ARRAYCOL', cx_Oracle.DB_TYPE_OBJECT, None, None, None,
None, 1) ])
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,
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.schema, self.connection.username.upper())
self.assertEqual(typeObj.name, "UDT_OBJECT")
subObjectValueType = self.connection.gettype("UDT_SUBOBJECT")
subObjectArrayType = self.connection.gettype("UDT_OBJECTARRAY")
expectedAttributeNames = ["NUMBERVALUE", "STRINGVALUE",
"FIXEDCHARVALUE", "NSTRINGVALUE", "NFIXEDCHARVALUE",
"RAWVALUE", "INTVALUE", "SMALLINTVALUE", "REALVALUE",
@ -163,9 +168,23 @@ class TestCase(TestEnv.BaseTestCase):
"NCLOBVALUE", "BLOBVALUE", "SUBOBJECTVALUE", "SUBOBJECTARRAY"]
actualAttributeNames = [a.name for a in typeObj.attributes]
self.assertEqual(actualAttributeNames, expectedAttributeNames)
typeObj = self.connection.gettype("UDT_OBJECTARRAY")
self.assertEqual(typeObj.iscollection, True)
self.assertEqual(typeObj.attributes, [])
expectedAttributeTypes = [cx_Oracle.DB_TYPE_NUMBER,
cx_Oracle.DB_TYPE_VARCHAR, cx_Oracle.DB_TYPE_CHAR,
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):
"test object type data"
@ -316,7 +335,8 @@ class TestCase(TestEnv.BaseTestCase):
"test setting value of object variable to wrong object type"
wrongObjType = self.connection.gettype("UDT_OBJECTARRAY")
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)
def testStringFormat(self):

View File

@ -272,7 +272,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindLongString(self):
"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("""
declare
t_Temp varchar2(20000);
@ -295,15 +295,15 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate"
self.cursor.execute("select * from TestStrings")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('STRINGCOL', cx_Oracle.STRING, 20,
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('STRINGCOL', cx_Oracle.DB_TYPE_VARCHAR, 20,
20 * TestEnv.GetCharSetRatio(), None,
None, 0),
('RAWCOL', cx_Oracle.BINARY, 30, 30, None, None, 0),
('FIXEDCHARCOL', cx_Oracle.FIXED_CHAR, 40,
('RAWCOL', cx_Oracle.DB_TYPE_RAW, 30, 30, None, None, 0),
('FIXEDCHARCOL', cx_Oracle.DB_TYPE_CHAR, 40,
40 * TestEnv.GetCharSetRatio(),
None, None, 0),
('NULLABLECOL', cx_Oracle.STRING, 50,
('NULLABLECOL', cx_Oracle.DB_TYPE_VARCHAR, 50,
50 * TestEnv.GetCharSetRatio(), None,
None, 1) ])

View File

@ -45,7 +45,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindTimestamp(self):
"test binding in a timestamp"
self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP)
self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_TIMESTAMP)
self.cursor.execute("""
select * from TestTimestamps
where TimestampCol = :value""",
@ -54,7 +54,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindNull(self):
"test binding in a null"
self.cursor.setinputsizes(value = cx_Oracle.TIMESTAMP)
self.cursor.setinputsizes(value = cx_Oracle.DB_TYPE_TIMESTAMP)
self.cursor.execute("""
select * from TestTimestamps
where TimestampCol = :value""",
@ -63,7 +63,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutSetInputSizes(self):
"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("""
begin
:value := to_timestamp('20021209', 'YYYYMMDD');
@ -73,7 +73,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutSetInputSizes(self):
"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("""
begin
:value := :value + 5.25;
@ -84,7 +84,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindOutVar(self):
"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("""
begin
:value := to_date('20021231 12:31:00',
@ -96,7 +96,7 @@ class TestCase(TestEnv.BaseTestCase):
def testBindInOutVarDirectSet(self):
"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))
self.cursor.execute("""
begin
@ -110,9 +110,11 @@ class TestCase(TestEnv.BaseTestCase):
"test cursor description is accurate"
self.cursor.execute("select * from TestTimestamps")
self.assertEqual(self.cursor.description,
[ ('INTCOL', cx_Oracle.NUMBER, 10, None, 9, 0, 0),
('TIMESTAMPCOL', cx_Oracle.TIMESTAMP, 23, None, 0, 6, 0),
('NULLABLECOL', cx_Oracle.TIMESTAMP, 23, None, 0, 6, 1) ])
[ ('INTCOL', cx_Oracle.DB_TYPE_NUMBER, 10, None, 9, 0, 0),
('TIMESTAMPCOL', cx_Oracle.DB_TYPE_TIMESTAMP, 23, None, 0, 6,
0),
('NULLABLECOL', cx_Oracle.DB_TYPE_TIMESTAMP, 23, None, 0, 6,
1) ])
def testFetchAll(self):
"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.
#
@ -36,6 +36,7 @@ moduleNames = [
"Cursor",
"CursorVar",
"DateTimeVar",
"DbTypes",
"DMLReturning",
"Error",
"IntervalVar",