Skip to content

Commit 5a14a77

Browse files
authored
Merge pull request #58 from ydb-platform/move_dbapi_to_ext_repo
Update DBAPI to QueryService
2 parents 05d9299 + 98a5f76 commit 5a14a77

File tree

20 files changed

+229
-1385
lines changed

20 files changed

+229
-1385
lines changed

.github/workflows/style.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
strategy:
1111
max-parallel: 4
1212
matrix:
13-
python-version: [3.8]
13+
python-version: [3.9]
1414
environment: [style, black]
1515

1616
steps:

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ jobs:
1818
fail-fast: false
1919
max-parallel: 4
2020
matrix:
21-
python-version: [3.8]
21+
python-version: [3.9]
2222
environment: [test]
23-
folder: [ydb_sqlalchemy, test, test_dbapi]
23+
folder: [ydb_sqlalchemy, test]
2424

2525
steps:
2626
- uses: actions/checkout@v1

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ $ tox -e test-all
4646

4747
Run specific test:
4848
```bash
49-
$ tox -e test -- test_dbapi/test_dbapi.py
49+
$ tox -e test -- test/test_core.py
5050
```
5151

5252
Check code style:

docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
version: "3.3"
22
services:
33
ydb:
4-
image: cr.yandex/yc/yandex-docker-local-ydb:trunk
4+
image: ydbplatform/local-ydb:trunk
55
restart: always
66
ports:
77
- "2136:2136"
88
hostname: localhost
99
environment:
1010
- YDB_USE_IN_MEMORY_PDISKS=true
11+
- YDB_ENABLE_COLUMN_TABLES=true

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
sqlalchemy >= 2.0.7, < 3.0.0
2-
ydb >= 3.11.3
2+
ydb >= 3.18.8
3+
ydb-dbapi >= 0.1.1

test-requirements.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
sqlalchemy==2.0.7
2-
ydb==3.11.3
1+
pyyaml==5.3.1
2+
greenlet
33

4+
sqlalchemy==2.0.7
5+
ydb >= 3.18.8
6+
ydb-dbapi >= 0.1.1
47
requests<2.29
58
pytest==7.2.2
69
docker==6.0.1

test/test_core.py

