@@ -788,3 +788,179 @@ def test_insert_in_name_and_field(self, connection):
788788 row = connection .execute (sa .select (tb ).where (tb .c .id == 2 )).fetchone ()
789789
790790 assert row == (2 , "INSERT is my favourite operation" )
791+
792+
793+ class TestSecondaryIndex (TestBase ):
794+ __backend__ = True
795+
796+ def test_column_indexes (self , connection : sa .Connection , metadata : sa .MetaData ):
797+ table = Table (
798+ "test_column_indexes/table" ,
799+ metadata ,
800+ sa .Column ("id" , sa .Integer , primary_key = True ),
801+ sa .Column ("index_col1" , sa .Integer , index = True ),
802+ sa .Column ("index_col2" , sa .Integer , index = True ),
803+ )
804+ table .create (connection )
805+
806+ table_desc : ydb .TableDescription = connection .connection .driver_connection .describe (table .name )
807+ indexes : list [ydb .TableIndex ] = table_desc .indexes
808+ assert len (indexes ) == 2
809+ indexes_map = {idx .name : idx for idx in indexes }
810+
811+ assert "ix_test_column_indexes_table_index_col1" in indexes_map
812+ index1 = indexes_map ["ix_test_column_indexes_table_index_col1" ]
813+ assert index1 .index_columns == ["index_col1" ]
814+
815+ assert "ix_test_column_indexes_table_index_col2" in indexes_map
816+ index1 = indexes_map ["ix_test_column_indexes_table_index_col2" ]
817+ assert index1 .index_columns == ["index_col2" ]
818+
819+ def test_async_index (self , connection : sa .Connection , metadata : sa .MetaData ):
820+ table = Table (
821+ "test_async_index/table" ,
822+ metadata ,
823+ sa .Column ("id" , sa .Integer , primary_key = True ),
824+ sa .Column ("index_col1" , sa .Integer ),
825+ sa .Column ("index_col2" , sa .Integer ),
826+ sa .Index ("test_async_index" , "index_col1" , "index_col2" , ydb_async = True ),
827+ )
828+ table .create (connection )
829+
830+ table_desc : ydb .TableDescription = connection .connection .driver_connection .describe (table .name )
831+ indexes : list [ydb .TableIndex ] = table_desc .indexes
832+ assert len (indexes ) == 1
833+ index = indexes [0 ]
834+ assert index .name == "test_async_index"
835+ assert set (index .index_columns ) == {"index_col1" , "index_col2" }
836+ # TODO: Check type after https://github.com/ydb-platform/ydb-python-sdk/issues/351
837+
838+ def test_cover_index (self , connection : sa .Connection , metadata : sa .MetaData ):
839+ table = Table (
840+ "test_cover_index/table" ,
841+ metadata ,
842+ sa .Column ("id" , sa .Integer , primary_key = True ),
843+ sa .Column ("index_col1" , sa .Integer ),
844+ sa .Column ("index_col2" , sa .Integer ),
845+ sa .Index ("test_cover_index" , "index_col1" , ydb_cover = ["index_col2" ]),
846+ )
847+ table .create (connection )
848+
849+ table_desc : ydb .TableDescription = connection .connection .driver_connection .describe (table .name )
850+ indexes : list [ydb .TableIndex ] = table_desc .indexes
851+ assert len (indexes ) == 1
852+ index = indexes [0 ]
853+ assert index .name == "test_cover_index"
854+ assert set (index .index_columns ) == {"index_col1" }
855+ # TODO: Check covered columns after https://github.com/ydb-platform/ydb-python-sdk/issues/409
856+
857+ def test_indexes_reflection (self , connection : sa .Connection , metadata : sa .MetaData ):
858+ table = Table (
859+ "test_indexes_reflection/table" ,
860+ metadata ,
861+ sa .Column ("id" , sa .Integer , primary_key = True ),
862+ sa .Column ("index_col1" , sa .Integer , index = True ),
863+ sa .Column ("index_col2" , sa .Integer ),
864+ sa .Index ("test_index" , "index_col1" , "index_col2" ),
865+ sa .Index ("test_async_index" , "index_col1" , "index_col2" , ydb_async = True ),
866+ sa .Index ("test_cover_index" , "index_col1" , ydb_cover = ["index_col2" ]),
867+ sa .Index ("test_async_cover_index" , "index_col1" , ydb_async = True , ydb_cover = ["index_col2" ]),
868+ )
869+ table .create (connection )
870+
871+ indexes = sa .inspect (connection ).get_indexes (table .name )
872+ assert len (indexes ) == 5
873+ indexes_names = {idx ["name" ]: set (idx ["column_names" ]) for idx in indexes }
874+
875+ assert indexes_names == {
876+ "ix_test_indexes_reflection_table_index_col1" : {"index_col1" },
877+ "test_index" : {"index_col1" , "index_col2" },
878+ "test_async_index" : {"index_col1" , "index_col2" },
879+ "test_cover_index" : {"index_col1" },
880+ "test_async_cover_index" : {"index_col1" },
881+ }
882+
883+ def test_index_simple_usage (self , connection : sa .Connection , metadata : sa .MetaData ):
884+ persons = Table (
885+ "test_index_simple_usage/persons" ,
886+ metadata ,
887+ sa .Column ("id" , sa .Integer (), primary_key = True ),
888+ sa .Column ("tax_number" , sa .Integer ()),
889+ sa .Column ("full_name" , sa .Unicode ()),
890+ sa .Index ("ix_tax_number_cover_full_name" , "tax_number" , ydb_cover = ["full_name" ]),
891+ )
892+ persons .create (connection )
893+ connection .execute (
894+ sa .insert (persons ).values (
895+ [
896+ {"id" : 1 , "tax_number" : 333333 , "full_name" : "John Connor" },
897+ {"id" : 2 , "tax_number" : 444444 , "full_name" : "Sarah Connor" },
898+ ]
899+ )
900+ )
901+
902+ # Because of this bug https://github.com/ydb-platform/ydb/issues/3510,
903+ # it is not possible to use full qualified name of columns with VIEW clause
904+ select_stmt = (
905+ sa .select (sa .column (persons .c .full_name .name ))
906+ .select_from (persons )
907+ .with_hint (persons , "VIEW `ix_tax_number_cover_full_name`" )
908+ .where (sa .column (persons .c .tax_number .name ) == 444444 )
909+ )
910+
911+ cursor = connection .execute (select_stmt )
912+ assert cursor .scalar_one () == "Sarah Connor"
913+
914+ def test_index_with_join_usage (self , connection : sa .Connection , metadata : sa .MetaData ):
915+ persons = Table (
916+ "test_index_with_join_usage/persons" ,
917+ metadata ,
918+ sa .Column ("id" , sa .Integer (), primary_key = True ),
919+ sa .Column ("tax_number" , sa .Integer ()),
920+ sa .Column ("full_name" , sa .Unicode ()),
921+ sa .Index ("ix_tax_number_cover_full_name" , "tax_number" , ydb_cover = ["full_name" ]),
922+ )
923+ persons .create (connection )
924+ connection .execute (
925+ sa .insert (persons ).values (
926+ [
927+ {"id" : 1 , "tax_number" : 333333 , "full_name" : "John Connor" },
928+ {"id" : 2 , "tax_number" : 444444 , "full_name" : "Sarah Connor" },
929+ ]
930+ )
931+ )
932+ person_status = Table (
933+ "test_index_with_join_usage/person_status" ,
934+ metadata ,
935+ sa .Column ("id" , sa .Integer (), primary_key = True ),
936+ sa .Column ("status" , sa .Unicode ()),
937+ )
938+ person_status .create (connection )
939+ connection .execute (
940+ sa .insert (person_status ).values (
941+ [
942+ {"id" : 1 , "status" : "unknown" },
943+ {"id" : 2 , "status" : "wanted" },
944+ ]
945+ )
946+ )
947+
948+ # Because of this bug https://github.com/ydb-platform/ydb/issues/3510,
949+ # it is not possible to use full qualified name of columns with VIEW clause
950+ persons_indexed = (
951+ sa .select (
952+ sa .column (persons .c .id .name ),
953+ sa .column (persons .c .full_name .name ),
954+ sa .column (persons .c .tax_number .name ),
955+ )
956+ .select_from (persons )
957+ .with_hint (persons , "VIEW `ix_tax_number_cover_full_name`" )
958+ )
959+ select_stmt = (
960+ sa .select (persons_indexed .c .full_name , person_status .c .status )
961+ .select_from (person_status .join (persons_indexed , persons_indexed .c .id == person_status .c .id ))
962+ .where (persons_indexed .c .tax_number == 444444 )
963+ )
964+
965+ cursor = connection .execute (select_stmt )
966+ assert cursor .one () == ("Sarah Connor" , "wanted" )
0 commit comments