Skip to content

Commit 6caccf9

Browse files
Support for enhanced metadata.
1 parent 6646529 commit 6caccf9

38 files changed

+560
-271
lines changed

doc/src/api_manual/connection.rst

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -756,14 +756,19 @@ Connection Attributes
756756

757757
This read-write attribute specifies a method called for each column that is
758758
going to be fetched from any cursor associated with this connection. The
759-
method signature is handler(cursor, name, defaultType, length, precision,
760-
scale) and the return value is expected to be a variable object or None in
761-
which case a default variable object will be created. If this attribute is
762-
None, the default behavior will take place for all columns fetched from
763-
cursors.
759+
method signature is ``handler(cursor, metadata)`` and the return value is
760+
expected to be a :ref:`variable object<varobj>` or None in which case a
761+
default variable object will be created. If this attribute is None, the
762+
default behavior will take place for all columns fetched from cursors.
764763

765764
See :ref:`outputtypehandlers`.
766765

766+
.. versionchanged:: 1.4
767+
768+
The method signature was changed. The previous signature
769+
``handler(cursor, name, default_type, length, precision, scale)`` will
770+
still work but is deprecated and will be removed in a future version.
771+
767772
.. note::
768773

769774
This attribute is an extension to the DB API definition.

doc/src/api_manual/cursor.rst

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -496,14 +496,10 @@ Cursor Attributes
496496

497497
.. data:: Cursor.description
498498

499-
This read-only attribute is a sequence of 7-item sequences. Each of these
500-
sequences contains information describing one result column: (name, type,
501-
display_size, internal_size, precision, scale, null_ok). This attribute
502-
will be None for operations that do not return rows or if the cursor has
503-
not had an operation invoked via the :meth:`~Cursor.execute()` method yet.
504-
505-
The type will be one of the :ref:`database type constants <dbtypes>`
506-
defined at the module level.
499+
This read-only attribute is a sequence of :ref:`FetchInfo<fetchinfoobj>`
500+
objects. This attribute will be None for operations that do not return rows
501+
or if the cursor has not had an operation invoked via the
502+
:meth:`~Cursor.execute()` method yet.
507503

508504
.. attribute:: Cursor.fetchvars
509505

@@ -541,13 +537,19 @@ Cursor Attributes
541537

542538
This read-write attribute specifies a method called for each column that is
543539
to be fetched from this cursor. The method signature is
544-
handler(cursor, name, defaultType, length, precision, scale) and the return
545-
value is expected to be a variable object or None in which case a default
546-
variable object will be created. If this attribute is None, then the default
540+
handler(cursor, metadata) and the return value is expected to be a
541+
:ref:`variable object<varobj>` or None in which case a default variable
542+
object will be created. If this attribute is None, then the default
547543
behavior will take place for all columns fetched from this cursor.
548544

549545
See :ref:`outputtypehandlers`.
550546

547+
.. versionchanged:: 1.4
548+
549+
The method signature was changed. The previous signature
550+
handler(cursor, name, default_type, length, precision, scale) will
551+
still work but is deprecated and will be removed in a future version.
552+
551553
.. note::
552554

553555
This attribute is an extension to the DB API definition.

doc/src/api_manual/deprecations.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ when they were first deprecated and a comment on what should be used instead,
99
if applicable. The most recent deprecations are listed first.
1010

1111

12+
.. list-table-with-summary:: Deprecated in python-oracledb 1.4
13+
:header-rows: 1
14+
:class: wy-table-responsive
15+
: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.
16+
:name: _deprecations_1_4
17+
18+
* - Name
19+
- Comments
20+
* - Output type handler with arguments
21+
``handler(cursor, name, default_type, length, precision, scale)``
22+
- Replace with ``handler(cursor, metadata)``. See
23+
:ref:`outputtypehandlers`.
24+
1225
.. list-table-with-summary:: Deprecated in python-oracledb 1.0
1326
:header-rows: 1
1427
:class: wy-table-responsive