Lines changed: 41 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,18 @@ def test_sa_text(self, connection):
3333
SELECT x, y FROM AS_TABLE(:data)
3434
"""
3535
),
36-
[{"data": [{"x": 2, "y": 1}, {"x": 3, "y": 2}]}],
36+
[
37+
{
38+
"data": ydb.TypedValue(
39+
[{"x": 2, "y": 1}, {"x": 3, "y": 2}],
40+
ydb.ListType(
41+
ydb.StructType()
42+
.add_member("x", ydb.PrimitiveType.Int64)
43+
.add_member("y", ydb.PrimitiveType.Int64)
44+
),
45+
)
46+
}
47+
],
3748
)
3849
assert set(rs.fetchall()) == {(2, 1), (3, 2)}
3950

@@ -454,85 +465,6 @@ def test_several_keys(self, connection, metadata):
454465
assert desc.partitioning_settings.max_partitions_count == 5
455466

456467

457-
class TestScanQuery(TablesTest):
458-
__backend__ = True
459-
460-
@classmethod
461-
def define_tables(cls, metadata: sa.MetaData):
462-
Table(
463-
"test",
464-
metadata,
465-
Column("id", Integer, primary_key=True),
466-
)
467-
468-
@classmethod
469-
def insert_data(cls, connection: sa.Connection):
470-
table = cls.tables.test
471-
for i in range(50):
472-
connection.execute(ydb_sa.upsert(table).values([{"id": i * 1000 + j} for j in range(1000)]))
473-
474-
def test_characteristic(self):
475-
engine = self.bind.execution_options()
476-
477-
with engine.connect() as connection:
478-
default_options = connection.get_execution_options()
479-
480-
with engine.connect() as connection:
481-
connection.execution_options(ydb_scan_query=True)
482-
options_after_set = connection.get_execution_options()
483-
484-
with engine.connect() as connection:
485-
options_after_reset = connection.get_execution_options()
486-
487-
assert "ydb_scan_query" not in default_options
488-
assert options_after_set["ydb_scan_query"]
489-
assert "ydb_scan_query" not in options_after_reset
490-
491-
def test_fetchmany(self, connection_no_trans: sa.Connection):
492-
table = self.tables.test
493-
stmt = sa.select(table).where(table.c.id % 2 == 0)
494-
495-
connection_no_trans.execution_options(ydb_scan_query=True)
496-
cursor = connection_no_trans.execute(stmt)
497-
498-
assert cursor.cursor.use_scan_query
499-
result = cursor.fetchmany(1000) # fetches only the first 5k rows
500-
assert result == [(i,) for i in range(2000) if i % 2 == 0]
501-
502-
def test_fetchall(self, connection_no_trans: sa.Connection):
503-
table = self.tables.test
504-
stmt = sa.select(table).where(table.c.id % 2 == 0)
505-
506-
connection_no_trans.execution_options(ydb_scan_query=True)
507-
cursor = connection_no_trans.execute(stmt)
508-
509-
assert cursor.cursor.use_scan_query
510-
result = cursor.fetchall()
511-
assert result == [(i,) for i in range(50000) if i % 2 == 0]
512-
513-
def test_begin_does_nothing(self, connection_no_trans: sa.Connection):
514-
table = self.tables.test
515-
connection_no_trans.execution_options(ydb_scan_query=True)
516-
517-
with connection_no_trans.begin():
518-
cursor = connection_no_trans.execute(sa.select(table))
519-
520-
assert cursor.cursor.use_scan_query
521-
assert cursor.cursor.tx_context is None
522-
523-
def test_engine_option(self):
524-
table = self.tables.test
525-
engine = self.bind.execution_options(ydb_scan_query=True)
526-
527-
with engine.begin() as connection:
528-
cursor = connection.execute(sa.select(table))
529-
assert cursor.cursor.use_scan_query
530-
531-
with engine.begin() as connection:
532-
cursor = connection.execute(sa.select(table))
533-
assert cursor.cursor.use_scan_query
534-
535-
536468
class TestTransaction(TablesTest):
537469
__backend__ = True
538470

@@ -585,11 +517,11 @@ def test_interactive_transaction(
585517

586518
connection_no_trans.execution_options(isolation_level=isolation_level)
587519
with connection_no_trans.begin():
588-
tx_id = dbapi_connection.tx_context.tx_id
589-
assert tx_id is not None
590520
cursor1 = connection_no_trans.execute(sa.select(table))
521+
tx_id = dbapi_connection._tx_context.tx_id
522+
assert tx_id is not None
591523
cursor2 = connection_no_trans.execute(sa.select(table))
592-
assert dbapi_connection.tx_context.tx_id == tx_id
524+
assert dbapi_connection._tx_context.tx_id == tx_id
593525

594526
assert set(cursor1.fetchall()) == {(5,), (6,)}
595527
assert set(cursor2.fetchall()) == {(5,), (6,)}
@@ -614,10 +546,10 @@ def test_not_interactive_transaction(
614546

615547
connection_no_trans.execution_options(isolation_level=isolation_level)
616548
with connection_no_trans.begin():
617-
assert dbapi_connection.tx_context is None
549+
assert dbapi_connection._tx_context is None
618550
cursor1 = connection_no_trans.execute(sa.select(table))
619551
cursor2 = connection_no_trans.execute(sa.select(table))
620-
assert dbapi_connection.tx_context is None
552+
assert dbapi_connection._tx_context is None
621553

622554
assert set(cursor1.fetchall()) == {(7,), (8,)}
623555
assert set(cursor2.fetchall()) == {(7,), (8,)}
@@ -631,14 +563,14 @@ class IsolationSettings(NamedTuple):
631563
interactive: bool
632564

633565
YDB_ISOLATION_SETTINGS_MAP = {
634-
IsolationLevel.AUTOCOMMIT: IsolationSettings(ydb.SerializableReadWrite().name, False),
635-
IsolationLevel.SERIALIZABLE: IsolationSettings(ydb.SerializableReadWrite().name, True),
636-
IsolationLevel.ONLINE_READONLY: IsolationSettings(ydb.OnlineReadOnly().name, False),
566+
IsolationLevel.AUTOCOMMIT: IsolationSettings(ydb.QuerySerializableReadWrite().name, False),
567+
IsolationLevel.SERIALIZABLE: IsolationSettings(ydb.QuerySerializableReadWrite().name, True),
568+
IsolationLevel.ONLINE_READONLY: IsolationSettings(ydb.QueryOnlineReadOnly().name, False),
637569
IsolationLevel.ONLINE_READONLY_INCONSISTENT: IsolationSettings(
638-
ydb.OnlineReadOnly().with_allow_inconsistent_reads().name, False
570+
ydb.QueryOnlineReadOnly().with_allow_inconsistent_reads().name, False
639571
),
640-
IsolationLevel.STALE_READONLY: IsolationSettings(ydb.StaleReadOnly().name, False),
641-
IsolationLevel.SNAPSHOT_READONLY: IsolationSettings(ydb.SnapshotReadOnly().name, True),
572+
IsolationLevel.STALE_READONLY: IsolationSettings(ydb.QueryStaleReadOnly().name, False),
573+
IsolationLevel.SNAPSHOT_READONLY: IsolationSettings(ydb.QuerySnapshotReadOnly().name, True),
642574
}
643575

644576
def test_connection_set(self, connection_no_trans: sa.Connection):
@@ -647,13 +579,13 @@ def test_connection_set(self, connection_no_trans: sa.Connection):
647579
for sa_isolation_level, ydb_isolation_settings in self.YDB_ISOLATION_SETTINGS_MAP.items():
648580
connection_no_trans.execution_options(isolation_level=sa_isolation_level)
649581
with connection_no_trans.begin():
650-
assert dbapi_connection.tx_mode.name == ydb_isolation_settings[0]
582+
assert dbapi_connection._tx_mode.name == ydb_isolation_settings[0]
651583
assert dbapi_connection.interactive_transaction is ydb_isolation_settings[1]
652584
if dbapi_connection.interactive_transaction:
653-
assert dbapi_connection.tx_context is not None
654-
assert dbapi_connection.tx_context.tx_id is not None
585+
assert dbapi_connection._tx_context is not None
586+
# assert dbapi_connection._tx_context.tx_id is not None
655587
else:
656-
assert dbapi_connection.tx_context is None
588+
assert dbapi_connection._tx_context is None
657589

658590

659591
class TestEngine(TestBase):
@@ -674,7 +606,7 @@ def ydb_driver(self):
674606

675607
@pytest.fixture(scope="class")
676608
def ydb_pool(self, ydb_driver):
677-
session_pool = ydb.SessionPool(ydb_driver, size=5, workers_threads_count=1)
609+
session_pool = ydb.QuerySessionPool(ydb_driver, size=5)
678610

679611
try:
680612
yield session_pool
@@ -689,8 +621,8 @@ def test_sa_queue_pool_with_ydb_shared_session_pool(self, ydb_driver, ydb_pool):
689621
dbapi_conn1: dbapi.Connection = conn1.connection.dbapi_connection
690622
dbapi_conn2: dbapi.Connection = conn2.connection.dbapi_connection
691623

692-
assert dbapi_conn1.session_pool is dbapi_conn2.session_pool
693-
assert dbapi_conn1.driver is dbapi_conn2.driver
624+
assert dbapi_conn1._session_pool is dbapi_conn2._session_pool
625+
assert dbapi_conn1._driver is dbapi_conn2._driver
694626

695627
engine1.dispose()
696628
engine2.dispose()
@@ -704,8 +636,8 @@ def test_sa_null_pool_with_ydb_shared_session_pool(self, ydb_driver, ydb_pool):
704636
dbapi_conn1: dbapi.Connection = conn1.connection.dbapi_connection
705637
dbapi_conn2: dbapi.Connection = conn2.connection.dbapi_connection
706638

707-
assert dbapi_conn1.session_pool is dbapi_conn2.session_pool
708-
assert dbapi_conn1.driver is dbapi_conn2.driver
639+
assert dbapi_conn1._session_pool is dbapi_conn2._session_pool
640+
assert dbapi_conn1._driver is dbapi_conn2._driver
709641

710642
engine1.dispose()
711643
engine2.dispose()
@@ -726,14 +658,15 @@ def ydb_driver(self):
726658
finally:
727659
loop.run_until_complete(driver.stop())
728660

661+
@pytest.mark.asyncio
729662
@pytest.fixture(scope="class")
730663
def ydb_pool(self, ydb_driver):
731-
session_pool = ydb.aio.SessionPool(ydb_driver, size=5)
664+
loop = asyncio.get_event_loop()
665+
session_pool = ydb.aio.QuerySessionPool(ydb_driver, size=5, loop=loop)
732666

733667
try:
734668
yield session_pool
735669
finally:
736-
loop = asyncio.get_event_loop()
737670
loop.run_until_complete(session_pool.stop())
738671

739672

@@ -742,9 +675,9 @@ class TestCredentials(TestBase):
742675
__only_on__ = "yql+ydb"
743676

744677
@pytest.fixture(scope="class")
745-
def table_client_settings(self):
678+
def query_client_settings(self):
746679
yield (
747-
ydb.TableClientSettings()
680+
ydb.QueryClientSettings()
748681
.with_native_date_in_result_sets(True)
749682
.with_native_datetime_in_result_sets(True)
750683
.with_native_timestamp_in_result_sets(True)
@@ -753,18 +686,18 @@ def table_client_settings(self):
753686
)
754687

755688
@pytest.fixture(scope="class")
756-
def driver_config_for_credentials(self, table_client_settings):
689+
def driver_config_for_credentials(self, query_client_settings):
757690
url = config.db_url
758691
endpoint = f"grpc://{url.host}:{url.port}"
759692
database = url.database
760693

761694
yield ydb.DriverConfig(
762695
endpoint=endpoint,
763696
database=database,
764-
table_client_settings=table_client_settings,
697+
query_client_settings=query_client_settings,
765698
)
766699

767-
def test_ydb_credentials_good(self, table_client_settings, driver_config_for_credentials):
700+
def test_ydb_credentials_good(self, query_client_settings, driver_config_for_credentials):
768701
credentials_good = ydb.StaticCredentials(
769702
driver_config=driver_config_for_credentials,
770703
user="root",
@@ -775,7 +708,7 @@ def test_ydb_credentials_good(self, table_client_settings, driver_config_for_cre
775708
result = conn.execute(sa.text("SELECT 1 as value"))
776709
assert result.fetchone()
777710

778-
def test_ydb_credentials_bad(self, table_client_settings, driver_config_for_credentials):
711+
def test_ydb_credentials_bad(self, query_client_settings, driver_config_for_credentials):
779712
credentials_bad = ydb.StaticCredentials(
780713
driver_config=driver_config_for_credentials,
781714
user="root",

test/test_suite.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,18 @@ def test_struct_type_bind_variable(self, connection):
506506

507507
eq_(connection.scalar(stmt, {"struct": {"id": 1}}), 1)
508508

509+
def test_struct_type_bind_variable_text(self, connection):
510+
rs = connection.execute(
511+
sa.text("SELECT :struct.x + :struct.y").bindparams(
512+
sa.bindparam(
513+
key="struct",
514+
type_=ydb_sa_types.StructType({"x": sa.Integer, "y": sa.Integer}),
515+
value={"x": 1, "y": 2},
516+
)
517+
)
518+
)
519+
assert rs.scalar() == 3
520+
509521
def test_from_as_table(self, connection):
510522
table = self.tables.container_types_test
511523

test_dbapi/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)