Skip to content

Commit 136c0dc

Browse files
committed
Add index creation
1 parent 94b8885 commit 136c0dc

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

test/test_core.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,3 +732,68 @@ def test_insert_in_name_and_field(self, connection):
732732
row = connection.execute(sa.select(tb).where(tb.c.id == 2)).fetchone()
733733

734734
assert row == (2, "INSERT is my favourite operation")
735+
736+
737+
class TestSecondaryIndex(TestBase):
738+
__backend__ = True
739+
740+
def test_column_indexes(self, connection: sa.Connection, metadata: sa.MetaData):
741+
table = Table(
742+
"test_column_indexes/table",
743+
metadata,
744+
sa.Column("id", sa.Integer, primary_key=True),
745+
sa.Column("index_col1", sa.Integer, index=True),
746+
sa.Column("index_col2", sa.Integer, index=True),
747+
)
748+
table.create(connection)
749+
750+
table_desc: ydb.TableDescription = connection.connection.driver_connection.describe(table.name)
751+
indexes: list[ydb.TableIndex] = table_desc.indexes
752+
assert len(indexes) == 2
753+
indexes_map = {idx.name: idx for idx in indexes}
754+
755+
assert "ix_test_column_indexes_table_index_col1" in indexes_map
756+
index1 = indexes_map["ix_test_column_indexes_table_index_col1"]
757+
assert index1.index_columns == ["index_col1"]
758+
759+
assert "ix_test_column_indexes_table_index_col2" in indexes_map
760+
index1 = indexes_map["ix_test_column_indexes_table_index_col2"]
761+
assert index1.index_columns == ["index_col2"]
762+
763+
def test_async_index(self, connection: sa.Connection, metadata: sa.MetaData):
764+
table = Table(
765+
"test_async_index/table",
766+
metadata,
767+
sa.Column("id", sa.Integer, primary_key=True),
768+
sa.Column("index_col1", sa.Integer),
769+
sa.Column("index_col2", sa.Integer),
770+
sa.Index("test_async_index", "index_col1", "index_col2", ydb_async=True),
771+
)
772+
table.create(connection)
773+
774+
table_desc: ydb.TableDescription = connection.connection.driver_connection.describe(table.name)
775+
indexes: list[ydb.TableIndex] = table_desc.indexes
776+
assert len(indexes) == 1
777+
index = indexes[0]
778+
assert index.name == "test_async_index"
779+
assert set(index.index_columns) == {"index_col1", "index_col2"}
780+
# TODO: Uncomment after fix https://github.com/ydb-platform/ydb-python-sdk/issues/351
781+
# assert index.to_pb().WhichOneof("type") == "global_async_index"
782+
783+
def test_cover_index(self, connection: sa.Connection, metadata: sa.MetaData):
784+
table = Table(
785+
"test_cover_index/table",
786+
metadata,
787+
sa.Column("id", sa.Integer, primary_key=True),
788+
sa.Column("index_col1", sa.Integer),
789+
sa.Column("index_col2", sa.Integer),
790+
sa.Index("test_cover_index", "index_col1", ydb_cover=["index_col2"]),
791+
)
792+
table.create(connection)
793+
794+
table_desc: ydb.TableDescription = connection.connection.driver_connection.describe(table.name)
795+
indexes: list[ydb.TableIndex] = table_desc.indexes
796+
assert len(indexes) == 1
797+
index = indexes[0]
798+
assert index.name == "test_cover_index"
799+
assert set(index.index_columns) == {"index_col1"}

ydb_sqlalchemy/sqlalchemy/__init__.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ def __init__(self, dialect):
6262
final_quote="`",
6363
)
6464

65+
def format_index(self, index: sa.Index) -> str:
66+
return super().format_index(index).replace("/", "_")
67+
6568

6669
class YqlTypeCompiler(StrSQLTypeCompiler):
6770
def visit_JSON(self, type_: Union[sa.JSON, types.YqlJSON], **kw):
@@ -446,6 +449,34 @@ def visit_upsert(self, insert_stmt, visited_bindparam=None, **kw):
446449

447450

448451
class YqlDDLCompiler(DDLCompiler):
452+
def visit_create_index(self, create, include_schema=False, include_table_schema=True, **kw):
453+
index: sa.Index = create.element
454+
ydb_opts = index.dialect_options.get("ydb", {})
455+
456+
self._verify_index_table(index)
457+
458+
if index.name is None:
459+
raise CompileError("ADD INDEX requires that the index have a name")
460+
461+
table_name = self.preparer.format_table(index.table)
462+
index_name = self._prepared_index_name(index)
463+
464+
text = f"ALTER TABLE {table_name} ADD INDEX {index_name} GLOBAL"
465+
466+
text += " SYNC " if not ydb_opts.get("async", False) else " ASYNC"
467+
468+
columns = {self.preparer.format_column(col) for col in index.columns.values()}
469+
cover_columns = {
470+
col if isinstance(col, str) else self.preparer.format_column(col) for col in ydb_opts.get("cover", [])
471+
}
472+
473+
text += " ON (" + ", ".join(columns) + ")"
474+
475+
if cover_columns:
476+
text += " COVER (" + ", ".join(cover_columns) + ")"
477+
478+
return text
479+
449480
def post_create_table(self, table: sa.Table) -> str:
450481
ydb_opts = table.dialect_options["ydb"]
451482
with_clause_list = self._render_table_partitioning_settings(ydb_opts)
@@ -578,6 +609,13 @@ class YqlDialect(StrCompileDialect):
578609
"partition_at_keys": None,
579610
},
580611
),
612+
(
613+
sa.schema.Index,
614+
{
615+
"async": False,
616+
"cover": [],
617+
},
618+
),
581619
]
582620

583621
@classmethod

0 commit comments

Comments
 (0)