doc/src/api_manual/fetch_info.rst

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
.. _fetchinfoobj:
2+
3+
**********************
4+
API: FetchInfo Objects
5+
**********************
6+
7+
These objects are created internally when a query is executed. They are found
8+
in the sequence :data:`Cursor.description`. For compatibility with the Python
9+
Database API, this object behaves as a 7-tuple containing the values for the
10+
attributes ``name``, ``type_code``, ``display_size``, ``internal_size``,
11+
``precision``, ``scale``, and ``null_ok`` in that order. For example, if
12+
``fetch_info`` is of type FetchInfo, then ``fetch_info[2]`` is the same as
13+
``fetch_info.display_size``.
14+
15+
.. note::
16+
17+
This object is an extension the DB API.
18+
19+
FetchInfo Attributes
20+
====================
21+
22+
.. attribute:: FetchInfo.display_size
23+
24+
This read-only attribute returns the display size of the column as mandated
25+
by the Python Database API.
26+
27+
.. attribute:: FetchInfo.internal_size
28+
29+
This read-only attribute returns the internal size of the column as
30+
mandated by the Python Database API.
31+
32+
.. attribute:: FetchInfo.is_json
33+
34+
This read-only attribute returns whether the column is known to contain
35+
JSON data. This will be ``true`` when the type code is
36+
``oracledb.DB_TYPE_JSON`` as well as when an "IS JSON" constraint is
37+
enabled on LOB and VARCHAR2 columns.
38+
39+
.. attribute:: FetchInfo.name
40+
41+
This read-only attribute returns the name of the column as mandated by the
42+
Python Database API.
43+
44+
.. attribute:: FetchInfo.null_ok
45+
46+
This read-only attribute returns whether nulls are allowed in the column as
47+
mandated by the Python Database API.
48+
49+
.. attribute:: FetchInfo.precision
50+
51+
This read-only attribute returns the precision of the column as mandated by
52+
the Python Database API.
53+
54+
.. attribute:: FetchInfo.scale
55+
56+
This read-only attribute returns the scale of the column as mandated by
57+
the Python Database API.
58+
59+
.. attribute:: FetchInfo.type
60+
61+
This read-only attribute returns the type of the column. This will be an
62+
:ref:`Oracle Object Type <dbobjecttype>` if the column contains Oracle
63+
objects; otherwise, it will be one of the :ref:`database type constants
64+
<dbtypes>` defined at the module level.
65+
66+
67+
.. attribute:: FetchInfo.type_code
68+
69+
This read-only attribute returns the type of the column as mandated by the
70+
Python Database API. The type will be one of the :ref:`database type
71+
constants <dbtypes>` defined at the module level.

doc/src/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ API Manual
6666
api_manual/connection_pool.rst
6767
api_manual/pool_params.rst
6868
api_manual/cursor.rst
69+
api_manual/fetch_info.rst
6970
api_manual/variable.rst
7071
api_manual/subscription.rst
7172
api_manual/lob.rst

doc/src/release_notes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ Thick Mode Changes
7070
Common Changes
7171
++++++++++++++
7272

73+
#) Replaced fixed 7-tuple for the cursor metadata found in
74+
:data:`Cursor.description` with a class which provides additional
75+
information such as the database object type and whether the column
76+
contains JSON data.
77+
#) Changed the signature for output type handlers to
78+
``handler(cursor, metadata)`` where the ``metadata`` parameter is a
79+
:ref:`FetchInfo<fetchinfoobj>` object containing the same information found
80+
in :data:`Cursor.description`. The original signature for output type
81+
handlers is deprecated and will be removed in some future version.
7382
#) Added support for fetching VARCHAR2 and LOB columns which contain JSON (and
7483
have the "IS JSON" check constraint enabled) in the same way as columns of
7584
type JSON (which requires Oracle Database 21c or higher) are fetched. In

