Compare commits

...

12 Commits
main ... v1.1.x

Author SHA1 Message Date
Anthony Tuininga f2f2ed9fd5 Update documentation for querying corrupt data. 2022-09-28 17:21:32 -06:00
Anthony Tuininga 065d8ba81a Preparing to release python-oracledb 1.1.1. 2022-09-28 09:33:06 -06:00
Anthony Tuininga 790ee29d3c Reduce minimum version of cryptography package for ease of building on
Oracle Linux.
2022-09-28 09:32:43 -06:00
Anthony Tuininga 1674d8ce8f Fixed bug that caused "Connection.is_healthy()" to return "True" after a
connection has been killed.
2022-09-28 09:14:51 -06:00
Anthony Tuininga 6dda5b597c The mode of python-oracledb is now fixed only after a
call to "oracledb.init_oracle_client()", "oracledb.connect()" or
"oracledb.create_pool()" has completed successfully (#44).
2022-09-28 09:14:17 -06:00
Anthony Tuininga 0a59085f64 Additional test cases. 2022-09-28 09:13:57 -06:00
Anthony Tuininga 512826e028 Check for multiple probe/control/EOF packets being sent by the server. 2022-09-28 09:09:28 -06:00
Anthony Tuininga d3e14f6b87 Corrected and enhanced type checking issues (#52, #54, #60). 2022-09-28 09:08:54 -06:00
Anthony Tuininga aa9b1daa65 Fixed bug returning metadata of SODA documents inserted into a collection
using saveAndGet().
2022-09-28 09:08:30 -06:00
Anthony Tuininga b0f0c671b2 Fixed bug that prevented binding data of types DB_TYPE_ROWID and
DB_TYPE_UROWID.
2022-09-28 09:07:47 -06:00
Anthony Tuininga 6f37dec1d9 Documentation tweaks. 2022-09-28 09:07:10 -06:00
Anthony Tuininga 25fb52b6b2 Bump version to 1.1.1 in preparation for release of patch. 2022-09-28 09:06:36 -06:00
34 changed files with 569 additions and 279 deletions

View File

@ -1,4 +1,4 @@
# python-oracledb 1.0
# python-oracledb 1.1
python-oracledb is a [Python programming language][python] extension module
allowing Python programs to connect to [Oracle Database][oracledb]. It is the

View File

@ -2066,9 +2066,9 @@ DB API Types
.. data:: 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.
database types :data:`DB_TYPE_ROWID` and :data:`DB_TYPE_UROWID` 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
@ -2281,6 +2281,17 @@ when binding data.
:data:`DATETIME`.
.. data:: DB_TYPE_UROWID
Describes columns, attributes or array elements in a database that are of
type UROWID. It will compare equal to the DB API type :data:`ROWID`.
.. note::
This type is not supported in python-oracledb Thick mode.
See :ref:`querymetadatadiff`.
.. data:: DB_TYPE_VARCHAR
Describes columns, attributes or array elements in a database that are of

View File

@ -7,6 +7,40 @@ python-oracledb Release Notes
For deprecations, see :ref:`Deprecations <deprecations>`.
oracledb 1.1.1 (September 2022)
-------------------------------
Thin Mode Changes
+++++++++++++++++
#) Fixed bug that prevented binding data of types
:data:`~oracledb.DB_TYPE_ROWID` and :data:`~oracledb.DB_TYPE_UROWID`.
#) Fixed bug that caused :meth:`Connection.is_healthy()` to return `True`
after a connection has been killed.
#) Internally, before a connection is returned from a pool, perform additional
checks in order to avoid returning a dead connection from the pool.
Thick Mode Changes
++++++++++++++++++
#) Fixed bug returning metadata of SODA documents inserted into a collection
using :meth:`SodaCollection.saveAndGet()`.
Common Changes
++++++++++++++
#) Fixed type checking errors
(`issue 52 <https://github.com/oracle/python-oracledb/issues/52>`__).
#) Enhanced type checking
(`issue 54 <https://github.com/oracle/python-oracledb/issues/54>`__),
(`issue 60 <https://github.com/oracle/python-oracledb/issues/60>`__).
#) The mode of python-oracledb is now fixed only after a call to
:meth:`oracledb.init_oracle_client()`, :meth:`oracledb.connect()` or
:meth:`oracledb.create_pool()` has completed successfully
(`issue 44 <https://github.com/oracle/python-oracledb/issues/44>`__).
#) Improved test suite and documentation.
oracledb 1.1.0 (September 2022)
-------------------------------

View File

@ -463,11 +463,11 @@ values.
* - ROWID
- DB_TYPE_ROWID
- Yes
- cannot be set
- bytes, str
* - UROWID
- DB_TYPE_ROWID
- DB_TYPE_ROWID, DB_TYPE_UROWID (only supported in python-oracledb Thin mode)
- Yes. May show DB_TYPE_UROWID in metadata. See :ref:`Query Metadata Differences <querymetadatadiff>`.
- cannot be set
- bytes, str
* - CHAR
- DB_TYPE_CHAR
- Yes
@ -578,7 +578,7 @@ these arrays.
* - DB_TYPE_RAW
- bytes, str
* - DB_TYPE_ROWID
- cannot be set
- bytes, str
* - DB_TYPE_TIMESTAMP
- datetime.date, datetime.datetime
* - DB_TYPE_TIMESTAMP_LTZ
@ -586,6 +586,6 @@ these arrays.
* - DB_TYPE_TIMESTAMP_TZ
- datetime.date, datetime.datetime
* - DB_TYPE_UROWID
- cannot be set
- bytes, str
* - DB_TYPE_VARCHAR
- bytes, str

View File

@ -183,6 +183,48 @@ fetching a row and then updating that row by binding its rowid:
where rowid = :rid""", manager_id=205, rid=rowid)
Binding UROWID Values
=====================
Universal rowids (UROWID) are used to uniquely identify rows in index
organized tables. In python-oracledb, UROWID values are represented as strings.
The example below shows fetching a row from index organized table
``universal_rowids`` and then updating that row by binding its urowid:
.. code-block:: sql
CREATE TABLE universal_rowids (
int_col number(9) not null,
str_col varchar2(250) not null,
date_col date not null,
CONSTRAINT universal_rowids_pk PRIMARY KEY(int_col, str_col, date_col)
) ORGANIZATION INDEX
.. code-block:: python
ridvar = cursor.var(oracledb.DB_TYPE_UROWID)
# fetch the row
cursor.execute("""
begin
select rowid into :rid from universal_rowids
where int_col = 3;
end;""", rid=ridvar)
# update the row by binding UROWID
cursor.execute("""
update universal_rowids set
str_col = :str_val
where rowid = :rowid_val""",
str_val="String #33", rowid_val=ridvar)
Note that the type :attr:`oracledb.DB_TYPE_UROWID` is only supported in
python-oracledb Thin mode. For python-oracledb Thick mode, the database type
UROWID can be bound with type :attr:`oracledb.DB_TYPE_ROWID`.
See :ref:`querymetadatadiff`.
DML RETURNING Bind Variables
============================

View File

@ -795,13 +795,13 @@ In order to install using the source on GitHub, use the following commands::
Note that if you download a source zip file directly from GitHub then you will
also need to download an `ODPI-C <https://github.com/oracle/odpi>`__ source zip
file and put the extracted contents inside the subdirectory
"python-oracledb-*/src/oracledb/impl/thick/odpi".
file and put the extracted contents inside the "odpi" subdirectory, for example
in "python-oracledb-main/src/oracledb/impl/thick/odpi".
Python-oracledb source code is also available from opensource.oracle.com. This
can be installed with::
git clone --recurse-submodules git://opensource.oracle.com/git/oracle/python-oracledb.git
git clone --recurse-submodules https://opensource.oracle.com/git/oracle/python-oracledb.git
cd python-oracledb
python setup.py build
python setup.py install

View File

@ -239,7 +239,7 @@ Python object that is returned by default. Python types can be changed with
- :attr:`oracledb.DB_TYPE_TIMESTAMP_TZ`
- datetime.datetime [2]_
* - UROWID
- :attr:`oracledb.DB_TYPE_ROWID`
- :attr:`oracledb.DB_TYPE_ROWID`, :attr:`oracledb.DB_TYPE_UROWID`
- str
* - VARCHAR2
- :attr:`oracledb.DB_TYPE_VARCHAR`
@ -744,15 +744,10 @@ Querying Corrupt Data
If queries fail with the error "codec can't decode byte" when you select data,
then:
* Check your :ref:`character set <globalization>` is correct. Review the
* Check if your :ref:`character set <globalization>` is correct. Review the
:ref:`database character sets <findingcharset>`. Check with
:ref:`fetching-raw-data`. Consider using UTF-8, if this is appropriate:
.. code-block:: python
connection = oracledb.connect(user="hr", password=userpwd,
dsn="dbhost.example.com/orclpdb",
encoding="UTF-8", nencoding="UTF-8")
:ref:`fetching-raw-data`. Note that the encoding used for all character
data in python-oracledb is "UTF-8".
* Check for corrupt data in the database.

View File

@ -36,7 +36,7 @@ classifiers =
zip_safe = false
python_requires = >=3.6
setup_requires = cython
install_requires = cryptography>=3.4
install_requires = cryptography>=3.2.1
test_suite = tests
packages = find:
package_dir =

View File

@ -32,7 +32,7 @@
import datetime
from . import connection as connection_module
from typing import Type, Union
from typing import Union, List
from . import errors, exceptions
from .dbobject import DbObject, DbObjectType
@ -48,7 +48,7 @@ class Queue:
queue._impl = impl
return queue
def _verify_message(self, message: Type["MessageProperties"]) -> None:
def _verify_message(self, message: "MessageProperties") -> None:
"""
Internal method used for verifying a message.
"""
@ -58,7 +58,7 @@ class Queue:
errors._raise_err(errors.ERR_MESSAGE_HAS_NO_PAYLOAD)
@property
def connection(self) -> Type["connection_module.Connection"]:
def connection(self) -> "connection_module.Connection":
"""
Returns the connection on which the queue was created.
"""
@ -72,13 +72,13 @@ class Queue:
message_impls = self._impl.deq_many(max_num_messages)
return [MessageProperties._from_impl(impl) for impl in message_impls]
def deqMany(self, max_num_messages: int) -> list:
def deqMany(self, max_num_messages: int) -> List["MessageProperties"]:
"""
Deprecated: use deqmany() instead.
"""
return self.deqmany(max_num_messages)
def deqone(self) -> Union[Type["MessageProperties"], None]:
def deqone(self) -> Union["MessageProperties", None]:
"""
Dequeues at most one message from the queue and returns it. If no
message is dequeued, None is returned.
@ -87,14 +87,14 @@ class Queue:
if message_impl is not None:
return MessageProperties._from_impl(message_impl)
def deqOne(self) -> Union[Type["MessageProperties"], None]:
def deqOne(self) -> Union["MessageProperties", None]:
"""
Deprecated: use deqone() instead.
"""
return self.deqone()
@property
def deqoptions(self) -> Type["DeqOptions"]:
def deqoptions(self) -> "DeqOptions":
"""
Returns the options that will be used when dequeuing messages from the
queue.
@ -102,7 +102,7 @@ class Queue:
return self._deq_options
@property
def deqOptions(self) -> Type["DeqOptions"]:
def deqOptions(self) -> "DeqOptions":
"""
Deprecated: use deqoptions instead.
"""
@ -131,7 +131,7 @@ class Queue:
"""
return self.enqmany(messages)
def enqone(self, message: Type["MessageProperties"]) -> None:
def enqone(self, message: "MessageProperties") -> None:
"""
Enqueues a single message into the queue. The message must be a message
property object which has had its payload attribute set to a value that
@ -140,14 +140,14 @@ class Queue:
self._verify_message(message)
self._impl.enq_one(message._impl)
def enqOne(self, message: Type["MessageProperties"]) -> None:
def enqOne(self, message: "MessageProperties") -> None:
"""
Deprecated: use enqone() instead.
"""
return self.enqone(message)
@property
def enqoptions(self) -> Type["EnqOptions"]:
def enqoptions(self) -> "EnqOptions":
"""
Returns the options that will be used when enqueuing messages into the
queue.
@ -155,7 +155,7 @@ class Queue:
return self._enq_options
@property
def enqOptions(self) -> Type["EnqOptions"]:
def enqOptions(self) -> "EnqOptions":
"""
Deprecated: use enqoptions() instead.
"""

View File

@ -34,7 +34,7 @@
#------------------------------------------------------------------------------
import functools
from typing import Type, Union, Callable
from typing import Union, Callable
import oracledb
@ -575,7 +575,7 @@ class ConnectParams:
"""
return self._impl.wallet_location
def copy(self) -> Type["ConnectParams"]:
def copy(self) -> "ConnectParams":
"""
Creates a copy of the parameters and returns it.
"""

View File

@ -64,7 +64,7 @@ class Connection:
def __init__(self,
dsn: str=None, *,
pool: Type["pool_module.ConnectionPool"]=None,
pool: "pool_module.ConnectionPool"=None,
params: ConnectParams=None,
**kwargs) -> None:
"""
@ -99,7 +99,8 @@ class Connection:
self._impl = None
# determine if thin mode is being used
thin = driver_mode.check_and_return_mode()
with driver_mode.get_manager() as mode_mgr:
thin = mode_mgr.thin
# determine which connection parameters to use
if params is None:
@ -502,7 +503,7 @@ class Connection:
This function performs a local check. To fully check a connection's
health, use ping() which performs a round-trip to the database.
"""
return self._impl.get_is_healthy()
return self._impl is not None and self._impl.get_is_healthy()
@property
def ltxid(self) -> bytes:
@ -603,7 +604,7 @@ class Connection:
"""
return self.tpc_prepare()
def queue(self, name: str, payload_type: [DbObjectType, str]=None, *,
def queue(self, name: str, payload_type: Union[DbObjectType, str]=None, *,
payloadType: DbObjectType=None) -> Queue:
"""
Creates and returns a queue which is used to enqueue and dequeue
@ -1001,7 +1002,7 @@ def _connection_factory(f):
"""
@functools.wraps(f)
def connect(dsn: str=None, *,
pool: Type["pool_module.ConnectionPool"]=None,
pool: "pool_module.ConnectionPool"=None,
conn_class: Type[Connection]=Connection,
params: ConnectParams=None,
**kwargs) -> Connection:
@ -1014,7 +1015,7 @@ def _connection_factory(f):
@_connection_factory
def connect(dsn: str=None, *,
pool: Type["pool_module.ConnectionPool"]=None,
pool: "pool_module.ConnectionPool"=None,
conn_class: Type[Connection]=Connection,
params: ConnectParams=None,
user: str=None,

View File

@ -29,7 +29,7 @@
# fetching results from queries.
#------------------------------------------------------------------------------
from typing import Any, Type, Union, Callable
from typing import Any, Union, Callable
from . import __name__ as MODULE_NAME
from . import errors, exceptions
@ -42,7 +42,7 @@ from .dbobject import DbObjectType
class Cursor:
__module__ = MODULE_NAME
def __init__(self, connection: Type["connection_module.Connection"],
def __init__(self, connection: "connection_module.Connection",
scrollable: bool = False) -> None:
self._impl = None
self.connection = connection
@ -309,7 +309,7 @@ class Cursor:
def execute(self, statement: Union[str, None],
parameters: Union[list, tuple, dict]=None,
**keyword_parameters: dict) -> Union[Type["Cursor"], None]:
**keyword_parameters: dict) -> Union["Cursor", None]:
"""
Execute a statement against the database.
@ -739,7 +739,7 @@ class Cursor:
encoding_errors: str=None,
bypass_decode: bool=False,
*,
encodingErrors: str=None) -> Type["Var"]:
encodingErrors: str=None) -> "Var":
"""
Create a variable with the specified characteristics. This method was
designed for use with PL/SQL in/out variables where the length or type

View File

@ -29,7 +29,7 @@
# object type metadata: DbObject, DbObjectType and DbObjectAttr.
#------------------------------------------------------------------------------
from typing import Sequence, Type, Union
from typing import Sequence, Union
from . import __name__ as MODULE_NAME
from .base_impl import DbType
@ -91,7 +91,7 @@ class DbObject:
ix = self._impl.get_next_index(ix)
return result
def copy(self) -> Type["DbObject"]:
def copy(self) -> "DbObject":
"""
Create a copy of the object and return it.
"""
@ -181,7 +181,7 @@ class DbObject:
self._impl.trim(num)
@property
def type(self) -> Type["DbObjectType"]:
def type(self) -> "DbObjectType":
"""
Returns an ObjectType corresponding to the type of the object.
"""
@ -211,7 +211,7 @@ class DbObjectAttr:
return self._impl.name
@property
def type(self) -> Union[Type["DbObjectType"], DbType]:
def type(self) -> Union["DbObjectType", DbType]:
"""
This read-only attribute returns the type of the attribute. This will
be an Oracle Object Type if the variable binds Oracle objects;
@ -274,7 +274,7 @@ class DbObjectType:
return self._impl.name
@property
def element_type(self) -> Union[Type["DbObjectType"], DbType]:
def element_type(self) -> Union["DbObjectType", DbType]:
"""
This read-only attribute returns the type of elements found in
collections of this type, if iscollection is True; otherwise, it

View File

@ -31,27 +31,63 @@
# simultaneously.
#------------------------------------------------------------------------------
import threading
from . import errors
# this flag is used to indicate which mode is currently being used:
# The DriverModeHandler class is used to manage which mode the driver is using.
#
# The "thin_mode" flag contains the current state:
# None: neither thick nor thin implementation has been used yet
# False: thick implementation is being used
# True: thin implementation is being used
thin_mode = None
def check_and_return_mode(requested_thin_mode=None):
#
# The "requested_thin_mode" flag is set to the mode that is being requested:
# False: thick implementation is being initialized
# True: thin implementation is being initialized
class DriverModeManager:
"""
Internal function to return the current mode of python-oracledb.
Manages the mode the driver is using. The "thin_mode" flag contains the
current state:
None: neither thick nor thin implementation has been used yet
False: thick implementation is being used
True: thin implementation is being used
The "requested_thin_mode" is set to the mode that is being requested, but
only while initialization is taking place (otherwise, it contains the value
None):
False: thick implementation is being initialized
True: thin implementation is being initialized
The condition is used to ensure that only one thread is performing
initialization.
"""
def __init__(self):
self.thin_mode = None
self.requested_thin_mode = None
self.condition = threading.Condition()
If neither the thick nor the thin implementation have been used yet (the
value of thin_mode is None), then:
def __enter__(self):
return self
- the mode is set to the requested mode, or
def __exit__(self, exc_type, exc_value, exc_tb):
with self.condition:
if exc_type is None and exc_value is None and exc_tb is None \
and self.requested_thin_mode is not None:
self.thin_mode = self.requested_thin_mode
self.requested_thin_mode = None
self.condition.notify()
- the mode is set to thin, if no mode is requested.
@property
def thin(self):
if self.requested_thin_mode is not None:
return self.requested_thin_mode
return self.thin_mode
Otherwise, if requested_thin_mode is used and the mode requested
does not match the current mode, an error is raised.
manager = DriverModeManager()
def get_manager(requested_thin_mode=None):
"""
Returns the manager, but only after ensuring that no other threads are
attempting to initialize the mode.
NOTE: the current implementation of the driver only requires
requested_thin_mode to be set when initializing the thick mode; for this
@ -59,15 +95,19 @@ def check_and_return_mode(requested_thin_mode=None):
being created. If this assumption changes, a new error message will be
required.
"""
global thin_mode
if thin_mode is None:
with manager.condition:
if manager.thin_mode is None:
if manager.requested_thin_mode is not None:
manager.condition.wait()
if manager.thin_mode is None:
if requested_thin_mode is None:
thin_mode = True
manager.requested_thin_mode = True
else:
thin_mode = requested_thin_mode
elif requested_thin_mode is not None and requested_thin_mode != thin_mode:
manager.requested_thin_mode = requested_thin_mode
elif requested_thin_mode is not None \
and requested_thin_mode != manager.thin_mode:
errors._raise_err(errors.ERR_THIN_CONNECTION_ALREADY_CREATED)
return thin_mode
return manager
def is_thin_mode() -> bool:
@ -77,14 +117,14 @@ def is_thin_mode() -> bool:
Immediately after python-oracledb is imported, this function will return
True indicating that python-oracledb defaults to Thin mode. If
oracledb.init_oracle_client() is called, then a subsequent call to
is_thin_mode() will return False indicating that Thick mode is enabled.
Once the first standalone connection or connection pool is created, or a
call to oracledb.init_oracle_client() is made, then python-oracledb's mode
is fixed and the value returned by is_thin_mode() will never change for the
lifetime of the process.
oracledb.init_oracle_client() is called successfully, then a subsequent
call to is_thin_mode() will return False indicating that Thick mode is
enabled. Once the first standalone connection or connection pool is
created succesfully, or a call to oracledb.init_oracle_client() is made
successfully, then python-oracledb's mode is fixed and the value returned
by is_thin_mode() will never change for the lifetime of the process.
"""
if thin_mode is not None:
return thin_mode
if manager.thin_mode is not None:
return manager.thin_mode
return True

View File

@ -452,7 +452,7 @@ def init_oracle_client(lib_dir=None, config_dir=None, error_url=None,
if params_tuple != driver_context_params:
errors._raise_err(errors.ERR_LIBRARY_ALREADY_INITIALIZED)
return
driver_mode.check_and_return_mode(requested_thin_mode=False)
with driver_mode.get_manager(requested_thin_mode=False) as mode_mgr:
memset(&params, 0, sizeof(dpiContextCreateParams))
encoding_bytes = constants.ENCODING.encode()
params.defaultEncoding = encoding_bytes
@ -474,7 +474,8 @@ def init_oracle_client(lib_dir=None, config_dir=None, error_url=None,
error_url_bytes = constants.INSTALLATION_URL.encode()
params.loadErrorUrl = error_url_bytes
if dpiContext_createWithParams(DPI_MAJOR_VERSION, DPI_MINOR_VERSION,
&params, &driver_context, &error_info) < 0:
&params, &driver_context,
&error_info) < 0:
_raise_from_info(&error_info)
driver_context_params = params_tuple

View File

@ -1016,15 +1016,22 @@ cdef class ReadBuffer:
const char_type *input_ptr
bytearray output_value
uint32_t num_bytes
uint8_t length
Rowid rowid
# check for null
self.read_ub4(&num_bytes)
if num_bytes == 0:
self.read_ub1(&length)
if _is_null_length(length):
return None
# handle physical rowid
if length == 1:
self.skip_ub1()
else:
self.read_ub4(&num_bytes)
self.read_raw_bytes_chunked(&input_ptr, &input_len)
# handle physical rowid
if input_ptr[0] == 1:
rowid.rba = unpack_uint32(&input_ptr[1], BYTE_ORDER_MSB)
rowid.partition_id = unpack_uint16(&input_ptr[5], BYTE_ORDER_MSB)

View File

@ -325,7 +325,8 @@ cdef class ThinConnImpl(BaseConnImpl):
return self._internal_name
def get_is_healthy(self):
return not self._protocol._read_buf._session_needs_to_be_closed
return self._protocol._socket is not None \
and not self._protocol._read_buf._session_needs_to_be_closed
def get_ltxid(self):
return self._ltxid or b''

View File

@ -621,6 +621,7 @@ DEF TNS_MAX_CURSORS_TO_CLOSE = 500
DEF TNS_TXN_IN_PROGRESS = 0x00000002
DEF TNS_MAX_CONNECT_DATA = 230
DEF TNS_CHUNK_SIZE = 32767
DEF TNS_MAX_UROWID_LENGTH = 3950
# base 64 encoding alphabet
DEF TNS_BASE64_ALPHABET = \

View File

@ -537,7 +537,8 @@ cdef class MessageWithData(Message):
if var_impl.bypass_decode:
ora_type_num = TNS_DATA_TYPE_RAW
if buffer_size == 0 and ora_type_num != TNS_DATA_TYPE_LONG \
and ora_type_num != TNS_DATA_TYPE_LONG_RAW:
and ora_type_num != TNS_DATA_TYPE_LONG_RAW \
and ora_type_num != TNS_DATA_TYPE_UROWID:
column_value = None # column is null by describe
elif ora_type_num == TNS_DATA_TYPE_VARCHAR \
or ora_type_num == TNS_DATA_TYPE_CHAR \
@ -557,7 +558,7 @@ cdef class MessageWithData(Message):
column_value = buf.read_date()
elif ora_type_num == TNS_DATA_TYPE_ROWID:
if not self.in_fetch:
column_value = buf.read_urowid()
column_value = buf.read_str(TNS_CS_IMPLICIT)
else:
buf.read_ub1(&num_bytes)
if _is_null_length(num_bytes):
@ -566,6 +567,9 @@ cdef class MessageWithData(Message):
buf.read_rowid(&rowid)
column_value = _encode_rowid(&rowid)
elif ora_type_num == TNS_DATA_TYPE_UROWID:
if not self.in_fetch:
column_value = buf.read_str(TNS_CS_IMPLICIT)
else:
column_value = buf.read_urowid()
elif ora_type_num == TNS_DATA_TYPE_BINARY_DOUBLE:
column_value = buf.read_binary_double()
@ -895,11 +899,14 @@ cdef class MessageWithData(Message):
list bind_var_impls) except -1:
cdef:
uint8_t ora_type_num, flag
uint32_t buffer_size
ThinVarImpl var_impl
for var_impl in bind_var_impls:
ora_type_num = var_impl.dbtype._ora_type_num
if ora_type_num == TNS_DATA_TYPE_ROWID:
ora_type_num = TNS_DATA_TYPE_UROWID
buffer_size = var_impl.buffer_size
if ora_type_num in (TNS_DATA_TYPE_ROWID, TNS_DATA_TYPE_UROWID):
ora_type_num = TNS_DATA_TYPE_VARCHAR
buffer_size = TNS_MAX_UROWID_LENGTH
flag = TNS_BIND_USE_INDICATORS
if var_impl.is_array:
flag |= TNS_BIND_ARRAY
@ -909,10 +916,10 @@ cdef class MessageWithData(Message):
# expects that and complains if any other value is sent!
buf.write_uint8(0)
buf.write_uint8(0)
if var_impl.buffer_size >= TNS_MIN_LONG_LENGTH:
if buffer_size >= TNS_MIN_LONG_LENGTH:
buf.write_ub4(TNS_MAX_LONG_LENGTH)
else:
buf.write_ub4(var_impl.buffer_size)
buf.write_ub4(buffer_size)
if var_impl.is_array:
buf.write_ub4(var_impl.num_elements)
else:
@ -996,6 +1003,9 @@ cdef class MessageWithData(Message):
num_bytes = <uint32_t> len(lob_impl._locator)
buf.write_ub4(num_bytes)
buf.write_bytes_chunked(lob_impl._locator)
elif ora_type_num in (TNS_DATA_TYPE_ROWID, TNS_DATA_TYPE_UROWID):
temp_bytes = (<str> value).encode()
buf.write_bytes_chunked(temp_bytes)
else:
errors._raise_err(errors.ERR_DB_TYPE_NOT_SUPPORTED,
name=var_impl.dbtype.name)

View File

@ -331,8 +331,10 @@ cdef class ThinPoolImpl(BasePoolImpl):
read_buf = conn_impl._protocol._read_buf
if not read_buf._session_needs_to_be_closed:
socket_list = [conn_impl._protocol._socket]
while not read_buf._session_needs_to_be_closed:
read_socks, _, _ = select.select(socket_list, [], [], 0)
if read_socks:
if not read_socks:
break
read_buf.check_control_packet()
if read_buf._session_needs_to_be_closed:
with self._condition:

View File

@ -88,7 +88,8 @@ class ConnectionPool:
params_impl.set(kwargs)
self._connection_type = \
params_impl.connectiontype or connection_module.Connection
thin = driver_mode.check_and_return_mode()
with driver_mode.get_manager() as mode_mgr:
thin = mode_mgr.thin
if dsn is not None:
dsn = params_impl.parse_dsn(dsn, thin)
if dsn is None:
@ -120,7 +121,7 @@ class ConnectionPool:
tag: str=None,
matchanytag: bool=False,
shardingkey: list=None,
supershardingkey: list=None) -> Type["connection_module.Connection"]:
supershardingkey: list=None) -> "connection_module.Connection":
"""
Acquire a connection from the pool and return it.
@ -177,7 +178,7 @@ class ConnectionPool:
self._impl.close(force)
self._impl = None
def drop(self, connection: Type["connection_module.Connection"]) -> None:
def drop(self, connection: "connection_module.Connection") -> None:
"""
Drop the connection from the pool, which is useful if the connection is
no longer usable (such as when the database session is killed).
@ -322,7 +323,7 @@ class ConnectionPool:
def ping_interval(self, value: int) -> None:
self._impl.set_ping_interval(value)
def release(self, connection: Type["connection_module.Connection"],
def release(self, connection: "connection_module.Connection",
tag: str=None) -> None:
"""
Release the connection back to the pool now, rather than whenever

View File

@ -468,7 +468,7 @@ class PoolParams(ConnectParams):
"""
return self._impl.wait_timeout
def copy(self) -> Type["PoolParams"]:
def copy(self) -> "PoolParams":
"""
Creates a copy of the parameters and returns it.
"""

View File

@ -29,7 +29,7 @@
# SodaDatabase, SodaCollection, SodaDocument, SodaDocCursor and SodaOperation.
#------------------------------------------------------------------------------
from typing import Type, Union
from typing import Union, List
import json
from . import connection
@ -58,7 +58,7 @@ class SodaDatabase:
return json.dumps(content).encode()
def createCollection(self, name: str, metadata: Union[str, dict]=None,
mapMode: bool=False) -> Type["SodaCollection"]:
mapMode: bool=False) -> "SodaCollection":
"""
Creates a SODA collection with the given name and returns a new SODA
collection object. If you try to create a collection, and a collection
@ -85,7 +85,7 @@ class SodaDatabase:
return SodaCollection._from_impl(self, collection_impl)
def createDocument(self, content: object, key: str=None,
mediaType: str="application/json") -> Type["SodaDocument"]:
mediaType: str="application/json") -> "SodaDocument":
"""
Creates a SODA document usable for SODA write operations. You only need
to use this method if your collection requires client-assigned keys or
@ -112,7 +112,8 @@ class SodaDatabase:
doc_impl = self._impl.create_document(content_bytes, key, mediaType)
return SodaDocument._from_impl(doc_impl)
def getCollectionNames(self, startName: str=None, limit: int=0) -> list:
def getCollectionNames(self, startName: str=None,
limit: int=0) -> List[str]:
"""
Returns a list of the names of collections in the database that match
the criteria, in alphabetical order.
@ -126,7 +127,7 @@ class SodaDatabase:
"""
return self._impl.get_collection_names(startName, limit)
def openCollection(self, name: str) -> Type["SodaCollection"]:
def openCollection(self, name: str) -> "SodaCollection":
"""
Opens an existing collection with the given name and returns a new SODA
collection object. If a collection with that name does not exist, None
@ -191,7 +192,7 @@ class SodaCollection:
"""
return self._impl.drop_index(name, force)
def find(self) -> Type["SodaOperation"]:
def find(self) -> "SodaOperation":
"""
This method is used to begin an operation that will act upon documents
in the collection. It creates and returns a SodaOperation object which
@ -200,7 +201,7 @@ class SodaCollection:
"""
return SodaOperation(self)
def getDataGuide(self) -> Type["SodaDocument"]:
def getDataGuide(self) -> "SodaDocument":
"""
Returns a SODA document object containing property names, data types
and lengths inferred from the JSON documents in the collection. It can
@ -253,7 +254,7 @@ class SodaCollection:
self._impl.insert_one(doc_impl, hint=None, return_doc=False)
def insertOneAndGet(self, doc: object,
hint: str=None) -> Type["SodaDocument"]:
hint: str=None) -> "SodaDocument":
"""
Similarly to insertOne() this method inserts a given document into the
collection. The only difference is that it returns a SODA Document
@ -300,7 +301,7 @@ class SodaCollection:
doc_impl = self._process_doc_arg(doc)
self._impl.save(doc_impl, hint=None, return_doc=False)
def saveAndGet(self, doc: object, hint: str=None) -> Type["SodaDocument"]:
def saveAndGet(self, doc: object, hint: str=None) -> "SodaDocument":
"""
Saves a document into the collection. This method is equivalent to
insertOneAndGet() except that if client-assigned keys are used, and the
@ -318,7 +319,7 @@ class SodaCollection:
doc_impl = self._process_doc_arg(doc)
if hint is not None and not isinstance(hint, str):
raise TypeError("expecting a string")
return_doc_impl = self._impl.save(doc_impl, hint, return_doc=False)
return_doc_impl = self._impl.save(doc_impl, hint, return_doc=True)
return SodaDocument._from_impl(return_doc_impl)
def truncate(self) -> None:
@ -461,7 +462,7 @@ class SodaOperation:
"""
return self._collection._impl.get_count(self)
def fetchArraySize(self, value: int) -> Type["SodaOperation"]:
def fetchArraySize(self, value: int) -> "SodaOperation":
"""
This is a tuning method to specify the number of documents that are
internally fetched in batches by calls to getCursor() and
@ -480,7 +481,7 @@ class SodaOperation:
self._fetch_array_size = value
return self
def filter(self, value: Union[dict, str]) -> Type["SodaOperation"]:
def filter(self, value: Union[dict, str]) -> "SodaOperation":
"""
Sets a filter specification for complex document queries and ordering
of JSON documents. Filter specifications must be provided as a
@ -499,7 +500,7 @@ class SodaOperation:
raise TypeError("expecting string or dictionary")
return self
def getCursor(self) -> Type["SodaDocCursor"]:
def getCursor(self) -> "SodaDocCursor":
"""
Returns a SodaDocCursor object that can be used to iterate over the
documents that match the criteria.
@ -513,7 +514,7 @@ class SodaOperation:
"""
return [d for d in self.getCursor()]
def getOne(self) -> Union[Type["SodaDocument"], None]:
def getOne(self) -> Union["SodaDocument", None]:
"""
Returns a single SodaDocument object that matches the criteria. Note
that if multiple documents match the criteria only the first one is
@ -523,7 +524,7 @@ class SodaOperation:
if doc_impl is not None:
return SodaDocument._from_impl(doc_impl)
def hint(self, value: str) -> Type["SodaOperation"]:
def hint(self, value: str) -> "SodaOperation":
"""
Specifies a hint that will be provided to the SODA operation when it is
performed. This is expected to be a string in the same format as SQL
@ -541,7 +542,7 @@ class SodaOperation:
self._hint = value
return self
def key(self, value: str) -> Type["SodaOperation"]:
def key(self, value: str) -> "SodaOperation":
"""
Specifies that the document with the specified key should be returned.
This causes any previous calls made to this method and keys() to be
@ -556,7 +557,7 @@ class SodaOperation:
self._keys = None
return self
def keys(self, value: list) -> Type["SodaOperation"]:
def keys(self, value: list) -> "SodaOperation":
"""
Specifies that documents that match the keys found in the supplied
sequence should be returned. This causes any previous calls made to
@ -573,7 +574,7 @@ class SodaOperation:
self._key = None
return self
def limit(self, value: int) -> Type["SodaOperation"]:
def limit(self, value: int) -> "SodaOperation":
"""
Specifies that only the specified number of documents should be
returned. This method is only usable for read operations such as
@ -609,7 +610,7 @@ class SodaOperation:
return self._collection._impl.replace_one(self, doc_impl,
return_doc=False)
def replaceOneAndGet(self, doc: object) -> Type["SodaDocument"]:
def replaceOneAndGet(self, doc: object) -> "SodaDocument":
"""
Similarly to replaceOne(), this method replaces a single document in
the collection with the specified document. The only difference is that
@ -621,7 +622,7 @@ class SodaOperation:
return_doc=True)
return SodaDocument._from_impl(return_doc_impl)
def skip(self, value: int) -> Type["SodaOperation"]:
def skip(self, value: int) -> "SodaOperation":
"""
Specifies the number of documents that match the other criteria that
will be skipped. This method is only usable for read operations such as
@ -636,7 +637,7 @@ class SodaOperation:
self._skip = value
return self
def version(self, value: str) -> Type["SodaOperation"]:
def version(self, value: str) -> "SodaOperation":
"""
Specifies that documents with the specified version should be returned.
Typically this is used with key() to implement optimistic locking, so

View File

@ -30,7 +30,7 @@
# events are detected.
#------------------------------------------------------------------------------
from typing import Callable, Type, Union
from typing import Callable, Union, List
from . import connection
class Subscription:
@ -53,7 +53,7 @@ class Subscription:
return self._impl.callback
@property
def connection(self) -> Type["connection.Connection"]:
def connection(self) -> "connection.Connection":
"""
Returns the connection that was used to register the subscription when
it was created.
@ -172,7 +172,7 @@ class Message:
@property
def consumer_name(self) -> str:
def consumer_name(self) -> Union[str, None]:
"""
Returns the name of the consumer which generated the notification. It
will be populated if the subscription was created with the namespace
@ -181,28 +181,29 @@ class Message:
return self._consumer_name
@property
def consumerName(self) -> str:
def consumerName(self) -> Union[str, None]:
"""
Deprecated. Use property consumer_name instead.
"""
return self.consumer_name
@property
def dbname(self) -> str:
def dbname(self) -> Union[str, None]:
"""
Returns the name of the database that generated the notification.
"""
return self._db_name
@property
def msgid(self) -> bytes:
def msgid(self) -> Union[bytes, None]:
"""
Returns the message id of the AQ message that generated the notification.
Returns the message id of the AQ message that generated the
notification.
"""
return self._msgid
@property
def queries(self) -> list:
def queries(self) -> List["MessageQuery"]:
"""
Returns a list of message query objects that give information about
query result sets changed for this notification. This attribute will be
@ -212,7 +213,7 @@ class Message:
return self._queries
@property
def queue_name(self) -> str:
def queue_name(self) -> Union[str, None]:
"""
Returns the name of the queue which generated the notification. It will
only be populated if the subscription was created with the namespace
@ -221,7 +222,7 @@ class Message:
return self._queue_name
@property
def queueName(self) -> str:
def queueName(self) -> Union[str, None]:
"""
Deprecated. Use property queue_name instead.
"""
@ -247,7 +248,7 @@ class Message:
return self._subscription
@property
def tables(self) -> list:
def tables(self) -> List["MessageTable"]:
"""
Returns a list of message table objects that give information about the
tables changed for this notification. This attribute will be an empty
@ -257,14 +258,14 @@ class Message:
return self._tables
@property
def txid(self) -> bytes:
def txid(self) -> Union[bytes, None]:
"""
Returns the id of the transaction that generated the notification.
"""
return self._txid
@property
def type(self) -> str:
def type(self) -> int:
"""
Returns the type of message that has been sent.
"""
@ -297,7 +298,7 @@ class MessageQuery:
return self._operation
@property
def tables(self) -> list:
def tables(self) -> List["MessageTable"]:
"""
Returns a list of message table objects that give information about the
table changes that caused the query result set to change for this
@ -320,7 +321,7 @@ class MessageRow:
return self._operation
@property
def rowid(self) -> str:
def rowid(self) -> Union[str, None]:
"""
Returns the rowid of the row that was changed.
"""
@ -335,7 +336,7 @@ class MessageTable:
self._rows = []
@property
def name(self) -> str:
def name(self) -> Union[str, None]:
"""
Returns the name of the table that was changed.
"""
@ -349,7 +350,7 @@ class MessageTable:
return self._operation
@property
def rows(self) -> list:
def rows(self) -> List["MessageRow"]:
"""
Returns a list of message row objects that give information about the
rows changed on the table. This value is only filled in if the qos

View File

@ -30,4 +30,4 @@
# file doc/src/conf.py both reference this file directly.
#------------------------------------------------------------------------------
__version__ = "1.1.0"
__version__ = "1.1.1"

View File

@ -194,5 +194,10 @@ class TestCase(test_env.BaseTestCase):
"1528 - test oracledb.ROWID pickling"
self.__test_pickle(oracledb.ROWID)
def test_1529_DB_TYPE_UROWID(self):
"1529 - test oracledb.DB_TYPE_UROWID comparisons and pickling"
self.__test_compare(oracledb.DB_TYPE_UROWID, oracledb.ROWID)
self.__test_pickle(oracledb.DB_TYPE_UROWID)
if __name__ == "__main__":
test_env.run_test_cases()

View File

@ -27,6 +27,7 @@
"""
import datetime
import unittest
import oracledb
import test_env
@ -84,5 +85,90 @@ class TestCase(test_env.BaseTestCase):
val=rowid)
self.assertEqual(self.cursor.fetchall(), [(int_val,)])
def test_2904_select_rowids_as_rowids(self):
"2904 - test selecting regular rowids stored in a rowid column"
self.cursor.execute("truncate table TestRowids")
self.cursor.execute("""
insert into TestRowids (IntCol, RowidCol)
select IntCol, rowid from TestNumbers""")
self.connection.commit()
self.cursor.execute("select IntCol, RowidCol from TestRowids")
for int_val, rowid in self.cursor.fetchall():
self.cursor.execute("""
select IntCol
from TestNumbers
where rowid = :val""",
val=rowid)
self.assertEqual(self.cursor.fetchall(), [(int_val,)])
def test_2905_test_bind_and_insert_rowid(self):
"2905 - binding and inserting a rowid"
self.cursor.execute("truncate table TestRowids")
insert_data = [
(1, "String #1"), (2, "String #2"),
(3, "String #3"),(4, "String #4")
]
self.cursor.execute("truncate table TestTempTable")
sql = "insert into TestTempTable (IntCol, StringCol1) values (:1, :2)"
self.cursor.executemany(sql, insert_data)
self.connection.commit()
ridvar = self.cursor.var(oracledb.ROWID)
self.cursor.execute("""
begin
select rowid into :rid from TestTempTable
where IntCol = 3;
end;""",
rid=ridvar)
self.cursor.setinputsizes(r1=oracledb.ROWID)
self.cursor.execute("""
insert into TestRowids (IntCol, RowidCol)
values(1, :r1)""",
r1=ridvar)
self.connection.commit()
self.cursor.execute("select IntCol, RowidCol from TestRowids")
int_val, rowid = self.cursor.fetchone()
self.cursor.execute("""
select IntCol, StringCol1 from TestTempTable
where rowid = :val""",
val=rowid)
self.assertEqual(self.cursor.fetchone(), (3, "String #3"))
@unittest.skipIf(not test_env.get_is_thin(),
"thick mode doesn't support DB_TYPE_UROWID")
def test_2906_test_bind_and_insert_rowid_as_urowid(self):
"2906 - binding and inserting a rowid as urowid"
self.cursor.execute("truncate table TestRowids")
insert_data = [
(1, "String #1", datetime.datetime(2017, 4, 4)),
(2, "String #2", datetime.datetime(2017, 4, 5)),
(3, "String #3", datetime.datetime(2017, 4, 6)),
(4, "A" * 250, datetime.datetime(2017, 4, 7))
]
self.cursor.execute("truncate table TestUniversalRowids")
sql = "insert into TestUniversalRowids values (:1, :2, :3)"
self.cursor.executemany(sql, insert_data)
self.connection.commit()
ridvar = self.cursor.var(oracledb.DB_TYPE_UROWID)
self.cursor.execute("""
begin
select rowid into :rid from TestUniversalRowids
where IntCol = 3;
end;""",
rid=ridvar)
self.cursor.setinputsizes(r1=oracledb.DB_TYPE_UROWID)
self.cursor.execute("""
insert into TestRowids (IntCol, UrowidCol)
values(1, :r1)""",
r1=ridvar)
self.connection.commit()
self.cursor.execute("select IntCol, UrowidCol from TestRowids")
int_val, rowid = self.cursor.fetchone()
self.cursor.execute("""
select IntCol, StringCol, DateCol from TestUniversalRowids
where rowid = :val""",
val=rowid)
self.assertEqual(self.cursor.fetchone(),
(3, "String #3", datetime.datetime(2017, 4, 6)))
if __name__ == "__main__":
test_env.run_test_cases()

View File

@ -446,5 +446,46 @@ class TestCase(test_env.BaseTestCase):
result, = cursor.fetchone()
self.assertTrue(hint in result.read())
def test_3420_save_and_get(self):
"3420 - test saveAndGet"
soda_db = self.get_soda_database(minclient=(19, 9))
coll = soda_db.createCollection("TestSodaSaveAndGet")
coll.find().remove()
values_to_save = [
dict(name="John", age=50),
soda_db.createDocument(dict(name="Mark", age=45)),
soda_db.createDocument(dict(name="Jill", age=32))
]
inserted_keys = []
for value in values_to_save:
doc = coll.saveAndGet(value)
inserted_keys.append(doc.key)
fetched_docs = coll.find().getDocuments()
self.connection.commit()
self.assertEqual(coll.find().count(), len(values_to_save))
for key, fetched_doc in zip(inserted_keys, fetched_docs):
doc = coll.find().key(key).getOne()
self.assertEqual(doc.getContent(), fetched_doc.getContent())
coll.drop()
def test_3421_insert_many_and_get(self):
"3421 - test insert many and get"
soda_db = self.get_soda_database(minclient=(18, 5))
coll = soda_db.createCollection("TestInsertManyAndGet")
values_to_insert = [
dict(name="George", age=25),
soda_db.createDocument(dict(name="Lucas", age=47))
]
docs = coll.insertManyAndGet(values_to_insert)
inserted_keys = [i.key for i in docs]
self.connection.commit()
self.assertEqual(coll.find().count(), len(values_to_insert))
for key, expected_doc in zip(inserted_keys, values_to_insert):
if isinstance(expected_doc, dict):
expected_doc = soda_db.createDocument(expected_doc)
doc = coll.find().key(key).getOne()
self.assertEqual(doc.getContent(), expected_doc.getContent())
coll.drop()
if __name__ == "__main__":
test_env.run_test_cases()

View File

@ -395,5 +395,12 @@ class TestCase(test_env.BaseTestCase):
results = self.cursor.fetchall()
self.assertEqual(results, [(None, None, None, None, None, None, None)])
@unittest.skipIf(not test_env.get_is_thin(),
"thick mode doesn't support DB_TYPE_UROWID")
def test_3725_DB_TYPE_UROWID(self):
"3725 - setting values on variables of type DB_TYPE_UROWID"
self._test_negative_set_and_get(oracledb.DB_TYPE_UROWID, 12345)
self._test_negative_set_and_get(oracledb.DB_TYPE_UROWID, "523lkhlf")
if __name__ == "__main__":
test_env.run_test_cases()

View File

@ -739,6 +739,7 @@ class TestCase(test_env.BaseTestCase):
admin_cursor.execute(sql)
self.assertRaisesRegex(oracledb.DatabaseError, "^DPY-4011:",
cursor.execute, "select user from dual")
self.assertFalse(conn.is_healthy())
def test_4359_kill_conn_in_context_manager(self):
"4359 - kill connection in cursor context manager"

View File

@ -32,7 +32,7 @@
#------------------------------------------------------------------------------
import functools
from typing import Type, Union, Callable
from typing import Union, Callable
import oracledb
@ -88,7 +88,7 @@ class ConnectParams:
#{{ params_properties }}
def copy(self) -> Type["ConnectParams"]:
def copy(self) -> "ConnectParams":
"""
Creates a copy of the parameters and returns it.
"""

View File

@ -62,7 +62,7 @@ class Connection:
def __init__(self,
dsn: str=None, *,
pool: Type["pool_module.ConnectionPool"]=None,
pool: "pool_module.ConnectionPool"=None,
params: ConnectParams=None,
**kwargs) -> None:
"""
@ -97,7 +97,8 @@ class Connection:
self._impl = None
# determine if thin mode is being used
thin = driver_mode.check_and_return_mode()
with driver_mode.get_manager() as mode_mgr:
thin = mode_mgr.thin
# determine which connection parameters to use
if params is None:
@ -500,7 +501,7 @@ class Connection:
This function performs a local check. To fully check a connection's
health, use ping() which performs a round-trip to the database.
"""
return self._impl.get_is_healthy()
return self._impl is not None and self._impl.get_is_healthy()
@property
def ltxid(self) -> bytes:
@ -601,7 +602,7 @@ class Connection:
"""
return self.tpc_prepare()
def queue(self, name: str, payload_type: [DbObjectType, str]=None, *,
def queue(self, name: str, payload_type: Union[DbObjectType, str]=None, *,
payloadType: DbObjectType=None) -> Queue:
"""
Creates and returns a queue which is used to enqueue and dequeue
@ -999,7 +1000,7 @@ def _connection_factory(f):
"""
@functools.wraps(f)
def connect(dsn: str=None, *,
pool: Type["pool_module.ConnectionPool"]=None,
pool: "pool_module.ConnectionPool"=None,
conn_class: Type[Connection]=Connection,
params: ConnectParams=None,
**kwargs) -> Connection:
@ -1012,7 +1013,7 @@ def _connection_factory(f):
@_connection_factory
def connect(dsn: str=None, *,
pool: Type["pool_module.ConnectionPool"]=None,
pool: "pool_module.ConnectionPool"=None,
conn_class: Type[Connection]=Connection,
params: ConnectParams=None,
#{{ args_with_defaults }}

View File

@ -86,7 +86,8 @@ class ConnectionPool:
params_impl.set(kwargs)
self._connection_type = \
params_impl.connectiontype or connection_module.Connection
thin = driver_mode.check_and_return_mode()
with driver_mode.get_manager() as mode_mgr:
thin = mode_mgr.thin
if dsn is not None:
dsn = params_impl.parse_dsn(dsn, thin)
if dsn is None:
@ -118,7 +119,7 @@ class ConnectionPool:
tag: str=None,
matchanytag: bool=False,
shardingkey: list=None,
supershardingkey: list=None) -> Type["connection_module.Connection"]:
supershardingkey: list=None) -> "connection_module.Connection":
"""
Acquire a connection from the pool and return it.
@ -175,7 +176,7 @@ class ConnectionPool:
self._impl.close(force)
self._impl = None
def drop(self, connection: Type["connection_module.Connection"]) -> None:
def drop(self, connection: "connection_module.Connection") -> None:
"""
Drop the connection from the pool, which is useful if the connection is
no longer usable (such as when the database session is killed).
@ -320,7 +321,7 @@ class ConnectionPool:
def ping_interval(self, value: int) -> None:
self._impl.set_ping_interval(value)
def release(self, connection: Type["connection_module.Connection"],
def release(self, connection: "connection_module.Connection",
tag: str=None) -> None:
"""
Release the connection back to the pool now, rather than whenever

View File

@ -66,7 +66,7 @@ class PoolParams(ConnectParams):
#{{ params_properties }}
def copy(self) -> Type["PoolParams"]:
def copy(self) -> "PoolParams":
"""
Creates a copy of the parameters and returns it.
"""