Alembic experiments

This commit is contained in:
Jonas Linter
2025-11-18 11:04:38 +01:00
parent 10dcbae5ad
commit 5a660507d2
17 changed files with 1716 additions and 99 deletions

View File

@@ -1453713,3 +1453713,309 @@ WHERE alpinebits.conversions.pms_reservation_id = $1::VARCHAR]
2025-11-17 21:14:04 - alpine_bits_python.alpine_bits_helpers - WARNING - invalid email address: silviadallapiazza@gmail.con -> The domain name gmail.con does not exist.
2025-11-17 21:14:05 - alpine_bits_python.alpine_bits_helpers - WARNING - invalid email address: valentina_giannini92@hormail.it -> The domain name hormail.it does not exist.
2025-11-17 21:14:05 - alpine_bits_python.alpine_bits_helpers - WARNING - invalid email address: sara.trovarelli@gmail.con -> The domain name gmail.con does not exist.
2025-11-18 09:52:37 - alpine_bits_python.migrations - ERROR - Migration failed: (sqlalchemy.dialects.postgresql.asyncpg.Error) <class 'asyncpg.exceptions.DependentObjectsStillExistError'>: cannot drop table conversions because other objects depend on it
DETAIL: constraint conversion_rooms_conversion_id_fkey on table conversion_rooms depends on table conversions
HINT: Use DROP ... CASCADE to drop the dependent objects too.
[SQL: DROP TABLE IF EXISTS conversions]
(Background on this error at: https://sqlalche.me/e/20/dbapi)
Traceback (most recent call last):
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 545, in _prepare_and_execute
self._rows = deque(await prepared_stmt.fetch(*parameters))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/asyncpg/prepared_stmt.py", line 176, in fetch
data = await self.__bind_execute(args, 0, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/asyncpg/prepared_stmt.py", line 267, in __bind_execute
data, status, _ = await self.__do_execute(
^^^^^^^^^^^^^^^^^^^^^^^^
lambda protocol: protocol.bind_execute(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self._state, args, '', limit, True, timeout))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/asyncpg/prepared_stmt.py", line 256, in __do_execute
return await executor(protocol)
^^^^^^^^^^^^^^^^^^^^^^^^
File "asyncpg/protocol/protocol.pyx", line 206, in bind_execute
asyncpg.exceptions.DependentObjectsStillExistError: cannot drop table conversions because other objects depend on it
DETAIL: constraint conversion_rooms_conversion_id_fkey on table conversion_rooms depends on table conversions
HINT: Use DROP ... CASCADE to drop the dependent objects too.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
self.dialect.do_execute(
~~~~~~~~~~~~~~~~~~~~~~~^
cursor, str_statement, effective_parameters, context
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/default.py", line 951, in do_execute
cursor.execute(statement, parameters)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 580, in execute
self._adapt_connection.await_(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
self._prepare_and_execute(operation, parameters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 132, in await_only
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 196, in greenlet_spawn
value = await result
^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 558, in _prepare_and_execute
self._handle_exception(error)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 508, in _handle_exception
self._adapt_connection._handle_exception(error)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 792, in _handle_exception
raise translated_error from error
sqlalchemy.dialects.postgresql.asyncpg.AsyncAdapt_asyncpg_dbapi.Error: <class 'asyncpg.exceptions.DependentObjectsStillExistError'>: cannot drop table conversions because other objects depend on it
DETAIL: constraint conversion_rooms_conversion_id_fkey on table conversion_rooms depends on table conversions
HINT: Use DROP ... CASCADE to drop the dependent objects too.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/divusjulius/repos/alpine_bits_python_server/src/alpine_bits_python/migrations.py", line 504, in run_all_migrations
await migrate_normalize_conversions(engine)
File "/home/divusjulius/repos/alpine_bits_python_server/src/alpine_bits_python/migrations.py", line 462, in migrate_normalize_conversions
await drop_table(engine, "conversions")
File "/home/divusjulius/repos/alpine_bits_python_server/src/alpine_bits_python/migrations.py", line 383, in drop_table
await conn.execute(text(f"DROP TABLE IF EXISTS {table_name}"))
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/engine.py", line 658, in execute
result = await greenlet_spawn(
^^^^^^^^^^^^^^^^^^^^^
...<5 lines>...
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 201, in greenlet_spawn
result = context.throw(*sys.exc_info())
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1419, in execute
return meth(
self,
distilled_parameters,
execution_options or NO_OPTIONS,
)
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
return connection._execute_clauseelement(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
self, distilled_params, execution_options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1641, in _execute_clauseelement
ret = self._execute_context(
dialect,
...<8 lines>...
cache_hit=cache_hit,
)
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
return self._exec_single_context(
~~~~~~~~~~~~~~~~~~~~~~~~~^
dialect, context, statement, parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
self._handle_dbapi_exception(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
e, str_statement, effective_parameters, cursor, context
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 2355, in _handle_dbapi_exception
raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
self.dialect.do_execute(
~~~~~~~~~~~~~~~~~~~~~~~^
cursor, str_statement, effective_parameters, context
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/default.py", line 951, in do_execute
cursor.execute(statement, parameters)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 580, in execute
self._adapt_connection.await_(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
self._prepare_and_execute(operation, parameters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 132, in await_only
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 196, in greenlet_spawn
value = await result
^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 558, in _prepare_and_execute
self._handle_exception(error)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 508, in _handle_exception
self._adapt_connection._handle_exception(error)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 792, in _handle_exception
raise translated_error from error
sqlalchemy.exc.DBAPIError: (sqlalchemy.dialects.postgresql.asyncpg.Error) <class 'asyncpg.exceptions.DependentObjectsStillExistError'>: cannot drop table conversions because other objects depend on it
DETAIL: constraint conversion_rooms_conversion_id_fkey on table conversion_rooms depends on table conversions
HINT: Use DROP ... CASCADE to drop the dependent objects too.
[SQL: DROP TABLE IF EXISTS conversions]
(Background on this error at: https://sqlalche.me/e/20/dbapi)
2025-11-18 09:57:25 - alpine_bits_python.migrations - ERROR - Migration failed: (sqlalchemy.dialects.postgresql.asyncpg.Error) <class 'asyncpg.exceptions.DependentObjectsStillExistError'>: cannot drop table conversions because other objects depend on it
DETAIL: constraint conversion_rooms_conversion_id_fkey on table conversion_rooms depends on table conversions
HINT: Use DROP ... CASCADE to drop the dependent objects too.
[SQL: DROP TABLE IF EXISTS conversions]
(Background on this error at: https://sqlalche.me/e/20/dbapi)
Traceback (most recent call last):
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 545, in _prepare_and_execute
self._rows = deque(await prepared_stmt.fetch(*parameters))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/asyncpg/prepared_stmt.py", line 176, in fetch
data = await self.__bind_execute(args, 0, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/asyncpg/prepared_stmt.py", line 267, in __bind_execute
data, status, _ = await self.__do_execute(
^^^^^^^^^^^^^^^^^^^^^^^^
lambda protocol: protocol.bind_execute(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self._state, args, '', limit, True, timeout))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/asyncpg/prepared_stmt.py", line 256, in __do_execute
return await executor(protocol)
^^^^^^^^^^^^^^^^^^^^^^^^
File "asyncpg/protocol/protocol.pyx", line 206, in bind_execute
asyncpg.exceptions.DependentObjectsStillExistError: cannot drop table conversions because other objects depend on it
DETAIL: constraint conversion_rooms_conversion_id_fkey on table conversion_rooms depends on table conversions
HINT: Use DROP ... CASCADE to drop the dependent objects too.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
self.dialect.do_execute(
~~~~~~~~~~~~~~~~~~~~~~~^
cursor, str_statement, effective_parameters, context
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/default.py", line 951, in do_execute
cursor.execute(statement, parameters)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 580, in execute
self._adapt_connection.await_(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
self._prepare_and_execute(operation, parameters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 132, in await_only
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 196, in greenlet_spawn
value = await result
^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 558, in _prepare_and_execute
self._handle_exception(error)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 508, in _handle_exception
self._adapt_connection._handle_exception(error)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 792, in _handle_exception
raise translated_error from error
sqlalchemy.dialects.postgresql.asyncpg.AsyncAdapt_asyncpg_dbapi.Error: <class 'asyncpg.exceptions.DependentObjectsStillExistError'>: cannot drop table conversions because other objects depend on it
DETAIL: constraint conversion_rooms_conversion_id_fkey on table conversion_rooms depends on table conversions
HINT: Use DROP ... CASCADE to drop the dependent objects too.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/divusjulius/repos/alpine_bits_python_server/src/alpine_bits_python/migrations.py", line 504, in run_all_migrations
await migrate_normalize_conversions(engine)
File "/home/divusjulius/repos/alpine_bits_python_server/src/alpine_bits_python/migrations.py", line 462, in migrate_normalize_conversions
await drop_table(engine, "conversions")
File "/home/divusjulius/repos/alpine_bits_python_server/src/alpine_bits_python/migrations.py", line 383, in drop_table
await conn.execute(text(f"DROP TABLE IF EXISTS {table_name}"))
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/engine.py", line 658, in execute
result = await greenlet_spawn(
^^^^^^^^^^^^^^^^^^^^^
...<5 lines>...
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 201, in greenlet_spawn
result = context.throw(*sys.exc_info())
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1419, in execute
return meth(
self,
distilled_parameters,
execution_options or NO_OPTIONS,
)
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
return connection._execute_clauseelement(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
self, distilled_params, execution_options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1641, in _execute_clauseelement
ret = self._execute_context(
dialect,
...<8 lines>...
cache_hit=cache_hit,
)
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
return self._exec_single_context(
~~~~~~~~~~~~~~~~~~~~~~~~~^
dialect, context, statement, parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
self._handle_dbapi_exception(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
e, str_statement, effective_parameters, cursor, context
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 2355, in _handle_dbapi_exception
raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py", line 1967, in _exec_single_context
self.dialect.do_execute(
~~~~~~~~~~~~~~~~~~~~~~~^
cursor, str_statement, effective_parameters, context
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/engine/default.py", line 951, in do_execute
cursor.execute(statement, parameters)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 580, in execute
self._adapt_connection.await_(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
self._prepare_and_execute(operation, parameters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 132, in await_only
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py", line 196, in greenlet_spawn
value = await result
^^^^^^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 558, in _prepare_and_execute
self._handle_exception(error)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 508, in _handle_exception
self._adapt_connection._handle_exception(error)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/divusjulius/repos/alpine_bits_python_server/.venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py", line 792, in _handle_exception
raise translated_error from error
sqlalchemy.exc.DBAPIError: (sqlalchemy.dialects.postgresql.asyncpg.Error) <class 'asyncpg.exceptions.DependentObjectsStillExistError'>: cannot drop table conversions because other objects depend on it
DETAIL: constraint conversion_rooms_conversion_id_fkey on table conversion_rooms depends on table conversions
HINT: Use DROP ... CASCADE to drop the dependent objects too.
[SQL: DROP TABLE IF EXISTS conversions]
(Background on this error at: https://sqlalche.me/e/20/dbapi)