doc/src/user_guide/globalization.rst

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -133,16 +133,16 @@ To convert numbers:
133133
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
134134
135135
# simple naive conversion
136-
def type_handler1(cursor, name, default_type, size, precision, scale):
137-
if default_type == oracledb.DB_TYPE_NUMBER:
136+
def type_handler1(cursor, metadata):
137+
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
138138
return cursor.var(oracledb.DB_TYPE_VARCHAR, arraysize=cursor.arraysize,
139-
outconverter=lambda v: v.replace('.', ','))
139+
outconverter=lambda v: v.replace('.', ','))
140140
141141
# locale conversion
142-
def type_handler2(cursor, name, default_type, size, precision, scale):
143-
if default_type == oracledb.DB_TYPE_NUMBER:
144-
return cursor.var(default_type, arraysize=cursor.arraysize,
145-
outconverter=lambda v: locale.format_string("%g", v))
142+
def type_handler2(cursor, metadata):
143+
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
144+
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
145+
outconverter=lambda v: locale.format_string("%g", v))
146146
147147
148148
connection = oracledb.connect(user="hr", password=userpwd,
@@ -186,16 +186,16 @@ To convert dates:
186186
locale_date_format = locale.nl_langinfo(locale.D_T_FMT)
187187
188188
# simple naive conversion
189-
def type_handler3(cursor, name, default_type, size, precision, scale):
190-
if default_type == oracledb.DB_TYPE_DATE:
191-
return cursor.var(default_type, arraysize=cursor.arraysize,
192-
outconverter=lambda v: v.strftime("%Y-%m-%d %H:%M:%S"))
189+
def type_handler3(cursor, metadata):
190+
if metadata.type_code is oracledb.DB_TYPE_DATE:
191+
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
192+
outconverter=lambda v: v.strftime("%Y-%m-%d %H:%M:%S"))
193193
194194
# locale conversion
195195
def type_handler4(cursor, name, default_type, size, precision, scale):
196-
if default_type == oracledb.DB_TYPE_DATE:
197-
return cursor.var(default_type, arraysize=cursor.arraysize,
198-
outconverter=lambda v: v.strftime(locale_date_format))
196+
if metadata.type_code is oracledb.DB_TYPE_DATE:
197+
return cursor.var(metadata.type_code, arraysize=cursor.arraysize,
198+
outconverter=lambda v: v.strftime(locale_date_format))
199199
200200
201201
connection = oracledb.connect(user="hr", password=userpwd,

doc/src/user_guide/lob_data.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,12 @@ handler:
107107

108108
.. code-block:: python
109109
110-
def output_type_handler(cursor, name, default_type, size, precision, scale):
111-
if default_type == oracledb.DB_TYPE_CLOB:
110+
def output_type_handler(cursor, metadata):
111+
if metadata.type_code is oracledb.DB_TYPE_CLOB:
112112
return cursor.var(oracledb.DB_TYPE_LONG, arraysize=cursor.arraysize)
113-
if default_type == oracledb.DB_TYPE_BLOB:
113+
if metadata.type_code is oracledb.DB_TYPE_BLOB:
114114
return cursor.var(oracledb.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)
115-
if default_type == oracledb.DB_TYPE_NCLOB:
115+
if metadata.type_code is oracledb.DB_TYPE_NCLOB:
116116
return cursor.var(oracledb.DB_TYPE_LONG_NVARCHAR, arraysize=cursor.arraysize)
117117
118118
connection.outputtypehandler = output_type_handler

doc/src/user_guide/sql_execution.rst

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -312,10 +312,10 @@ cursors created by that connection will have their fetch type handling changed.
312312
The output type handler is expected to be a function with the following
313313
signature::
314314

315-
handler(cursor, name, default_type, size, precision, scale)
315+
handler(cursor, metadata)
316316

317-
The parameters are the same information as the query column metadata found in
318-
:attr:`Cursor.description`.
317+
The metadata parameter is a :ref:`FetchInfo object<fetchinfoobj>`, which is the
318+
same value found in :attr:`Cursor.description`.
319319

320320
The function is called once for each column that is going to be
321321
fetched. The function is expected to return a :ref:`variable object <varobj>`
@@ -326,8 +326,8 @@ For example:
326326

327327
.. code-block:: python
328328
329-
def output_type_handler(cursor, name, default_type, size, precision, scale):
330-
if default_type == oracledb.DB_TYPE_NUMBER:
329+
def output_type_handler(cursor, metadata):
330+
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
331331
return cursor.var(oracledb.DB_TYPE_VARCHAR, arraysize=cursor.arraysize)
332332
333333
This output type handler is called once for each column in the SELECT query.
@@ -375,15 +375,15 @@ For example:
375375

376376
.. code-block:: python
377377
378-
def output_type_handler(cursor, name, default_type, size, precision, scale):
378+
def output_type_handler(cursor, metadata):
379379
380380
def out_converter(d):
381381
if isinstance(d, str):
382382
return f"{d} was a string"
383383
else:
384384
return f"{d} was not a string"
385385
386-
if default_type == oracledb.DB_TYPE_NUMBER:
386+
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
387387
return cursor.var(oracledb.DB_TYPE_VARCHAR,
388388
arraysize=cursor.arraysize, outconverter=out_converter)
389389
@@ -456,15 +456,15 @@ An example showing an :ref:`output type handler <outputtypehandlers>`, an
456456

457457
.. code-block:: python
458458
459-
def output_type_handler(cursor, name, default_type, size, precision, scale):
459+
def output_type_handler(cursor, metadata):
460460
461461
def out_converter(d):
462462
if type(d) is str:
463463
return f"{d} was a string"
464464
else:
465465
return f"{d} was not a string"
466466
467-
if default_type == oracledb.DB_TYPE_NUMBER:
467+
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
468468
return cursor.var(oracledb.DB_TYPE_VARCHAR,
469469
arraysize=cursor.arraysize, outconverter=out_converter)
470470
@@ -532,8 +532,8 @@ to use an :ref:`output type handler <outputtypehandlers>` do the conversion.
532532
533533
import decimal
534534
535-
def number_to_decimal(cursor, name, default_type, size, precision, scale):
536-
if default_type == oracledb.DB_TYPE_NUMBER:
535+
def number_to_decimal(cursor, metadata):
536+
if metadata.type_code is oracledb.DB_TYPE_NUMBER:
537537
return cursor.var(decimal.Decimal, arraysize=cursor.arraysize)
538538
539539
cursor.outputtypehandler = number_to_decimal
@@ -824,9 +824,8 @@ The following sample demonstrates how to use this feature:
824824
.. code-block:: python
825825
826826
# define output type handler
827-
def return_strings_as_bytes(cursor, name, default_type, size,
828-
precision, scale):
829-
if default_type == oracledb.DB_TYPE_VARCHAR:
827+
def return_strings_as_bytes(cursor, metadata):
828+
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
830829
return cursor.var(str, arraysize=cursor.arraysize,
831830
bypass_decode=True)
832831
@@ -900,9 +899,10 @@ columns:
900899

901900
.. code-block:: python
902901
903-
def output_type_handler(cursor, name, default_type, size, precision, scale):
904-
if default_type == oracledb.DB_TYPE_VARCHAR:
905-
return cursor.var(default_type, size, arraysize=cursor.arraysize,
902+
def output_type_handler(cursor, metadata):
903+
if metadata.type_code is oracledb.DB_TYPE_VARCHAR:
904+
return cursor.var(metadata.type_code, size,
905+
arraysize=cursor.arraysize,
906906
encoding_errors="replace")
907907
908908
cursor.outputtypehandler = output_type_handler

samples/generic_row_factory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2016, 2022, Oracle and/or its affiliates.
2+
# Copyright (c) 2016, 2023, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -52,7 +52,7 @@ def execute(self, statement, args=None):
5252
if prepare_needed:
5353
description = self.description
5454
if description is not None:
55-
names = [d[0] for d in description]
55+
names = [d.name for d in description]
5656
self.rowfactory = collections.namedtuple("GenericQuery", names)
5757
return result
5858

0 commit comments

Comments
 (0)