Support for enhanced metadata.
This commit is contained in:
parent
6646529ec5
commit
6caccf9b51
|
@ -756,14 +756,19 @@ Connection Attributes
|
|||
|
||||
This read-write attribute specifies a method called for each column that is
|
||||
going to be fetched from any cursor associated with this connection. The
|
||||
method signature is handler(cursor, name, defaultType, length, precision,
|
||||
scale) and the return value is expected to be a variable object or None in
|
||||
which case a default variable object will be created. If this attribute is
|
||||
None, the default behavior will take place for all columns fetched from
|
||||
cursors.
|
||||
method signature is ``handler(cursor, metadata)`` and the return value is
|
||||
expected to be a :ref:`variable object<varobj>` or None in which case a
|
||||
default variable object will be created. If this attribute is None, the
|
||||
default behavior will take place for all columns fetched from cursors.
|
||||
|
||||
See :ref:`outputtypehandlers`.
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
|
||||
The method signature was changed. The previous signature
|
||||
``handler(cursor, name, default_type, length, precision, scale)`` will
|
||||
still work but is deprecated and will be removed in a future version.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
|
|
@ -496,14 +496,10 @@ Cursor Attributes
|
|||
|
||||
.. data:: Cursor.description
|
||||
|
||||
This read-only attribute is a sequence of 7-item sequences. Each of these
|
||||
sequences contains information describing one result column: (name, type,
|
||||
display_size, internal_size, precision, scale, null_ok). This attribute
|
||||
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 :ref:`database type constants <dbtypes>`
|
||||
defined at the module level.
|
||||
This read-only attribute is a sequence of :ref:`FetchInfo<fetchinfoobj>`
|
||||
objects. This attribute 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.
|
||||
|
||||
.. attribute:: Cursor.fetchvars
|
||||
|
||||
|
@ -541,13 +537,19 @@ Cursor Attributes
|
|||
|
||||
This read-write attribute specifies a method called for each column that is
|
||||
to be fetched from this cursor. The method signature is
|
||||
handler(cursor, name, defaultType, length, precision, scale) and the return
|
||||
value is expected to be a variable object or None in which case a default
|
||||
variable object will be created. If this attribute is None, then the default
|
||||
handler(cursor, metadata) and the return value is expected to be a
|
||||
:ref:`variable object<varobj>` or None in which case a default variable
|
||||
object will be created. If this attribute is None, then the default
|
||||
behavior will take place for all columns fetched from this cursor.
|
||||
|
||||
See :ref:`outputtypehandlers`.
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
|
||||
The method signature was changed. The previous signature
|
||||
handler(cursor, name, default_type, length, precision, scale) will
|
||||
still work but is deprecated and will be removed in a future version.
|
||||
|
||||
.. note::
|
||||
|
||||
This attribute is an extension to the DB API definition.
|
||||
|
|
|
@ -9,6 +9,19 @@ when they were first deprecated and a comment on what should be used instead,
|
|||
if applicable. The most recent deprecations are listed first.
|
||||
|
||||
|
||||
.. list-table-with-summary:: Deprecated in python-oracledb 1.4
|
||||
:header-rows: 1
|
||||
:class: wy-table-responsive
|
||||
:summary: The first column, Name, displays the deprecated API name. The second column, Comments, includes information about when the API was deprecated and what API to use, if applicable.
|
||||
:name: _deprecations_1_4
|
||||
|
||||
* - Name
|
||||
- Comments
|
||||
* - Output type handler with arguments
|
||||
``handler(cursor, name, default_type, length, precision, scale)``
|
||||
- Replace with ``handler(cursor, metadata)``. See
|
||||
:ref:`outputtypehandlers`.
|
||||
|
||||
.. list-table-with-summary:: Deprecated in python-oracledb 1.0
|
||||
:header-rows: 1
|
||||
:class: wy-table-responsive
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
.. _fetchinfoobj:
|
||||
|
||||
**********************
|
||||
API: FetchInfo Objects
|
||||
**********************
|
||||
|
||||
These objects are created internally when a query is executed. They are found
|
||||
in the sequence :data:`Cursor.description`. For compatibility with the Python
|
||||
Database API, this object behaves as a 7-tuple containing the values for the
|
||||
attributes ``name``, ``type_code``, ``display_size``, ``internal_size``,
|
||||
``precision``, ``scale``, and ``null_ok`` in that order. For example, if
|
||||
``fetch_info`` is of type FetchInfo, then ``fetch_info[2]`` is the same as
|
||||
``fetch_info.display_size``.
|
||||
|
||||
.. note::
|
||||
|
||||
This object is an extension the DB API.
|
||||
|
||||
FetchInfo Attributes
|
||||
====================
|
||||
|
||||
.. attribute:: FetchInfo.display_size
|
||||
|
||||
This read-only attribute returns the display size of the column as mandated
|
||||
by the Python Database API.
|
||||
|
||||
.. attribute:: FetchInfo.internal_size
|
||||
|
||||
This read-only attribute returns the internal size of the column as
|
||||
mandated by the Python Database API.
|
||||
|
||||
.. attribute:: FetchInfo.is_json
|
||||
|
||||
This read-only attribute returns whether the column is known to contain
|
||||
JSON data. This will be ``true`` when the type code is
|
||||
``oracledb.DB_TYPE_JSON`` as well as when an "IS JSON" constraint is
|
||||
enabled on LOB and VARCHAR2 columns.
|
||||
|
||||
.. attribute:: FetchInfo.name
|
||||
|
||||
This read-only attribute returns the name of the column as mandated by the
|
||||
Python Database API.
|
||||
|
||||
.. attribute:: FetchInfo.null_ok
|
||||
|
||||
This read-only attribute returns whether nulls are allowed in the column as
|
||||
mandated by the Python Database API.
|
||||
|
||||
.. attribute:: FetchInfo.precision
|
||||
|
||||
This read-only attribute returns the precision of the column as mandated by
|
||||
the Python Database API.
|
||||
|
||||
.. attribute:: FetchInfo.scale
|
||||
|
||||
This read-only attribute returns the scale of the column as mandated by
|
||||
the Python Database API.
|
||||
|
||||
.. attribute:: FetchInfo.type
|
||||
|
||||
This read-only attribute returns the type of the column. This will be an
|
||||
:ref:`Oracle Object Type <dbobjecttype>` if the column contains Oracle
|
||||
objects; otherwise, it will be one of the :ref:`database type constants
|
||||
<dbtypes>` defined at the module level.
|
||||
|
||||
|
||||
.. attribute:: FetchInfo.type_code
|
||||
|
||||
This read-only attribute returns the type of the column as mandated by the
|
||||
Python Database API. The type will be one of the :ref:`database type
|
||||
constants <dbtypes>` defined at the module level.
|
|
@ -66,6 +66,7 @@ API Manual
|
|||
api_manual/connection_pool.rst
|
||||
api_manual/pool_params.rst
|
||||
api_manual/cursor.rst
|
||||
api_manual/fetch_info.rst
|
||||
api_manual/variable.rst
|
||||
api_manual/subscription.rst
|
||||
api_manual/lob.rst
|
||||
|
|
|
@ -70,6 +70,15 @@ Thick Mode Changes
|
|||
Common Changes
|
||||
++++++++++++++
|
||||
|
||||
#) Replaced fixed 7-tuple for the cursor metadata found in
|
||||
:data:`Cursor.description` with a class which provides additional
|
||||
information such as the database object type and whether the column
|
||||
contains JSON data.
|
||||
#) Changed the signature for output type handlers to
|
||||
``handler(cursor, metadata)`` where the ``metadata`` parameter is a
|
||||
:ref:`FetchInfo<fetchinfoobj>` object containing the same information found
|
||||
in :data:`Cursor.description`. The original signature for output type
|
||||
handlers is deprecated and will be removed in some future version.
|
||||
#) Added support for fetching VARCHAR2 and LOB columns which contain JSON (and
|
||||
have the "IS JSON" check constraint enabled) in the same way as columns of
|
||||
type JSON (which requires Oracle Database 21c or higher) are fetched. In
|
||||
|
|
|
@ -133,16 +133,16 @@ To convert numbers:
|
|||
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
|
||||
|
||||
# simple naive conversion
|
||||
def type_handler1(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_NUMBER:
|
||||
def type_handler1(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(oracledb.DB_TYPE_VARCHAR, arraysize=cursor.arraysize,
|
||||
outconverter=lambda v: v.replace('.', ','))
|
||||
outconverter=lambda v: v.replace('.', ','))
|
||||
|
||||
# locale conversion
|
||||
def type_handler2(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(default_type, arraysize=cursor.arraysize,
|
||||
outconverter=lambda v: locale.format_string("%g", v))
|
||||
def type_handler2(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
|
||||
outconverter=lambda v: locale.format_string("%g", v))
|
||||
|
||||
|
||||
connection = oracledb.connect(user="hr", password=userpwd,
|
||||
|
@ -186,16 +186,16 @@ To convert dates:
|
|||
locale_date_format = locale.nl_langinfo(locale.D_T_FMT)
|
||||
|
||||
# simple naive conversion
|
||||
def type_handler3(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_DATE:
|
||||
return cursor.var(default_type, arraysize=cursor.arraysize,
|
||||
outconverter=lambda v: v.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
def type_handler3(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_DATE:
|
||||
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
|
||||
outconverter=lambda v: v.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
|
||||
# locale conversion
|
||||
def type_handler4(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_DATE:
|
||||
return cursor.var(default_type, arraysize=cursor.arraysize,
|
||||
outconverter=lambda v: v.strftime(locale_date_format))
|
||||
if metadata.type_code is oracledb.DB_TYPE_DATE:
|
||||
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
|
||||
outconverter=lambda v: v.strftime(locale_date_format))
|
||||
|
||||
|
||||
connection = oracledb.connect(user="hr", password=userpwd,
|
||||
|
|
|
@ -107,12 +107,12 @@ handler:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_CLOB:
|
||||
def output_type_handler(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_CLOB:
|
||||
return cursor.var(oracledb.DB_TYPE_LONG, arraysize=cursor.arraysize)
|
||||
if default_type == oracledb.DB_TYPE_BLOB:
|
||||
if metadata.type_code is oracledb.DB_TYPE_BLOB:
|
||||
return cursor.var(oracledb.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
|
||||
if default_type == oracledb.DB_TYPE_NCLOB:
|
||||
if metadata.type_code is oracledb.DB_TYPE_NCLOB:
|
||||
return cursor.var(oracledb.DB_TYPE_LONG_NVARCHAR, arraysize=cursor.arraysize)
|
||||
|
||||
connection.outputtypehandler = output_type_handler
|
||||
|
|
|
@ -312,10 +312,10 @@ cursors created by that connection will have their fetch type handling changed.
|
|||
The output type handler is expected to be a function with the following
|
||||
signature::
|
||||
|
||||
handler(cursor, name, default_type, size, precision, scale)
|
||||
handler(cursor, metadata)
|
||||
|
||||
The parameters are the same information as the query column metadata found in
|
||||
:attr:`Cursor.description`.
|
||||
The metadata parameter is a :ref:`FetchInfo object<fetchinfoobj>`, which is the
|
||||
same value found in :attr:`Cursor.description`.
|
||||
|
||||
The function is called once for each column that is going to be
|
||||
fetched. The function is expected to return a :ref:`variable object <varobj>`
|
||||
|
@ -326,8 +326,8 @@ For example:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_NUMBER:
|
||||
def output_type_handler(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(oracledb.DB_TYPE_VARCHAR, arraysize=cursor.arraysize)
|
||||
|
||||
This output type handler is called once for each column in the SELECT query.
|
||||
|
@ -375,7 +375,7 @@ For example:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
def output_type_handler(cursor, metadata):
|
||||
|
||||
def out_converter(d):
|
||||
if isinstance(d, str):
|
||||
|
@ -383,7 +383,7 @@ For example:
|
|||
else:
|
||||
return f"{d} was not a string"
|
||||
|
||||
if default_type == oracledb.DB_TYPE_NUMBER:
|
||||
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(oracledb.DB_TYPE_VARCHAR,
|
||||
arraysize=cursor.arraysize, outconverter=out_converter)
|
||||
|
||||
|
@ -456,7 +456,7 @@ An example showing an :ref:`output type handler <outputtypehandlers>`, an
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
def output_type_handler(cursor, metadata):
|
||||
|
||||
def out_converter(d):
|
||||
if type(d) is str:
|
||||
|
@ -464,7 +464,7 @@ An example showing an :ref:`output type handler <outputtypehandlers>`, an
|
|||
else:
|
||||
return f"{d} was not a string"
|
||||
|
||||
if default_type == oracledb.DB_TYPE_NUMBER:
|
||||
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(oracledb.DB_TYPE_VARCHAR,
|
||||
arraysize=cursor.arraysize, outconverter=out_converter)
|
||||
|
||||
|
@ -532,8 +532,8 @@ to use an :ref:`output type handler <outputtypehandlers>` do the conversion.
|
|||
|
||||
import decimal
|
||||
|
||||
def number_to_decimal(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_NUMBER:
|
||||
def number_to_decimal(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(decimal.Decimal, arraysize=cursor.arraysize)
|
||||
|
||||
cursor.outputtypehandler = number_to_decimal
|
||||
|
@ -824,9 +824,8 @@ The following sample demonstrates how to use this feature:
|
|||
.. code-block:: python
|
||||
|
||||
# define output type handler
|
||||
def return_strings_as_bytes(cursor, name, default_type, size,
|
||||
precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_VARCHAR:
|
||||
def return_strings_as_bytes(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
|
||||
return cursor.var(str, arraysize=cursor.arraysize,
|
||||
bypass_decode=True)
|
||||
|
||||
|
@ -900,9 +899,10 @@ columns:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_VARCHAR:
|
||||
return cursor.var(default_type, size, arraysize=cursor.arraysize,
|
||||
def output_type_handler(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
|
||||
return cursor.var(metadata.type_code, size,
|
||||
arraysize=cursor.arraysize,
|
||||
encoding_errors="replace")
|
||||
|
||||
cursor.outputtypehandler = output_type_handler
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -52,7 +52,7 @@ class Cursor(oracledb.Cursor):
|
|||
if prepare_needed:
|
||||
description = self.description
|
||||
if description is not None:
|
||||
names = [d[0] for d in description]
|
||||
names = [d.name for d in description]
|
||||
self.rowfactory = collections.namedtuple("GenericQuery", names)
|
||||
return result
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -72,7 +72,7 @@ with connection.cursor() as cursor:
|
|||
|
||||
print("Fetch each row as a Dictionary")
|
||||
cursor.execute(sql)
|
||||
columns = [col[0] for col in cursor.description]
|
||||
columns = [col.name for col in cursor.description]
|
||||
cursor.rowfactory = lambda *args: dict(zip(columns, args))
|
||||
for row in cursor:
|
||||
print(row)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2021, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -39,9 +39,8 @@ if not sample_env.get_is_thin():
|
|||
|
||||
STRING_VAL = 'I bought a cafetière on the Champs-Élysées'
|
||||
|
||||
def return_strings_as_bytes(cursor, name, default_type, size, precision,
|
||||
scale):
|
||||
if default_type == oracledb.DB_TYPE_VARCHAR:
|
||||
def return_strings_as_bytes(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
|
||||
return cursor.var(str, arraysize=cursor.arraysize, bypass_decode=True)
|
||||
|
||||
connection = oracledb.connect(user=sample_env.get_main_user(),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2018, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2018, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -65,12 +65,9 @@ cursor = connection.cursor()
|
|||
# executed for a single transaction
|
||||
connection.autocommit = True
|
||||
|
||||
# define output type handler to fetch LOBs, avoiding the second round trip to
|
||||
# the database to read the LOB contents
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.BLOB:
|
||||
return cursor.var(oracledb.LONG_BINARY, arraysize=cursor.arraysize)
|
||||
connection.outputtypehandler = output_type_handler
|
||||
# do not fetch LOBs, avoiding the second round trip to the database to read the
|
||||
# LOB contents
|
||||
oracledb.defaults.fetch_lobs = False
|
||||
|
||||
# drop and create table
|
||||
print("Dropping and creating table...")
|
||||
|
|
|
@ -1315,8 +1315,8 @@ for row in cur.execute("select * from dept"):
|
|||
<p>Add an output type handler to the bottom of the file:</p>
|
||||
|
||||
<pre>
|
||||
<strong>def ReturnNumbersAsStrings(cursor, name, defaultType, size, precision, scale):
|
||||
if defaultType == oracledb.NUMBER:
|
||||
<strong>def ReturnNumbersAsStrings(cursor, metadata):
|
||||
if metdata.type_code is oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(str, 9, cursor.arraysize)
|
||||
|
||||
print("Output type handler output...")
|
||||
|
@ -1376,8 +1376,8 @@ import db_config
|
|||
con = oracledb.connect(user=db_config.user, password=db_config.pw, dsn=db_config.dsn)
|
||||
cur = con.cursor()
|
||||
|
||||
<strong>def ReturnNumbersAsDecimal(cursor, name, defaultType, size, precision, scale):
|
||||
if defaultType == oracledb.NUMBER:
|
||||
<strong>def ReturnNumbersAsDecimal(cursor, metadata):
|
||||
if metadata.type_code is oracledb.NUMBER:
|
||||
return cursor.var(str, 9, cursor.arraysize, outconverter=decimal.Decimal)
|
||||
|
||||
cur.outputtypehandler = ReturnNumbersAsDecimal</strong>
|
||||
|
@ -2228,10 +2228,9 @@ def SDOInputTypeHandler(cursor, value, numElements):
|
|||
return mySDO(int(DBobj.SDO_GTYPE), DBobj.SDO_ELEM_INFO.aslist(),
|
||||
DBobj.SDO_ORDINATES.aslist())</strong>
|
||||
|
||||
<strong>def SDOOutputTypeHandler(cursor, name, default_type, size, precision,
|
||||
scale):
|
||||
if default_type == oracledb.DB_TYPE_OBJECT:
|
||||
return cursor.var(obj_type, arraysize=cursor.arraysize,
|
||||
<strong>def SDOOutputTypeHandler(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_OBJECT:
|
||||
return cursor.var(metadata.type, arraysize=cursor.arraysize,
|
||||
outconverter=SDOOutConverter)</strong>
|
||||
|
||||
sdo = mySDO(2003, [1, 1003, 3], [1, 1, 5, 7]) # Python object
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2022, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -79,9 +79,9 @@ def input_type_handler(cursor, value, num_elements):
|
|||
inconverter=building_in_converter)
|
||||
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.STRING:
|
||||
return cursor.var(default_type, arraysize=cursor.arraysize,
|
||||
def output_type_handler(cursor, metadata):
|
||||
if metadata.type_code is oracledb.STRING:
|
||||
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
|
||||
outconverter=Building.from_json)
|
||||
|
||||
connection = oracledb.connect(user=sample_env.get_main_user(),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# Portions Copyright 2007-2015, Anthony Tuininga. All rights reserved.
|
||||
#
|
||||
|
@ -82,9 +82,9 @@ def input_type_handler(cursor, value, num_elements):
|
|||
return cursor.var(obj_type, arraysize=num_elements,
|
||||
inconverter=building_in_converter)
|
||||
|
||||
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.OBJECT:
|
||||
return cursor.var(obj_type, arraysize=cursor.arraysize,
|
||||
def output_type_handler(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_OBJECT:
|
||||
return cursor.var(metadata.type, arraysize=cursor.arraysize,
|
||||
outconverter=building_out_converter)
|
||||
|
||||
buildings = [
|
||||
|
|
|
@ -76,6 +76,10 @@ from .dbobject import (
|
|||
DbObjectType as DbObjectType
|
||||
)
|
||||
|
||||
from .fetch_info import (
|
||||
FetchInfo as FetchInfo
|
||||
)
|
||||
|
||||
from .var import (
|
||||
Var as Var
|
||||
)
|
||||
|
@ -158,6 +162,7 @@ del package
|
|||
del exceptions, errors, connection, pool, constants, driver_mode, sys
|
||||
del constructors, dsn, lob, base_impl, thick_impl, thin_impl, utils, var
|
||||
del connect_params, pool_params, subscr, aq, soda, cursor, dbobject, future
|
||||
del fetch_info
|
||||
|
||||
# general aliases (for backwards compatibility)
|
||||
ObjectType = DbObjectType
|
||||
|
|
|
@ -268,6 +268,7 @@ cdef class BaseCursorImpl:
|
|||
public object outputtypehandler
|
||||
public object rowfactory
|
||||
public bint scrollable
|
||||
public list fetch_info_impls
|
||||
public list fetch_vars
|
||||
public list fetch_var_impls
|
||||
public list bind_vars
|
||||
|
@ -291,14 +292,14 @@ cdef class BaseCursorImpl:
|
|||
bint defer_type_assignment) except -1
|
||||
cdef int _close(self, bint in_del) except -1
|
||||
cdef int _create_fetch_var(self, object conn, object cursor,
|
||||
object type_handler, ssize_t pos,
|
||||
FetchInfo fetch_info) except -1
|
||||
object type_handler, bint uses_fetch_info,
|
||||
ssize_t pos, FetchInfoImpl fetch_info) except -1
|
||||
cdef object _create_row(self)
|
||||
cdef BaseVarImpl _create_var_impl(self, object conn)
|
||||
cdef int _fetch_rows(self, object cursor) except -1
|
||||
cdef BaseConnImpl _get_conn_impl(self)
|
||||
cdef object _get_input_type_handler(self)
|
||||
cdef object _get_output_type_handler(self)
|
||||
cdef object _get_output_type_handler(self, bint* uses_fetch_info)
|
||||
cdef int _init_fetch_vars(self, uint32_t num_columns) except -1
|
||||
cdef bint _is_plsql(self)
|
||||
cdef int _perform_binds(self, object conn, uint32_t num_execs) except -1
|
||||
|
@ -306,17 +307,17 @@ cdef class BaseCursorImpl:
|
|||
cdef int _verify_var(self, object var) except -1
|
||||
|
||||
|
||||
cdef class FetchInfo:
|
||||
cdef class FetchInfoImpl:
|
||||
cdef:
|
||||
int16_t _precision
|
||||
int16_t _scale
|
||||
uint32_t _buffer_size
|
||||
uint32_t _size
|
||||
bint _nulls_allowed
|
||||
str _name
|
||||
DbType _dbtype
|
||||
BaseDbObjectTypeImpl _objtype
|
||||
bint _is_json_col
|
||||
readonly int16_t precision
|
||||
readonly int16_t scale
|
||||
readonly uint32_t buffer_size
|
||||
readonly uint32_t size
|
||||
readonly bint nulls_allowed
|
||||
readonly str name
|
||||
readonly DbType dbtype
|
||||
readonly BaseDbObjectTypeImpl objtype
|
||||
readonly bint is_json
|
||||
|
||||
|
||||
cdef class BaseVarImpl:
|
||||
|
@ -337,7 +338,7 @@ cdef class BaseVarImpl:
|
|||
readonly BaseDbObjectTypeImpl objtype
|
||||
BaseConnImpl _conn_impl
|
||||
int _preferred_num_type
|
||||
FetchInfo _fetch_info
|
||||
FetchInfoImpl _fetch_info
|
||||
bint _is_value_set
|
||||
|
||||
cdef int _bind(self, object conn, BaseCursorImpl cursor,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -41,6 +41,7 @@ from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t
|
|||
import base64
|
||||
import datetime
|
||||
import decimal
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
@ -65,6 +66,7 @@ cdef type PY_TYPE_DB_OBJECT_TYPE
|
|||
cdef type PY_TYPE_LOB
|
||||
cdef type PY_TYPE_TIMEDELTA = datetime.timedelta
|
||||
cdef type PY_TYPE_VAR
|
||||
cdef type PY_TYPE_FETCHINFO
|
||||
|
||||
cdef int32_t* INTEGRITY_ERROR_CODES = [
|
||||
1, # unique constraint violated
|
||||
|
|
|
@ -35,6 +35,7 @@ from . import __name__ as MODULE_NAME
|
|||
from . import errors, exceptions
|
||||
from . import connection as connection_module
|
||||
from .defaults import defaults
|
||||
from .fetch_info import FetchInfo
|
||||
from .var import Var
|
||||
from .base_impl import DbType, DB_TYPE_OBJECT
|
||||
from .dbobject import DbObjectType
|
||||
|
@ -52,6 +53,7 @@ class Cursor:
|
|||
self._impl.scrollable = scrollable
|
||||
self._impl.arraysize = defaults.arraysize
|
||||
self._impl.prefetchrows = defaults.prefetchrows
|
||||
self._fetch_infos = None
|
||||
|
||||
def __del__(self):
|
||||
if self._impl is not None:
|
||||
|
@ -137,6 +139,7 @@ class Cursor:
|
|||
self._impl.prepare(statement, tag, cache_statement)
|
||||
self.statement = statement
|
||||
self._impl.rowfactory = None
|
||||
self._fetch_infos = None
|
||||
|
||||
def _set_oci_attr(self, attr_num: int, attr_type: int,
|
||||
value: Any) -> None:
|
||||
|
@ -304,8 +307,10 @@ class Cursor:
|
|||
cursor has not had an operation invoked via the execute() method yet.
|
||||
"""
|
||||
self._verify_open()
|
||||
if self._impl.is_query(self):
|
||||
return self._impl.get_description()
|
||||
if self._fetch_infos is None and self._impl.is_query(self):
|
||||
self._fetch_infos = [FetchInfo._from_impl(i) \
|
||||
for i in self._impl.fetch_info_impls]
|
||||
return self._fetch_infos
|
||||
|
||||
def execute(self, statement: Union[str, None],
|
||||
parameters: Union[list, tuple, dict]=None,
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
# 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
||||
# either license.
|
||||
#
|
||||
# If you elect to accept the software under the Apache License, Version 2.0,
|
||||
# the following applies:
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# fetch_info.py
|
||||
#
|
||||
# Contains the FetchInfo class which stores metadata about columns that are
|
||||
# being fetched.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
from typing import Union
|
||||
|
||||
from . import __name__ as MODULE_NAME
|
||||
from .dbobject import DbObjectType
|
||||
from .base_impl import (
|
||||
DbType,
|
||||
DB_TYPE_DATE,
|
||||
DB_TYPE_TIMESTAMP,
|
||||
DB_TYPE_TIMESTAMP_LTZ,
|
||||
DB_TYPE_TIMESTAMP_TZ,
|
||||
DB_TYPE_BINARY_FLOAT,
|
||||
DB_TYPE_BINARY_DOUBLE,
|
||||
DB_TYPE_BINARY_INTEGER,
|
||||
DB_TYPE_NUMBER
|
||||
)
|
||||
|
||||
class FetchInfo:
|
||||
"""
|
||||
Identifies metadata of columns that are being fetched.
|
||||
"""
|
||||
__module__ = MODULE_NAME
|
||||
|
||||
def __eq__(self, other):
|
||||
return tuple(self) == other
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""
|
||||
Return the parts mandated by the Python Database API.
|
||||
"""
|
||||
if index == 0 or index == -7:
|
||||
return self.name
|
||||
elif index == 1 or index == -6:
|
||||
return self.type_code
|
||||
elif index == 2 or index == -5:
|
||||
return self.display_size
|
||||
elif index == 3 or index == -4:
|
||||
return self.internal_size
|
||||
elif index == 4 or index == -3:
|
||||
return self.precision
|
||||
elif index == 5 or index == -2:
|
||||
return self.scale
|
||||
elif index == 6 or index == -1:
|
||||
return self.null_ok
|
||||
raise IndexError("list index out of range")
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
Length mandated by the Python Database API.
|
||||
"""
|
||||
return 7
|
||||
|
||||
def __repr__(self):
|
||||
return repr(tuple(self))
|
||||
|
||||
def __str__(self):
|
||||
return str(tuple(self))
|
||||
|
||||
@classmethod
|
||||
def _from_impl(cls, impl):
|
||||
info = cls.__new__(cls)
|
||||
info._impl = impl
|
||||
info._type = None
|
||||
return info
|
||||
|
||||
@property
|
||||
def display_size(self) -> Union[int, None]:
|
||||
"""
|
||||
Returns the display size of the column.
|
||||
"""
|
||||
if self._impl.size > 0:
|
||||
return self._impl.size
|
||||
dbtype = self._impl.dbtype
|
||||
if dbtype is DB_TYPE_DATE \
|
||||
or dbtype is DB_TYPE_TIMESTAMP \
|
||||
or dbtype is DB_TYPE_TIMESTAMP_LTZ \
|
||||
or dbtype is DB_TYPE_TIMESTAMP_TZ:
|
||||
return 23
|
||||
elif dbtype is DB_TYPE_BINARY_FLOAT \
|
||||
or dbtype is DB_TYPE_BINARY_DOUBLE \
|
||||
or dbtype is DB_TYPE_BINARY_INTEGER \
|
||||
or dbtype is DB_TYPE_NUMBER:
|
||||
if self._impl.precision:
|
||||
display_size = self._impl.precision + 1
|
||||
if self._impl.scale > 0:
|
||||
display_size += self._impl.scale + 1
|
||||
else:
|
||||
display_size = 127
|
||||
return display_size
|
||||
|
||||
@property
|
||||
def internal_size(self) -> Union[int, None]:
|
||||
"""
|
||||
Returns the size in bytes of the column.
|
||||
"""
|
||||
if self._impl.size > 0:
|
||||
return self._impl.buffer_size
|
||||
|
||||
@property
|
||||
def is_json(self) -> bool:
|
||||
"""
|
||||
Returns whether the column contains JSON.
|
||||
"""
|
||||
return self._impl.is_json
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""
|
||||
Returns the name of the column.
|
||||
"""
|
||||
return self._impl.name
|
||||
|
||||
@property
|
||||
def null_ok(self) -> bool:
|
||||
"""
|
||||
Returns whether nulls or permitted or not in the column.
|
||||
"""
|
||||
return self._impl.nulls_allowed
|
||||
|
||||
@property
|
||||
def precision(self) -> Union[int, None]:
|
||||
"""
|
||||
Returns the precision of the column.
|
||||
"""
|
||||
if self._impl.precision or self._impl.scale:
|
||||
return self._impl.precision
|
||||
|
||||
@property
|
||||
def scale(self) -> Union[int, None]:
|
||||
"""
|
||||
Returns the scale of the column.
|
||||
"""
|
||||
if self._impl.precision or self._impl.scale:
|
||||
return self._impl.scale
|
||||
|
||||
@property
|
||||
def type(self) -> Union[DbType, DbObjectType]:
|
||||
"""
|
||||
Returns the type of the column, as either a database object type or a
|
||||
database type.
|
||||
"""
|
||||
if self._type is None:
|
||||
if self._impl.objtype is not None:
|
||||
self._type = DbObjectType._from_impl(self._impl.objtype)
|
||||
else:
|
||||
self._type = self._impl.dbtype
|
||||
return self._type
|
||||
|
||||
@property
|
||||
def type_code(self) -> DbType:
|
||||
"""
|
||||
Returns the type of the column.
|
||||
"""
|
||||
return self._impl.dbtype
|
|
@ -29,6 +29,9 @@
|
|||
# base_impl.pyx).
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
cdef class FetchInfoImpl:
|
||||
pass
|
||||
|
||||
cdef class BaseCursorImpl:
|
||||
|
||||
@cython.boundscheck(False)
|
||||
|
@ -131,26 +134,35 @@ cdef class BaseCursorImpl:
|
|||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cdef int _create_fetch_var(self, object conn, object cursor,
|
||||
object type_handler, ssize_t pos,
|
||||
FetchInfo fetch_info) except -1:
|
||||
object type_handler, bint uses_fetch_info,
|
||||
ssize_t pos,
|
||||
FetchInfoImpl fetch_info) except -1:
|
||||
"""
|
||||
Create the fetch variable for the given position and fetch information.
|
||||
The output type handler is consulted, if present, to make any necessary
|
||||
adjustments.
|
||||
"""
|
||||
cdef:
|
||||
object var, pub_fetch_info
|
||||
BaseVarImpl var_impl
|
||||
uint32_t db_type_num
|
||||
object var
|
||||
|
||||
# add the fetch info to the list used for handling the cursor
|
||||
# description attribute
|
||||
self.fetch_info_impls[pos] = fetch_info
|
||||
|
||||
# if an output type handler is specified, call it; the output type
|
||||
# handler should return a variable or None; the value None implies that
|
||||
# the default processing should take place just as if no output type
|
||||
# handler was defined
|
||||
if type_handler is not None:
|
||||
var = type_handler(cursor, fetch_info._name, fetch_info._dbtype,
|
||||
fetch_info._size, fetch_info._precision,
|
||||
fetch_info._scale)
|
||||
if uses_fetch_info:
|
||||
pub_fetch_info = PY_TYPE_FETCHINFO._from_impl(fetch_info)
|
||||
var = type_handler(cursor, pub_fetch_info)
|
||||
else:
|
||||
var = type_handler(cursor, fetch_info.name, fetch_info.dbtype,
|
||||
fetch_info.size, fetch_info.precision,
|
||||
fetch_info.scale)
|
||||
if var is not None:
|
||||
self._verify_var(var)
|
||||
var_impl = var._impl
|
||||
|
@ -165,13 +177,13 @@ cdef class BaseCursorImpl:
|
|||
# otherwise, create a new variable using the provided fetch information
|
||||
var_impl = self._create_var_impl(conn)
|
||||
var_impl.num_elements = self._fetch_array_size
|
||||
var_impl.dbtype = fetch_info._dbtype
|
||||
var_impl.objtype = fetch_info._objtype
|
||||
var_impl.name = fetch_info._name
|
||||
var_impl.size = fetch_info._size
|
||||
var_impl.precision = fetch_info._precision
|
||||
var_impl.scale = fetch_info._scale
|
||||
var_impl.nulls_allowed = fetch_info._nulls_allowed
|
||||
var_impl.dbtype = fetch_info.dbtype
|
||||
var_impl.objtype = fetch_info.objtype
|
||||
var_impl.name = fetch_info.name
|
||||
var_impl.size = fetch_info.size
|
||||
var_impl.precision = fetch_info.precision
|
||||
var_impl.scale = fetch_info.scale
|
||||
var_impl.nulls_allowed = fetch_info.nulls_allowed
|
||||
var_impl._fetch_info = fetch_info
|
||||
|
||||
# adjust the variable based on the defaults specified by the user, if
|
||||
|
@ -183,7 +195,7 @@ cdef class BaseCursorImpl:
|
|||
elif var_impl.scale == 0 \
|
||||
or (var_impl.scale == -127 and var_impl.precision == 0):
|
||||
var_impl._preferred_num_type = NUM_TYPE_INT
|
||||
elif future.old_json_col_as_obj and fetch_info._is_json_col \
|
||||
elif future.old_json_col_as_obj and fetch_info.is_json \
|
||||
and db_type_num != DB_TYPE_NUM_JSON:
|
||||
def converter(value):
|
||||
if isinstance(value, PY_TYPE_LOB):
|
||||
|
@ -264,23 +276,34 @@ cdef class BaseCursorImpl:
|
|||
def _get_oci_attr(self, uint32_t attr_num, uint32_t attr_type):
|
||||
pass
|
||||
|
||||
cdef object _get_output_type_handler(self):
|
||||
cdef object _get_output_type_handler(self, bint *uses_fetch_info):
|
||||
"""
|
||||
Return the output type handler to use for the cursor. If one is not
|
||||
directly defined on the cursor then the one defined on the connection
|
||||
is used instead.
|
||||
"""
|
||||
cdef BaseConnImpl conn_impl
|
||||
cdef:
|
||||
BaseConnImpl conn_impl
|
||||
object type_handler
|
||||
if self.outputtypehandler is not None:
|
||||
return self.outputtypehandler
|
||||
conn_impl = self._get_conn_impl()
|
||||
return conn_impl.outputtypehandler
|
||||
type_handler = self.outputtypehandler
|
||||
else:
|
||||
conn_impl = self._get_conn_impl()
|
||||
type_handler = conn_impl.outputtypehandler
|
||||
if type_handler is not None:
|
||||
uses_fetch_info[0] = \
|
||||
(inspect.isfunction(type_handler) and \
|
||||
type_handler.__code__.co_argcount == 2) or \
|
||||
(inspect.ismethod(type_handler) and \
|
||||
type_handler.__code__.co_argcount == 3)
|
||||
return type_handler
|
||||
|
||||
cdef int _init_fetch_vars(self, uint32_t num_columns) except -1:
|
||||
"""
|
||||
Initializes the fetch variable lists in preparation for creating the
|
||||
fetch variables used in fetching rows from the database.
|
||||
"""
|
||||
self.fetch_info_impls = [None] * num_columns
|
||||
self.fetch_vars = [None] * num_columns
|
||||
self.fetch_var_impls = [None] * num_columns
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2022, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -200,9 +200,10 @@ def init_base_impl(package):
|
|||
is to avoid circular imports and eliminate the need for global lookups.
|
||||
"""
|
||||
global PY_TYPE_CURSOR, PY_TYPE_DB_OBJECT, PY_TYPE_DB_OBJECT_TYPE
|
||||
global PY_TYPE_LOB, PY_TYPE_VAR
|
||||
global PY_TYPE_LOB, PY_TYPE_VAR, PY_TYPE_FETCHINFO
|
||||
PY_TYPE_CURSOR = package.Cursor
|
||||
PY_TYPE_DB_OBJECT = package.DbObject
|
||||
PY_TYPE_DB_OBJECT_TYPE = package.DbObjectType
|
||||
PY_TYPE_FETCHINFO = package.FetchInfo
|
||||
PY_TYPE_LOB = package.LOB
|
||||
PY_TYPE_VAR = package.Var
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -259,43 +259,6 @@ cdef class BaseVarImpl:
|
|||
if size > self.size:
|
||||
self.size = size
|
||||
|
||||
def get_description(self):
|
||||
"""
|
||||
Return a 7-tuple containing information about the variable: (name,
|
||||
type, display_size, internal_size, precision, scale, null_ok).
|
||||
"""
|
||||
cdef:
|
||||
uint32_t display_size = 0, size_in_bytes = 0
|
||||
FetchInfo fetch_info = self._fetch_info
|
||||
DbType dbtype = fetch_info._dbtype
|
||||
object precision, scale
|
||||
if fetch_info._size > 0:
|
||||
display_size = fetch_info._size
|
||||
size_in_bytes = fetch_info._buffer_size
|
||||
elif dbtype is DB_TYPE_DATE \
|
||||
or dbtype is DB_TYPE_TIMESTAMP \
|
||||
or dbtype is DB_TYPE_TIMESTAMP_LTZ \
|
||||
or dbtype is DB_TYPE_TIMESTAMP_TZ:
|
||||
display_size = 23
|
||||
elif dbtype is DB_TYPE_BINARY_FLOAT \
|
||||
or dbtype is DB_TYPE_BINARY_DOUBLE \
|
||||
or dbtype is DB_TYPE_BINARY_INTEGER \
|
||||
or dbtype is DB_TYPE_NUMBER:
|
||||
if fetch_info._precision:
|
||||
display_size = fetch_info._precision + 1
|
||||
if fetch_info._scale > 0:
|
||||
display_size += fetch_info._scale + 1
|
||||
else:
|
||||
display_size = 127
|
||||
if fetch_info._precision or fetch_info._scale:
|
||||
precision = fetch_info._precision
|
||||
scale = fetch_info._scale
|
||||
else:
|
||||
scale = precision = None
|
||||
return (fetch_info._name, dbtype, display_size or None,
|
||||
size_in_bytes or None, precision, scale,
|
||||
fetch_info._nulls_allowed)
|
||||
|
||||
def get_all_values(self):
|
||||
"""
|
||||
Internal method for returning an array of all of the values stored in
|
||||
|
|
|
@ -69,7 +69,7 @@ cdef class ThickCursorImpl(BaseCursorImpl):
|
|||
return var_impl
|
||||
|
||||
cdef int _define_var(self, object conn, object cursor, object type_handler,
|
||||
ssize_t pos) except -1:
|
||||
bint uses_fetch_info, ssize_t pos) except -1:
|
||||
"""
|
||||
Internal method that creates the variable using the query info (unless
|
||||
an output type handler has been specified) and then performs the define
|
||||
|
@ -78,37 +78,38 @@ cdef class ThickCursorImpl(BaseCursorImpl):
|
|||
cdef:
|
||||
ThickDbObjectTypeImpl typ_impl
|
||||
dpiDataTypeInfo *type_info
|
||||
FetchInfoImpl fetch_info
|
||||
ThickConnImpl conn_impl
|
||||
dpiQueryInfo query_info
|
||||
ThickVarImpl var_impl
|
||||
FetchInfo fetch_info
|
||||
|
||||
# build FetchInfo based on query info provided by ODPI-C
|
||||
# build FetchInfoImpl based on query info provided by ODPI-C
|
||||
if dpiStmt_getQueryInfo(self._handle, pos + 1, &query_info) < 0:
|
||||
_raise_from_odpi()
|
||||
type_info = &query_info.typeInfo
|
||||
fetch_info = FetchInfo()
|
||||
fetch_info._dbtype = DbType._from_num(type_info.oracleTypeNum)
|
||||
if fetch_info._dbtype.num == DPI_ORACLE_TYPE_INTERVAL_YM:
|
||||
fetch_info = FetchInfoImpl()
|
||||
fetch_info.dbtype = DbType._from_num(type_info.oracleTypeNum)
|
||||
if fetch_info.dbtype.num == DPI_ORACLE_TYPE_INTERVAL_YM:
|
||||
errors._raise_err(errors.ERR_DB_TYPE_NOT_SUPPORTED,
|
||||
name=fetch_info._dbtype.name)
|
||||
name=fetch_info.dbtype.name)
|
||||
if type_info.sizeInChars > 0:
|
||||
fetch_info._size = type_info.sizeInChars
|
||||
fetch_info.size = type_info.sizeInChars
|
||||
else:
|
||||
fetch_info._size = type_info.clientSizeInBytes
|
||||
fetch_info._buffer_size = type_info.clientSizeInBytes
|
||||
fetch_info._name = query_info.name[:query_info.nameLength].decode()
|
||||
fetch_info._scale = type_info.scale + type_info.fsPrecision
|
||||
fetch_info._precision = type_info.precision
|
||||
fetch_info._nulls_allowed = query_info.nullOk
|
||||
fetch_info._is_json_col = type_info.isJson
|
||||
fetch_info.size = type_info.clientSizeInBytes
|
||||
fetch_info.buffer_size = type_info.clientSizeInBytes
|
||||
fetch_info.name = query_info.name[:query_info.nameLength].decode()
|
||||
fetch_info.scale = type_info.scale + type_info.fsPrecision
|
||||
fetch_info.precision = type_info.precision
|
||||
fetch_info.nulls_allowed = query_info.nullOk
|
||||
fetch_info.is_json = type_info.isJson
|
||||
if type_info.objectType != NULL:
|
||||
typ_impl = ThickDbObjectTypeImpl._from_handle(self._conn_impl,
|
||||
type_info.objectType)
|
||||
fetch_info._objtype = typ_impl
|
||||
fetch_info.objtype = typ_impl
|
||||
|
||||
# create variable and call define in ODPI-C
|
||||
self._create_fetch_var(conn, cursor, type_handler, pos, fetch_info)
|
||||
self._create_fetch_var(conn, cursor, type_handler, uses_fetch_info,
|
||||
pos, fetch_info)
|
||||
var_impl = self.fetch_var_impls[pos]
|
||||
var_impl._create_handle()
|
||||
if dpiStmt_define(self._handle, pos + 1, var_impl._handle) < 0:
|
||||
|
@ -167,6 +168,7 @@ cdef class ThickCursorImpl(BaseCursorImpl):
|
|||
ThickCursorImpl cursor_impl = <ThickCursorImpl> cursor._impl
|
||||
object var, type_handler, conn
|
||||
ThickVarImpl var_impl
|
||||
bint uses_fetch_info
|
||||
ssize_t i
|
||||
|
||||
# initialize fetching variables; these are used to reduce the number of
|
||||
|
@ -184,10 +186,10 @@ cdef class ThickCursorImpl(BaseCursorImpl):
|
|||
# populate list to contain fetch variables that are created
|
||||
self._fetch_array_size = self.arraysize
|
||||
self._init_fetch_vars(num_query_cols)
|
||||
type_handler = self._get_output_type_handler()
|
||||
type_handler = self._get_output_type_handler(&uses_fetch_info)
|
||||
conn = cursor.connection
|
||||
for i in range(num_query_cols):
|
||||
self._define_var(conn, cursor, type_handler, i)
|
||||
self._define_var(conn, cursor, type_handler, uses_fetch_info, i)
|
||||
|
||||
def _set_oci_attr(self, uint32_t attr_num, uint32_t attr_type,
|
||||
object value):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2021, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2021, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -119,12 +119,12 @@ cdef object _tstamp_to_date(object fetch_value):
|
|||
return fetch_value.replace(microsecond=0)
|
||||
|
||||
cdef int conversion_helper(ThinVarImpl output_var,
|
||||
FetchInfo fetch_info) except -1:
|
||||
FetchInfoImpl fetch_info) except -1:
|
||||
cdef:
|
||||
uint8_t fetch_ora_type_num, output_ora_type_num, csfrm
|
||||
object key, value
|
||||
|
||||
fetch_ora_type_num = fetch_info._dbtype._ora_type_num
|
||||
fetch_ora_type_num = fetch_info.dbtype._ora_type_num
|
||||
output_ora_type_num = output_var.dbtype._ora_type_num
|
||||
|
||||
key = (fetch_ora_type_num, output_ora_type_num)
|
||||
|
@ -135,11 +135,11 @@ cdef int conversion_helper(ThinVarImpl output_var,
|
|||
output_var._preferred_num_type = value
|
||||
else:
|
||||
csfrm = output_var.dbtype._csfrm
|
||||
fetch_info._dbtype = DbType._from_ora_type_and_csfrm(value,
|
||||
csfrm)
|
||||
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(value,
|
||||
csfrm)
|
||||
else:
|
||||
output_var._conv_func = value
|
||||
except:
|
||||
errors._raise_err(errors.ERR_INCONSISTENT_DATATYPES,
|
||||
input_type=fetch_info._dbtype.name,
|
||||
input_type=fetch_info.dbtype.name,
|
||||
output_type=output_var.dbtype.name)
|
||||
|
|
|
@ -70,8 +70,9 @@ cdef class ThinCursorImpl(BaseCursorImpl):
|
|||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cdef int _create_fetch_var(self, object conn, object cursor,
|
||||
object type_handler, ssize_t pos,
|
||||
FetchInfo fetch_info) except -1:
|
||||
object type_handler, bint uses_fetch_info,
|
||||
ssize_t pos,
|
||||
FetchInfoImpl fetch_info) except -1:
|
||||
"""
|
||||
Internal method that creates a fetch variable. A check is made after
|
||||
the variable is created to determine if a conversion is required and
|
||||
|
@ -80,10 +81,10 @@ cdef class ThinCursorImpl(BaseCursorImpl):
|
|||
cdef:
|
||||
ThinDbObjectTypeImpl typ_impl
|
||||
ThinVarImpl var_impl
|
||||
BaseCursorImpl._create_fetch_var(self, conn, cursor, type_handler, pos,
|
||||
fetch_info)
|
||||
BaseCursorImpl._create_fetch_var(self, conn, cursor, type_handler,
|
||||
uses_fetch_info, pos, fetch_info)
|
||||
var_impl = self.fetch_var_impls[pos]
|
||||
if var_impl.dbtype._ora_type_num != fetch_info._dbtype._ora_type_num:
|
||||
if var_impl.dbtype._ora_type_num != fetch_info.dbtype._ora_type_num:
|
||||
conversion_helper(var_impl, fetch_info)
|
||||
elif var_impl.objtype is not None:
|
||||
typ_impl = var_impl.objtype
|
||||
|
@ -202,6 +203,7 @@ cdef class ThinCursorImpl(BaseCursorImpl):
|
|||
self._statement = None
|
||||
self._statement = self._conn_impl._get_statement(sql.strip(),
|
||||
cache_statement)
|
||||
self.fetch_info_impls = self._statement._fetch_info_impls
|
||||
self.fetch_vars = self._statement._fetch_vars
|
||||
self.fetch_var_impls = self._statement._fetch_var_impls
|
||||
self._num_columns = self._statement._num_columns
|
||||
|
|
|
@ -318,7 +318,7 @@ cdef class MessageWithData(Message):
|
|||
|
||||
cdef int _adjust_fetch_info(self,
|
||||
ThinVarImpl prev_var_impl,
|
||||
FetchInfo fetch_info) except -1:
|
||||
FetchInfoImpl fetch_info) except -1:
|
||||
"""
|
||||
When a query is re-executed but the data type of a column has changed
|
||||
the server returns the type information of the new type. However, if
|
||||
|
@ -329,22 +329,22 @@ cdef class MessageWithData(Message):
|
|||
Detect this situation and adjust the fetch type appropriately.
|
||||
"""
|
||||
cdef:
|
||||
FetchInfo prev_fetch_info = prev_var_impl._fetch_info
|
||||
FetchInfoImpl prev_fetch_info = prev_var_impl._fetch_info
|
||||
uint8_t csfrm = prev_var_impl.dbtype._csfrm
|
||||
uint8_t type_num
|
||||
if fetch_info._dbtype._ora_type_num == TNS_DATA_TYPE_CLOB \
|
||||
and prev_fetch_info._dbtype._ora_type_num in \
|
||||
if fetch_info.dbtype._ora_type_num == TNS_DATA_TYPE_CLOB \
|
||||
and prev_fetch_info.dbtype._ora_type_num in \
|
||||
(TNS_DATA_TYPE_CHAR, TNS_DATA_TYPE_VARCHAR,
|
||||
TNS_DATA_TYPE_LONG):
|
||||
type_num = TNS_DATA_TYPE_LONG
|
||||
fetch_info._dbtype = DbType._from_ora_type_and_csfrm(type_num,
|
||||
csfrm)
|
||||
elif fetch_info._dbtype._ora_type_num == TNS_DATA_TYPE_BLOB \
|
||||
and prev_fetch_info._dbtype._ora_type_num in \
|
||||
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(type_num,
|
||||
csfrm)
|
||||
elif fetch_info.dbtype._ora_type_num == TNS_DATA_TYPE_BLOB \
|
||||
and prev_fetch_info.dbtype._ora_type_num in \
|
||||
(TNS_DATA_TYPE_RAW, TNS_DATA_TYPE_LONG_RAW):
|
||||
type_num = TNS_DATA_TYPE_LONG_RAW
|
||||
fetch_info._dbtype = DbType._from_ora_type_and_csfrm(type_num,
|
||||
csfrm)
|
||||
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(type_num,
|
||||
csfrm)
|
||||
|
||||
cdef object _create_cursor_from_describe(self, ReadBuffer buf,
|
||||
object cursor=None):
|
||||
|
@ -432,6 +432,7 @@ cdef class MessageWithData(Message):
|
|||
Statement statement = cursor_impl._statement
|
||||
object type_handler, conn
|
||||
ThinVarImpl var_impl
|
||||
bint uses_fetch_info
|
||||
ssize_t i, num_vals
|
||||
|
||||
# set values to indicate the start of a new fetch operation
|
||||
|
@ -449,11 +450,12 @@ cdef class MessageWithData(Message):
|
|||
# the one that was used during the last fetch, rebuild the fetch
|
||||
# variables in order to take the new type handler into account
|
||||
conn = self.cursor.connection
|
||||
type_handler = cursor_impl._get_output_type_handler()
|
||||
type_handler = cursor_impl._get_output_type_handler(&uses_fetch_info)
|
||||
if type_handler is not statement._last_output_type_handler:
|
||||
for i, var_impl in enumerate(cursor_impl.fetch_var_impls):
|
||||
cursor_impl._create_fetch_var(conn, self.cursor, type_handler,
|
||||
i, var_impl._fetch_info)
|
||||
uses_fetch_info, i,
|
||||
var_impl._fetch_info)
|
||||
statement._last_output_type_handler = type_handler
|
||||
|
||||
# the list of output variables is equivalent to the fetch variables
|
||||
|
@ -518,15 +520,15 @@ cdef class MessageWithData(Message):
|
|||
ThinCursorImpl cursor_impl
|
||||
object column_value = None
|
||||
ThinDbObjectImpl obj_impl
|
||||
FetchInfoImpl fetch_info
|
||||
int32_t actual_num_bytes
|
||||
uint32_t buffer_size
|
||||
FetchInfo fetch_info
|
||||
Rowid rowid
|
||||
fetch_info = var_impl._fetch_info
|
||||
if fetch_info is not None:
|
||||
ora_type_num = fetch_info._dbtype._ora_type_num
|
||||
csfrm = fetch_info._dbtype._csfrm
|
||||
buffer_size = fetch_info._buffer_size
|
||||
ora_type_num = fetch_info.dbtype._ora_type_num
|
||||
csfrm = fetch_info.dbtype._csfrm
|
||||
buffer_size = fetch_info.buffer_size
|
||||
else:
|
||||
ora_type_num = var_impl.dbtype._ora_type_num
|
||||
csfrm = var_impl.dbtype._csfrm
|
||||
|
@ -628,33 +630,33 @@ cdef class MessageWithData(Message):
|
|||
column_value = var_impl._conv_func(column_value)
|
||||
return column_value
|
||||
|
||||
cdef FetchInfo _process_column_info(self, ReadBuffer buf,
|
||||
ThinCursorImpl cursor_impl):
|
||||
cdef FetchInfoImpl _process_column_info(self, ReadBuffer buf,
|
||||
ThinCursorImpl cursor_impl):
|
||||
cdef:
|
||||
ThinDbObjectTypeImpl typ_impl
|
||||
uint32_t num_bytes, uds_flags
|
||||
uint8_t data_type, csfrm
|
||||
FetchInfoImpl fetch_info
|
||||
int8_t precision, scale
|
||||
uint8_t nulls_allowed
|
||||
FetchInfo fetch_info
|
||||
str schema, name
|
||||
int cache_num
|
||||
bytes oid
|
||||
buf.read_ub1(&data_type)
|
||||
fetch_info = FetchInfo()
|
||||
fetch_info = FetchInfoImpl()
|
||||
buf.skip_ub1() # flags
|
||||
buf.read_sb1(&precision)
|
||||
fetch_info._precision = precision
|
||||
fetch_info.precision = precision
|
||||
if data_type == TNS_DATA_TYPE_NUMBER \
|
||||
or data_type == TNS_DATA_TYPE_INTERVAL_DS \
|
||||
or data_type == TNS_DATA_TYPE_TIMESTAMP \
|
||||
or data_type == TNS_DATA_TYPE_TIMESTAMP_LTZ \
|
||||
or data_type == TNS_DATA_TYPE_TIMESTAMP_TZ:
|
||||
buf.read_sb2(&fetch_info._scale)
|
||||
buf.read_sb2(&fetch_info.scale)
|
||||
else:
|
||||
buf.read_sb1(&scale)
|
||||
fetch_info._scale = scale
|
||||
buf.read_ub4(&fetch_info._buffer_size)
|
||||
fetch_info.scale = scale
|
||||
buf.read_ub4(&fetch_info.buffer_size)
|
||||
buf.skip_ub4() # max number of array elements
|
||||
buf.skip_ub4() # cont flags
|
||||
buf.read_ub4(&num_bytes) # OID
|
||||
|
@ -663,18 +665,18 @@ cdef class MessageWithData(Message):
|
|||
buf.skip_ub2() # version
|
||||
buf.skip_ub2() # character set id
|
||||
buf.read_ub1(&csfrm) # character set form
|
||||
fetch_info._dbtype = DbType._from_ora_type_and_csfrm(data_type, csfrm)
|
||||
buf.read_ub4(&fetch_info._size)
|
||||
fetch_info.dbtype = DbType._from_ora_type_and_csfrm(data_type, csfrm)
|
||||
buf.read_ub4(&fetch_info.size)
|
||||
if data_type == TNS_DATA_TYPE_RAW:
|
||||
fetch_info._size = fetch_info._buffer_size
|
||||
fetch_info.size = fetch_info.buffer_size
|
||||
if buf._caps.ttc_field_version >= TNS_CCAP_FIELD_VERSION_12_2:
|
||||
buf.skip_ub4() # oaccolid
|
||||
buf.read_ub1(&nulls_allowed)
|
||||
fetch_info._nulls_allowed = nulls_allowed
|
||||
fetch_info.nulls_allowed = nulls_allowed
|
||||
buf.skip_ub1() # v7 length of name
|
||||
buf.read_ub4(&num_bytes)
|
||||
if num_bytes > 0:
|
||||
fetch_info._name = buf.read_str(TNS_CS_IMPLICIT)
|
||||
fetch_info.name = buf.read_str(TNS_CS_IMPLICIT)
|
||||
buf.read_ub4(&num_bytes)
|
||||
if num_bytes > 0:
|
||||
schema = buf.read_str(TNS_CS_IMPLICIT)
|
||||
|
@ -683,14 +685,14 @@ cdef class MessageWithData(Message):
|
|||
name = buf.read_str(TNS_CS_IMPLICIT)
|
||||
buf.skip_ub2() # column position
|
||||
buf.read_ub4(&uds_flags)
|
||||
fetch_info._is_json_col = uds_flags & TNS_UDS_FLAGS_IS_JSON
|
||||
fetch_info.is_json = uds_flags & TNS_UDS_FLAGS_IS_JSON
|
||||
if data_type == TNS_DATA_TYPE_INT_NAMED:
|
||||
if self.type_cache is None:
|
||||
cache_num = self.conn_impl._dbobject_type_cache_num
|
||||
self.type_cache = get_dbobject_type_cache(cache_num)
|
||||
typ_impl = self.type_cache.get_type_for_info(oid, schema, None,
|
||||
name)
|
||||
fetch_info._objtype = typ_impl
|
||||
fetch_info.objtype = typ_impl
|
||||
return fetch_info
|
||||
|
||||
cdef int _process_describe_info(self, ReadBuffer buf,
|
||||
|
@ -700,8 +702,9 @@ cdef class MessageWithData(Message):
|
|||
Statement stmt = cursor_impl._statement
|
||||
list prev_fetch_var_impls
|
||||
object type_handler, conn
|
||||
FetchInfoImpl fetch_info
|
||||
uint32_t num_bytes, i
|
||||
FetchInfo fetch_info
|
||||
bint uses_fetch_info
|
||||
str message
|
||||
buf.skip_ub4() # max row size
|
||||
buf.read_ub4(&cursor_impl._num_columns)
|
||||
|
@ -709,7 +712,7 @@ cdef class MessageWithData(Message):
|
|||
cursor_impl._init_fetch_vars(cursor_impl._num_columns)
|
||||
if cursor_impl._num_columns > 0:
|
||||
buf.skip_ub1()
|
||||
type_handler = cursor_impl._get_output_type_handler()
|
||||
type_handler = cursor_impl._get_output_type_handler(&uses_fetch_info)
|
||||
conn = self.cursor.connection
|
||||
for i in range(cursor_impl._num_columns):
|
||||
fetch_info = self._process_column_info(buf, cursor_impl)
|
||||
|
@ -717,13 +720,13 @@ cdef class MessageWithData(Message):
|
|||
and i < len(prev_fetch_var_impls):
|
||||
self._adjust_fetch_info(prev_fetch_var_impls[i], fetch_info)
|
||||
if not stmt._no_prefetch and \
|
||||
fetch_info._dbtype._ora_type_num in (TNS_DATA_TYPE_BLOB,
|
||||
TNS_DATA_TYPE_CLOB,
|
||||
TNS_DATA_TYPE_JSON):
|
||||
fetch_info.dbtype._ora_type_num in (TNS_DATA_TYPE_BLOB,
|
||||
TNS_DATA_TYPE_CLOB,
|
||||
TNS_DATA_TYPE_JSON):
|
||||
stmt._requires_define = True
|
||||
stmt._no_prefetch = True
|
||||
cursor_impl._create_fetch_var(conn, self.cursor, type_handler, i,
|
||||
fetch_info)
|
||||
cursor_impl._create_fetch_var(conn, self.cursor, type_handler,
|
||||
uses_fetch_info, i, fetch_info)
|
||||
buf.read_ub4(&num_bytes)
|
||||
if num_bytes > 0:
|
||||
buf.skip_raw_bytes_chunked() # current date
|
||||
|
@ -734,6 +737,7 @@ cdef class MessageWithData(Message):
|
|||
buf.read_ub4(&num_bytes)
|
||||
if num_bytes > 0:
|
||||
buf.skip_raw_bytes_chunked() # dcbqcky
|
||||
stmt._fetch_info_impls = cursor_impl.fetch_info_impls
|
||||
stmt._fetch_vars = cursor_impl.fetch_vars
|
||||
stmt._fetch_var_impls = cursor_impl.fetch_var_impls
|
||||
stmt._num_columns = cursor_impl._num_columns
|
||||
|
|
|
@ -88,6 +88,7 @@ cdef class Statement:
|
|||
bint _is_ddl
|
||||
bint _is_returning
|
||||
list _bind_info_list
|
||||
list _fetch_info_impls
|
||||
list _fetch_vars
|
||||
list _fetch_var_impls
|
||||
object _bind_info_dict
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -52,8 +52,8 @@ from .base_impl cimport BaseDbObjectImpl, BaseLobImpl, BasePoolImpl
|
|||
from .base_impl cimport BaseSodaDbImpl, BaseSodaCollImpl, BaseSodaDocImpl
|
||||
from .base_impl cimport BaseSodaDocCursorImpl, BaseQueueImpl
|
||||
from .base_impl cimport BaseDeqOptionsImpl, BaseEnqOptionsImpl
|
||||
from .base_impl cimport BaseMsgPropsImpl, BaseSubscrImpl, BindVar, FetchInfo
|
||||
from .base_impl cimport ConnectParamsImpl, PoolParamsImpl
|
||||
from .base_impl cimport BaseMsgPropsImpl, BaseSubscrImpl, BindVar
|
||||
from .base_impl cimport ConnectParamsImpl, PoolParamsImpl, FetchInfoImpl
|
||||
from .base_impl cimport NUM_TYPE_FLOAT, NUM_TYPE_INT, NUM_TYPE_DECIMAL
|
||||
from libc.string cimport memchr, memset
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -77,7 +77,7 @@ from .defaults import defaults
|
|||
from .base_impl cimport get_exception_class, NUM_TYPE_FLOAT
|
||||
from .base_impl cimport NUM_TYPE_INT, NUM_TYPE_DECIMAL, NUM_TYPE_STR
|
||||
from .base_impl cimport BaseConnImpl, BaseCursorImpl, BaseVarImpl, DbType
|
||||
from .base_impl cimport BaseLobImpl, BasePoolImpl, FetchInfo
|
||||
from .base_impl cimport BaseLobImpl, BasePoolImpl, FetchInfoImpl
|
||||
from .base_impl cimport Address, AddressList, Description, DescriptionList
|
||||
from .base_impl cimport ConnectParamsImpl, PoolParamsImpl, BaseDbObjectAttrImpl
|
||||
from .base_impl cimport BaseDbObjectImpl, BaseDbObjectTypeImpl
|
||||
|
|
|
@ -192,7 +192,7 @@ class TestCase(test_env.BaseTestCase):
|
|||
|
||||
def test_1309_output_type_handler_with_ref_cursor(self):
|
||||
"1309 - test using an output type handler with a REF cursor"
|
||||
def type_handler(cursor, name, default_type, size, precision, scale):
|
||||
def type_handler(cursor, metadata):
|
||||
return cursor.var(str, arraysize=cursor.arraysize)
|
||||
self.connection.outputtypehandler = type_handler
|
||||
var = self.cursor.var(oracledb.DB_TYPE_CURSOR)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -33,19 +33,16 @@ import test_env
|
|||
|
||||
class TestCase(test_env.BaseTestCase):
|
||||
|
||||
def output_type_handler_binary_int(self, cursor, name, default_type, size,
|
||||
precision, scale):
|
||||
def output_type_handler_binary_int(self, cursor, metadata):
|
||||
return cursor.var(oracledb.DB_TYPE_BINARY_INTEGER,
|
||||
arraysize=cursor.arraysize)
|
||||
|
||||
def output_type_handler_decimal(self, cursor, name, default_type, size,
|
||||
precision, scale):
|
||||
if default_type == oracledb.NUMBER:
|
||||
def output_type_handler_decimal(self, cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
|
||||
return cursor.var(str, 255, outconverter=decimal.Decimal,
|
||||
arraysize=cursor.arraysize)
|
||||
|
||||
def output_type_handler_str(self, cursor, name, default_type, size,
|
||||
precision, scale):
|
||||
def output_type_handler_str(self, cursor, metadata):
|
||||
return cursor.var(str, 255, arraysize=cursor.arraysize)
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -51,9 +51,8 @@ class TestCase(test_env.BaseTestCase):
|
|||
self.raw_data.append(data_tuple)
|
||||
self.data_by_key[i] = data_tuple
|
||||
|
||||
def __return_strings_as_bytes(self, cursor, name, default_type, size,
|
||||
precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_VARCHAR:
|
||||
def __return_strings_as_bytes(self, cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
|
||||
return cursor.var(str, arraysize=cursor.arraysize,
|
||||
bypass_decode=True)
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class TestCase(test_env.BaseTestCase):
|
|||
|
||||
def __test_type_handler(self, input_type, output_type, in_value,
|
||||
expected_out_value):
|
||||
def type_handler(cursor, name, default_type, size, precision, scale):
|
||||
def type_handler(cursor, metadata):
|
||||
return cursor.var(output_type, arraysize=cursor.arraysize)
|
||||
self.cursor.outputtypehandler = type_handler
|
||||
var = self.cursor.var(input_type)
|
||||
|
@ -49,8 +49,8 @@ class TestCase(test_env.BaseTestCase):
|
|||
|
||||
def __test_type_handler_lob(self, lob_type, output_type):
|
||||
db_type = getattr(oracledb, lob_type)
|
||||
def type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == db_type:
|
||||
def type_handler(cursor, metadata):
|
||||
if metadata.type_code is db_type:
|
||||
return cursor.var(output_type, arraysize=cursor.arraysize)
|
||||
self.cursor.outputtypehandler = type_handler
|
||||
in_value = f"Some {lob_type} data"
|
||||
|
@ -479,7 +479,7 @@ class TestCase(test_env.BaseTestCase):
|
|||
|
||||
def test_3671_incorrect_arraysize(self):
|
||||
"3671 - execute raises an error if an incorrect arraysize is used"
|
||||
def type_handler(cursor, name, default_type, size, precision, scale):
|
||||
def type_handler(cursor, metadata):
|
||||
return cursor.var(str)
|
||||
cursor = self.connection.cursor()
|
||||
cursor.arraysize = 100
|
||||
|
@ -489,8 +489,7 @@ class TestCase(test_env.BaseTestCase):
|
|||
|
||||
def test_3672_incorrect_outputtypehandler_return_type(self):
|
||||
"3672 - execute raises an error if a var is not returned"
|
||||
def type_handler(cursor, name, default_type, size,
|
||||
precision, scale):
|
||||
def type_handler(cursor, metadata):
|
||||
return "incorrect_return"
|
||||
cursor = self.connection.cursor()
|
||||
cursor.outputtypehandler = type_handler
|
||||
|
@ -506,8 +505,7 @@ class TestCase(test_env.BaseTestCase):
|
|||
|
||||
def test_3674_cursor_description_unchanged(self):
|
||||
"3674 - use of output type handler does not affect description"
|
||||
def type_handler(cursor, name, default_type, size,
|
||||
precision, scale):
|
||||
def type_handler(cursor, metadata):
|
||||
return cursor.var(str, arraysize=cursor.arraysize)
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.execute("select user from dual")
|
||||
|
@ -517,5 +515,14 @@ class TestCase(test_env.BaseTestCase):
|
|||
cursor.execute("select user from dual")
|
||||
self.assertEqual(cursor.description, desc_before)
|
||||
|
||||
def test_3675_old_signature(self):
|
||||
"3675 - use the old signature for an output type handler"
|
||||
def type_handler(cursor, name, default_type, size, precision, scale):
|
||||
return cursor.var(str, arraysize=cursor.arraysize)
|
||||
with self.connection.cursor() as cursor:
|
||||
cursor.outputtypehandler = type_handler
|
||||
cursor.execute("select 1 from dual")
|
||||
self.assertEqual(cursor.fetchall(), [('1',)])
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_env.run_test_cases()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#------------------------------------------------------------------------------
|
||||
# Copyright (c) 2021, 2022, Oracle and/or its affiliates.
|
||||
# Copyright (c) 2021, 2023, Oracle and/or its affiliates.
|
||||
#
|
||||
# This software is dual-licensed to you under the Universal Permissive License
|
||||
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
||||
|
@ -68,10 +68,9 @@ class TestCase(test_env.BaseTestCase):
|
|||
return cursor.var(oracledb.STRING, arraysize=num_elements,
|
||||
inconverter=self.building_in_converter)
|
||||
|
||||
def output_type_handler(self, cursor, name, default_type, size, precision,
|
||||
scale):
|
||||
if default_type == oracledb.STRING:
|
||||
return cursor.var(default_type, arraysize=cursor.arraysize,
|
||||
def output_type_handler(self, cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
|
||||
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
|
||||
outconverter=Building.from_json)
|
||||
|
||||
def test_3800(self):
|
||||
|
@ -173,9 +172,8 @@ class TestCase(test_env.BaseTestCase):
|
|||
self.connection.commit()
|
||||
def converter(value):
|
||||
return "CONVERTED"
|
||||
def output_type_handler(cursor, name, default_type, size, precision,
|
||||
scale):
|
||||
if default_type is oracledb.DB_TYPE_VARCHAR:
|
||||
def output_type_handler(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
|
||||
return cursor.var(str, outconverter=converter,
|
||||
arraysize=cursor.arraysize)
|
||||
self.cursor.outputtypehandler = output_type_handler
|
||||
|
@ -212,16 +210,15 @@ class TestCase(test_env.BaseTestCase):
|
|||
# take advantage of direct binding
|
||||
self.cursor.setinputsizes(None, oracledb.DB_TYPE_JSON)
|
||||
self.cursor.executemany(insert_sql, data_to_insert)
|
||||
def output_type_handler(cursor, name, default_type, size, precision,
|
||||
scale):
|
||||
def output_type_handler(cursor, metadata):
|
||||
# fetch 21c JSON datatype when using python-oracledb thin mode
|
||||
if default_type == oracledb.DB_TYPE_JSON:
|
||||
if metadata.type_code is oracledb.DB_TYPE_JSON:
|
||||
return cursor.var(str, arraysize=cursor.arraysize,
|
||||
outconverter=json.loads)
|
||||
# if using Oracle Client version < 21, then database returns BLOB
|
||||
# data type instead of JSON data type
|
||||
elif default_type == oracledb.DB_TYPE_BLOB:
|
||||
return cursor.var(default_type, arraysize=cursor.arraysize,
|
||||
elif metadata.type_code is oracledb.DB_TYPE_BLOB:
|
||||
return cursor.var(metadata.type, arraysize=cursor.arraysize,
|
||||
outconverter=lambda v: json.loads(v.read()))
|
||||
if json_as_string:
|
||||
self.cursor.outputtypehandler = output_type_handler
|
||||
|
|
|
@ -374,8 +374,8 @@ class TestCase(test_env.BaseTestCase):
|
|||
|
||||
def test_3929_output_type_handler_with_prefetch_gt_arraysize(self):
|
||||
"3929 - test an output type handler with prefetch > arraysize"
|
||||
def type_handler(cursor, name, default_type, size, precision, scale):
|
||||
return cursor.var(default_type, arraysize=cursor.arraysize)
|
||||
def type_handler(cursor, metadata):
|
||||
return cursor.var(metadata.type_code, arraysize=cursor.arraysize)
|
||||
self.cursor.arraysize = 2
|
||||
self.cursor.prefetchrows = 3
|
||||
self.cursor.outputtypehandler = type_handler
|
||||
|
|
|
@ -588,8 +588,8 @@ class TestCase(test_env.BaseTestCase):
|
|||
|
||||
def test_4348_reexecute_query_with_blob_as_bytes(self):
|
||||
"4348 - test re-executing a query with blob as bytes"
|
||||
def type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if default_type == oracledb.DB_TYPE_BLOB:
|
||||
def type_handler(cursor, metadata):
|
||||
if metadata.type_code is oracledb.DB_TYPE_BLOB:
|
||||
return cursor.var(bytes, arraysize=cursor.arraysize)
|
||||
self.connection.outputtypehandler = type_handler
|
||||
blob_data = b"An arbitrary set of blob data for test case 4348"
|
||||
|
@ -700,8 +700,8 @@ class TestCase(test_env.BaseTestCase):
|
|||
def out_converter(value):
|
||||
self.assertIs(type(value), str)
|
||||
return int(value)
|
||||
def type_handler(cursor, name, default_type, size, precision, scale):
|
||||
if name == "COL_3":
|
||||
def type_handler(cursor, metadata):
|
||||
if metadata.name == "COL_3":
|
||||
return cursor.var(str, arraysize=cursor.arraysize,
|
||||
outconverter=out_converter)
|
||||
self.cursor.outputtypehandler = type_handler
|
||||
|
|
Loading…
Reference in New Issue