matrixone-python-sdk 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
matrixone/async_client.py CHANGED
@@ -34,9 +34,10 @@ from datetime import datetime
34
34
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
35
35
 
36
36
  from .account import Account, User
37
+ from .async_metadata_manager import AsyncMetadataManager
37
38
  from .async_vector_index_manager import AsyncVectorManager
38
39
  from .base_client import BaseMatrixOneClient, BaseMatrixOneExecutor
39
- from .connection_hooks import ConnectionHook, ConnectionAction, create_connection_hook
40
+ from .connection_hooks import ConnectionAction, ConnectionHook, create_connection_hook
40
41
  from .exceptions import (
41
42
  AccountError,
42
43
  ConnectionError,
@@ -48,7 +49,6 @@ from .exceptions import (
48
49
  SnapshotError,
49
50
  )
50
51
  from .logger import MatrixOneLogger, create_default_logger
51
- from .async_metadata_manager import AsyncMetadataManager
52
52
  from .pitr import Pitr
53
53
  from .pubsub import Publication, Subscription
54
54
  from .snapshot import Snapshot, SnapshotLevel
@@ -1837,7 +1837,6 @@ class AsyncClient(BaseMatrixOneClient):
1837
1837
  """Cleanup when object is garbage collected"""
1838
1838
  # Don't try to cleanup in __del__ as it can cause issues with event loops
1839
1839
  # The fixture should handle proper cleanup
1840
- pass
1841
1840
 
1842
1841
  def get_sqlalchemy_engine(self) -> AsyncEngine:
1843
1842
  """
@@ -1920,6 +1919,7 @@ class AsyncClient(BaseMatrixOneClient):
1920
1919
  and other SDK components. External users should use execute() instead.
1921
1920
  """
1922
1921
  import time
1922
+
1923
1923
  from sqlalchemy import text
1924
1924
 
1925
1925
  start_time = time.time()
@@ -2554,6 +2554,97 @@ class AsyncClient(BaseMatrixOneClient):
2554
2554
  except Exception as e:
2555
2555
  raise QueryError(f"Failed to get git version: {e}")
2556
2556
 
2557
+ async def get_secondary_index_tables(self, table_name: str) -> List[str]:
2558
+ """
2559
+ Get all secondary index table names for a given table (async version).
2560
+
2561
+ Args:
2562
+ table_name: Name of the table to get secondary indexes for
2563
+
2564
+ Returns:
2565
+ List of secondary index table names
2566
+
2567
+ Examples::
2568
+
2569
+ >>> async with AsyncClient() as client:
2570
+ ... await client.connect(host='localhost', port=6001, user='root', password='111', database='test')
2571
+ ... index_tables = await client.get_secondary_index_tables('cms_all_content_chunk_info')
2572
+ ... print(index_tables)
2573
+ """
2574
+ from .index_utils import build_get_index_tables_sql
2575
+
2576
+ sql, params = build_get_index_tables_sql(table_name)
2577
+ result = await self.execute(sql, params)
2578
+ return [row[0] for row in result.fetchall()]
2579
+
2580
+ async def get_secondary_index_table_by_name(self, table_name: str, index_name: str) -> Optional[str]:
2581
+ """
2582
+ Get the physical table name of a secondary index by its index name (async version).
2583
+
2584
+ Args:
2585
+ table_name: Name of the table
2586
+ index_name: Name of the secondary index
2587
+
2588
+ Returns:
2589
+ Physical table name of the secondary index, or None if not found
2590
+
2591
+ Examples::
2592
+
2593
+ >>> async with AsyncClient() as client:
2594
+ ... await client.connect(host='localhost', port=6001, user='root', password='111', database='test')
2595
+ ... index_table = await client.get_secondary_index_table_by_name('cms_all_content_chunk_info', 'cms_id')
2596
+ ... print(index_table)
2597
+ """
2598
+ from .index_utils import build_get_index_table_by_name_sql
2599
+
2600
+ sql, params = build_get_index_table_by_name_sql(table_name, index_name)
2601
+ result = await self.execute(sql, params)
2602
+ row = result.fetchone()
2603
+ return row[0] if row else None
2604
+
2605
+ async def verify_table_index_counts(self, table_name: str) -> int:
2606
+ """
2607
+ Verify that the main table and all its secondary index tables have the same row count (async version).
2608
+
2609
+ This method compares the COUNT(*) of the main table with all its secondary index tables
2610
+ in a single SQL query for consistency. If counts don't match, raises an exception.
2611
+
2612
+ Args:
2613
+ table_name: Name of the table to verify
2614
+
2615
+ Returns:
2616
+ Row count (int) if verification succeeds
2617
+
2618
+ Raises:
2619
+ ValueError: If any secondary index table has a different count than the main table,
2620
+ with details about all counts in the error message
2621
+
2622
+ Examples::
2623
+
2624
+ >>> async with AsyncClient() as client:
2625
+ ... await client.connect(host='localhost', port=6001, user='root', password='111', database='test')
2626
+ ... count = await client.verify_table_index_counts('cms_all_content_chunk_info')
2627
+ ... print(f"✓ Verification passed, row count: {count}")
2628
+
2629
+ >>> # If verification fails:
2630
+ >>> try:
2631
+ ... count = await client.verify_table_index_counts('some_table')
2632
+ ... except ValueError as e:
2633
+ ... print(f"Verification failed: {e}")
2634
+ """
2635
+ from .index_utils import build_verify_counts_sql, process_verify_result
2636
+
2637
+ # Get all secondary index tables
2638
+ index_tables = await self.get_secondary_index_tables(table_name)
2639
+
2640
+ # Build and execute verification SQL
2641
+ sql = build_verify_counts_sql(table_name, index_tables)
2642
+ result = await self.execute(sql)
2643
+ row = result.fetchone()
2644
+
2645
+ # Process result and raise exception if verification fails
2646
+ return process_verify_result(table_name, index_tables, row)
2647
+
2557
2648
  async def __aenter__(self):
2558
2649
  return self
2559
2650
 
@@ -19,8 +19,10 @@ This module provides async metadata scanning capabilities for MatrixOne tables,
19
19
  allowing users to analyze table statistics, column information, and data distribution.
20
20
  """
21
21
 
22
- from typing import Optional, List, Dict, Any, Union
22
+ from typing import Any, Dict, List, Optional, Union
23
+
23
24
  from sqlalchemy.engine import Result
25
+
24
26
  from .metadata import BaseMetadataManager, MetadataColumn, MetadataRow
25
27
 
26
28
 
matrixone/client.py CHANGED
@@ -30,7 +30,7 @@ from sqlalchemy.engine import Engine
30
30
 
31
31
  from .account import AccountManager, TransactionAccountManager
32
32
  from .base_client import BaseMatrixOneClient, BaseMatrixOneExecutor
33
- from .connection_hooks import ConnectionHook, ConnectionAction, create_connection_hook
33
+ from .connection_hooks import ConnectionAction, ConnectionHook, create_connection_hook
34
34
  from .exceptions import ConnectionError, QueryError
35
35
  from .logger import MatrixOneLogger, create_default_logger
36
36
  from .metadata import MetadataManager, TransactionMetadataManager
@@ -1698,8 +1698,9 @@ class Client(BaseMatrixOneClient):
1698
1698
  table_name = model_class.__tablename__
1699
1699
  table = model_class.__table__
1700
1700
 
1701
+ from sqlalchemy.schema import CreateIndex, CreateTable
1702
+
1701
1703
  from .sqlalchemy_ext import FulltextIndex, VectorIndex
1702
- from sqlalchemy.schema import CreateTable, CreateIndex
1703
1704
 
1704
1705
  try:
1705
1706
  engine_context = self.get_sqlalchemy_engine().begin()
@@ -2612,6 +2613,99 @@ class Client(BaseMatrixOneClient):
2612
2613
 
2613
2614
  return self
2614
2615
 
2616
+ def get_secondary_index_tables(self, table_name: str) -> List[str]:
2617
+ """
2618
+ Get all secondary index table names for a given table.
2619
+
2620
+ Args:
2621
+ table_name: Name of the table to get secondary indexes for
2622
+
2623
+ Returns:
2624
+ List of secondary index table names
2625
+
2626
+ Examples::
2627
+
2628
+ >>> client = Client()
2629
+ >>> client.connect(host='localhost', port=6001, user='root', password='111', database='test')
2630
+ >>> index_tables = client.get_secondary_index_tables('cms_all_content_chunk_info')
2631
+ >>> print(index_tables)
2632
+ ['__mo_index_secondary_..._cms_id', '__mo_index_secondary_..._idx_all_content_length']
2633
+ """
2634
+ from .index_utils import build_get_index_tables_sql
2635
+
2636
+ sql, params = build_get_index_tables_sql(table_name)
2637
+ result = self.execute(sql, params)
2638
+ return [row[0] for row in result.fetchall()]
2639
+
2640
+ def get_secondary_index_table_by_name(self, table_name: str, index_name: str) -> Optional[str]:
2641
+ """
2642
+ Get the physical table name of a secondary index by its index name.
2643
+
2644
+ Args:
2645
+ table_name: Name of the table
2646
+ index_name: Name of the secondary index
2647
+
2648
+ Returns:
2649
+ Physical table name of the secondary index, or None if not found
2650
+
2651
+ Examples::
2652
+
2653
+ >>> client = Client()
2654
+ >>> client.connect(host='localhost', port=6001, user='root', password='111', database='test')
2655
+ >>> index_table = client.get_secondary_index_table_by_name('cms_all_content_chunk_info', 'cms_id')
2656
+ >>> print(index_table)
2657
+ '__mo_index_secondary_018cfbda-bde1-7c3e-805c-3f8e71769f75_cms_id'
2658
+ """
2659
+ from .index_utils import build_get_index_table_by_name_sql
2660
+
2661
+ sql, params = build_get_index_table_by_name_sql(table_name, index_name)
2662
+ result = self.execute(sql, params)
2663
+ row = result.fetchone()
2664
+ return row[0] if row else None
2665
+
2666
+ def verify_table_index_counts(self, table_name: str) -> int:
2667
+ """
2668
+ Verify that the main table and all its secondary index tables have the same row count.
2669
+
2670
+ This method compares the COUNT(*) of the main table with all its secondary index tables
2671
+ in a single SQL query for consistency. If counts don't match, raises an exception.
2672
+
2673
+ Args:
2674
+ table_name: Name of the table to verify
2675
+
2676
+ Returns:
2677
+ Row count (int) if verification succeeds
2678
+
2679
+ Raises:
2680
+ ValueError: If any secondary index table has a different count than the main table,
2681
+ with details about all counts in the error message
2682
+
2683
+ Examples::
2684
+
2685
+ >>> client = Client()
2686
+ >>> client.connect(host='localhost', port=6001, user='root', password='111', database='test')
2687
+ >>> count = client.verify_table_index_counts('cms_all_content_chunk_info')
2688
+ >>> print(f"✓ Verification passed, row count: {count}")
2689
+
2690
+ >>> # If verification fails:
2691
+ >>> try:
2692
+ ... count = client.verify_table_index_counts('some_table')
2693
+ ... except ValueError as e:
2694
+ ... print(f"Verification failed: {e}")
2695
+ """
2696
+ from .index_utils import build_verify_counts_sql, process_verify_result
2697
+
2698
+ # Get all secondary index tables
2699
+ index_tables = self.get_secondary_index_tables(table_name)
2700
+
2701
+ # Build and execute verification SQL
2702
+ sql = build_verify_counts_sql(table_name, index_tables)
2703
+ result = self.execute(sql)
2704
+ row = result.fetchone()
2705
+
2706
+ # Process result and raise exception if verification fails
2707
+ return process_verify_result(table_name, index_tables, row)
2708
+
2615
2709
  def __exit__(self, exc_type, exc_val, exc_tb):
2616
2710
  self.disconnect()
2617
2711
 
@@ -18,6 +18,7 @@ Connection hooks for MatrixOne clients
18
18
 
19
19
  from enum import Enum
20
20
  from typing import Callable, List, Optional, Union
21
+
21
22
  from sqlalchemy import event
22
23
  from sqlalchemy.engine import Engine
23
24
  from sqlalchemy.ext.asyncio import AsyncEngine
matrixone/exceptions.py CHANGED
@@ -20,70 +20,46 @@ MatrixOne SDK Exceptions
20
20
  class MatrixOneError(Exception):
21
21
  """Base exception for all MatrixOne SDK errors"""
22
22
 
23
- pass
24
-
25
23
 
26
24
  class ConnectionError(MatrixOneError):
27
25
  """Raised when connection to MatrixOne fails"""
28
26
 
29
- pass
30
-
31
27
 
32
28
  class QueryError(MatrixOneError):
33
29
  """Raised when SQL query execution fails"""
34
30
 
35
- pass
36
-
37
31
 
38
32
  class ConfigurationError(MatrixOneError):
39
33
  """Raised when configuration is invalid"""
40
34
 
41
- pass
42
-
43
35
 
44
36
  class SnapshotError(MatrixOneError):
45
37
  """Raised when snapshot operations fail"""
46
38
 
47
- pass
48
-
49
39
 
50
40
  class CloneError(MatrixOneError):
51
41
  """Raised when clone operations fail"""
52
42
 
53
- pass
54
-
55
43
 
56
44
  class MoCtlError(MatrixOneError):
57
45
  """Raised when mo_ctl operations fail"""
58
46
 
59
- pass
60
-
61
47
 
62
48
  class RestoreError(MatrixOneError):
63
49
  """Raised when restore operations fail"""
64
50
 
65
- pass
66
-
67
51
 
68
52
  class PitrError(MatrixOneError):
69
53
  """Raised when PITR operations fail"""
70
54
 
71
- pass
72
-
73
55
 
74
56
  class PubSubError(MatrixOneError):
75
57
  """Raised when publish-subscribe operations fail"""
76
58
 
77
- pass
78
-
79
59
 
80
60
  class AccountError(MatrixOneError):
81
61
  """Raised when account management operations fail"""
82
62
 
83
- pass
84
-
85
63
 
86
64
  class VersionError(MatrixOneError):
87
65
  """Raised when version compatibility check fails"""
88
-
89
- pass
@@ -0,0 +1,121 @@
1
+ # Copyright 2021 - 2022 Matrix Origin
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Index utilities - Shared logic for secondary index operations
17
+ """
18
+
19
+ from typing import List, Tuple
20
+
21
+
22
+ def build_get_index_tables_sql(table_name: str) -> Tuple[str, Tuple]:
23
+ """
24
+ Build SQL to get all secondary index table names for a given table.
25
+
26
+ Args:
27
+ table_name: Name of the table
28
+
29
+ Returns:
30
+ Tuple of (sql, params)
31
+ """
32
+ sql = """
33
+ SELECT DISTINCT index_table_name
34
+ FROM mo_catalog.mo_indexes
35
+ JOIN mo_catalog.mo_tables ON mo_indexes.table_id = mo_tables.rel_id
36
+ WHERE relname = ? AND type = 'MULTIPLE'
37
+ """
38
+ return sql, (table_name,)
39
+
40
+
41
+ def build_get_index_table_by_name_sql(table_name: str, index_name: str) -> Tuple[str, Tuple]:
42
+ """
43
+ Build SQL to get the physical table name of a secondary index by its index name.
44
+
45
+ Args:
46
+ table_name: Name of the table
47
+ index_name: Name of the secondary index
48
+
49
+ Returns:
50
+ Tuple of (sql, params)
51
+ """
52
+ sql = """
53
+ SELECT DISTINCT index_table_name
54
+ FROM mo_catalog.mo_indexes
55
+ JOIN mo_catalog.mo_tables ON mo_indexes.table_id = mo_tables.rel_id
56
+ WHERE relname = ? AND name = ?
57
+ """
58
+ return sql, (table_name, index_name)
59
+
60
+
61
+ def build_verify_counts_sql(table_name: str, index_tables: List[str]) -> str:
62
+ """
63
+ Build SQL to verify counts of main table and all index tables in a single query.
64
+
65
+ Args:
66
+ table_name: Name of the main table
67
+ index_tables: List of index table names
68
+
69
+ Returns:
70
+ SQL string
71
+ """
72
+ if not index_tables:
73
+ return f"SELECT COUNT(*) FROM `{table_name}`"
74
+
75
+ select_parts = [f"(SELECT COUNT(*) FROM `{table_name}`) as main_count"]
76
+ for idx, index_table in enumerate(index_tables):
77
+ select_parts.append(f"(SELECT COUNT(*) FROM `{index_table}`) as idx{idx}_count")
78
+
79
+ return "SELECT " + ", ".join(select_parts)
80
+
81
+
82
+ def process_verify_result(table_name: str, index_tables: List[str], row: Tuple) -> int:
83
+ """
84
+ Process the verification result and raise exception if counts don't match.
85
+
86
+ Args:
87
+ table_name: Name of the main table
88
+ index_tables: List of index table names
89
+ row: Result row from the verification SQL
90
+
91
+ Returns:
92
+ Row count if verification succeeds
93
+
94
+ Raises:
95
+ ValueError: If any index table has a different count
96
+ """
97
+ main_count = row[0]
98
+
99
+ if not index_tables:
100
+ return main_count
101
+
102
+ index_counts = {}
103
+ mismatch = []
104
+
105
+ for idx, index_table in enumerate(index_tables):
106
+ index_count = row[idx + 1]
107
+ index_counts[index_table] = index_count
108
+ if index_count != main_count:
109
+ mismatch.append(index_table)
110
+
111
+ # If there's a mismatch, raise an exception with details
112
+ if mismatch:
113
+ error_details = [f"Main table '{table_name}': {main_count} rows"]
114
+ for index_table, count in index_counts.items():
115
+ status = "✗ MISMATCH" if index_table in mismatch else "✓"
116
+ error_details.append(f"{status} Index '{index_table}': {count} rows")
117
+
118
+ error_msg = "Index count verification failed!\n" + "\n".join(error_details)
119
+ raise ValueError(error_msg)
120
+
121
+ return main_count
matrixone/metadata.py CHANGED
@@ -19,10 +19,11 @@ This module provides metadata scanning capabilities for MatrixOne tables,
19
19
  allowing users to analyze table statistics, column information, and data distribution.
20
20
  """
21
21
 
22
- from typing import Optional, List, Dict, Any, Union
23
- from sqlalchemy.engine import Result
24
22
  from dataclasses import dataclass
25
23
  from enum import Enum
24
+ from typing import Any, Dict, List, Optional, Union
25
+
26
+ from sqlalchemy.engine import Result
26
27
 
27
28
 
28
29
  class MetadataColumn(Enum):
matrixone/moctl.py CHANGED
@@ -40,8 +40,6 @@ from .exceptions import MatrixOneError
40
40
  class MoCtlError(MatrixOneError):
41
41
  """Raised when mo_ctl operations fail"""
42
42
 
43
- pass
44
-
45
43
 
46
44
  class MoCtlManager:
47
45
  """
@@ -147,6 +147,7 @@ __all__ = [
147
147
  "FulltextIndex",
148
148
  "FulltextAlgorithmType",
149
149
  "FulltextModeType",
150
+ "FulltextParserType",
150
151
  "FulltextSearchBuilder",
151
152
  "create_fulltext_index",
152
153
  "fulltext_search_builder",
matrixone/version.py CHANGED
@@ -519,8 +519,6 @@ def requires_version(
519
519
  class VersionError(MatrixOneError):
520
520
  """Raised when version compatibility check fails"""
521
521
 
522
- pass
523
-
524
522
 
525
523
  # Initialize common feature requirements
526
524
  def _initialize_default_features():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrixone-python-sdk
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: A comprehensive Python SDK for MatrixOne database operations with vector search, fulltext search, and advanced features
5
5
  Home-page: https://github.com/matrixorigin/matrixone
6
6
  Author: MatrixOne Team
@@ -1,17 +1,18 @@
1
1
  matrixone/__init__.py,sha256=7bePzwzerZTiZIlbUT9zI_u4fp49DvQNcIqyiIvEVVs,3966
2
2
  matrixone/account.py,sha256=0r9xLNTiUfXa3xWZQUhEJ_uUcNp_gja-YulOR5iYDU4,24712
3
- matrixone/async_client.py,sha256=krF9xtn_aGS45VcShV5eYy89FHk0mCI9Ig-8RufBJmY,151518
4
- matrixone/async_metadata_manager.py,sha256=7k5qtQbKO-52IXzhDN0qhvYdp5wars5M6ZjdSLfD8RM,10230
3
+ matrixone/async_client.py,sha256=KKvWIzcV2PtwaFT31jgawRLt1CJhI_rePQdAs8xLzEc,155275
4
+ matrixone/async_metadata_manager.py,sha256=W3TJBCy56diDPBzimpBImFrmf9DBc7iiS5CdBHLAh_Q,10232
5
5
  matrixone/async_orm.py,sha256=O4Rf85MnZg0_fuLvbHGfOPrHI2OLznYwLk9eam5J5ik,4864
6
6
  matrixone/async_vector_index_manager.py,sha256=fVnS00zgbUXw4YmZR-wRCzm541n30TLlmXXmXV34ELU,23134
7
7
  matrixone/base_client.py,sha256=PUhq0c7Si9OIk83B4ZZ73ozwaHDRZ1zi0Q94YixseqY,6952
8
- matrixone/client.py,sha256=XWEXEq8FlMfAJ4O4qbnriy2S7qmYGmpLeWe37HHrzg0,183615
8
+ matrixone/client.py,sha256=bxdKUVTzo18yMDFmgcUdSIfoKozC-pfUOlKuWCwwGwI,187341
9
9
  matrixone/config.py,sha256=jrm1rLBhLWrX3dVdPx67sFaZ_9M8Vj4hImVrAmFaYbw,13917
10
- matrixone/connection_hooks.py,sha256=kjv0HMCUZkma-9nTndNBV66NVcp4cTWs-8u5b6KCj2E,13530
11
- matrixone/exceptions.py,sha256=oUQUY_TD8UTSzZzbwGdUgQ0kFJIYZq5Fx-vFsMaOfvw,1787
10
+ matrixone/connection_hooks.py,sha256=FrCGWipg5KUrdBt-cJMORaOUfoIqgSZNZCtDnn9la50,13531
11
+ matrixone/exceptions.py,sha256=VsGgESLl4vm5-9QbigyE5RTwFujyoYXhQuGhGKoujFw,1667
12
+ matrixone/index_utils.py,sha256=ND_Md-mj1Yh8Qq4RGW5tYxwqkgwItkWzWDLL_SQ9AMI,3759
12
13
  matrixone/logger.py,sha256=aZmetje_AqtyxFxsqZvp-3R68n8I_ldplRGfV2MHg48,26105
13
- matrixone/metadata.py,sha256=wHQNrW4bRYOriujZv-ym9F68XEwJJt8N23xcyNQ9wKY,31706
14
- matrixone/moctl.py,sha256=rLt69ZlhIr_pCbyu8cImKgdR_YOGf85XvqCHcW12sc0,6714
14
+ matrixone/metadata.py,sha256=Yf0eJywe3BUqg6aEywSWh9neRcVsrgf6g-uaZFvqLOA,31707
15
+ matrixone/moctl.py,sha256=b_qQPSK52JiyyUH4PYzAuhJPk8dAD6LRfT_sFGsf23o,6704
15
16
  matrixone/orm.py,sha256=QWNdfJ067KcgWU-eB__vXwJuANBaDUeVVG7thFH5kiU,90473
16
17
  matrixone/pitr.py,sha256=NPsexPsJXIAiJTH3oMyFOwiXWjvfohPzWQyFHhQKh5k,21632
17
18
  matrixone/pubsub.py,sha256=ckaG6Z3IuEtcdwp11qvB88PrCDHYymocSOJ1v4TftzY,26893
@@ -19,8 +20,8 @@ matrixone/restore.py,sha256=xAbsRUL-2hCk6YpuouVLbiwc3GwUNzCoIVEFtcmghkg,15791
19
20
  matrixone/search_vector_index.py,sha256=NjQpluaPcja66herm-OYq3nNeNAqY_-FGGL4SOBOWNA,46075
20
21
  matrixone/snapshot.py,sha256=cOatk26Z6Jt6aAdasOMejxxiv73S7Z2o2if_va20Ihg,18103
21
22
  matrixone/sql_builder.py,sha256=ZR9TpRkV4cN_RiDJQXMLwSzodpESyxAqV-TTIr--ZbY,29009
22
- matrixone/version.py,sha256=jsHOK9_liBu4zT4r2PWUu4RqIEcYmCGERdMdVJZhJ14,19923
23
- matrixone/sqlalchemy_ext/__init__.py,sha256=L9f3mizOwnUSBRlaondoePqvnLo9o_CKe42pdkEf0Sk,4003
23
+ matrixone/version.py,sha256=7wpMFVqDjGE16bFtnvFwbcnAI3mzz2zMZPpc9v7G9-c,19913
24
+ matrixone/sqlalchemy_ext/__init__.py,sha256=YrcXthWkpN-mkRq6293t6Su5D39MdsEKYu_g1lz4qgI,4029
24
25
  matrixone/sqlalchemy_ext/adapters.py,sha256=-PoQtxN8uveto4Me2BTyTq1FoeSj8qo3TkUKAhoUd4w,5286
25
26
  matrixone/sqlalchemy_ext/dialect.py,sha256=wrAQDjDDpCz6HT2Atrz7QXAzbyxaNTbRXcauh2REa1A,21100
26
27
  matrixone/sqlalchemy_ext/fulltext_index.py,sha256=OceS34yg1kG-1uopG1lDf7GbVyRiUnxzKHDUHYLNLuA,27614
@@ -30,7 +31,7 @@ matrixone/sqlalchemy_ext/ivf_config.py,sha256=-esAijWMfyF1GUJivpZh-F4lvyKRmY8LYI
30
31
  matrixone/sqlalchemy_ext/table_builder.py,sha256=JVritBPnCXZt0eJUivDrmGdlpaYO-uL7i2n24HiarEE,12870
31
32
  matrixone/sqlalchemy_ext/vector_index.py,sha256=dasW3kT4f69io9y6DLQMarvf1FTbzh0UN-HALs0kBVs,55907
32
33
  matrixone/sqlalchemy_ext/vector_type.py,sha256=HaOJ9dRdW_yrecD9qGUucW9bMfM3zCxbDC-0Ca32Kmk,30669
33
- matrixone_python_sdk-0.1.2.dist-info/licenses/LICENSE,sha256=-PpUMwDyMyFlH9H7cnzkTh0Uo42tRvz43k7hnxe7G_I,11252
34
+ matrixone_python_sdk-0.1.3.dist-info/licenses/LICENSE,sha256=-PpUMwDyMyFlH9H7cnzkTh0Uo42tRvz43k7hnxe7G_I,11252
34
35
  tests/__init__.py,sha256=odB22tIaJIHSwRhumhDlQYD6Fug_C0opWa07dSKkeQs,694
35
36
  tests/offline/__init__.py,sha256=M13mz7gtVDS0_dJUW1EFyyiAGhEj282k3ia7eWA3dPs,703
36
37
  tests/offline/conftest.py,sha256=Mz_NT6GBOxqSZsSCR2SXe1pkSpLGKT2-ssWNHhh9xOg,2494
@@ -77,6 +78,7 @@ tests/online/test_account_management.py,sha256=mWIF3kPaegcb-JEhHBM9LrSae2xZ3ppjk
77
78
  tests/online/test_advanced_features.py,sha256=f0oOe1bB6dT4bXZqx4LzrWd4ljwwcr9iIZLDHnTdPhU,13641
78
79
  tests/online/test_async_client_interfaces.py,sha256=ZT2kWm-ZT_O-_ZkGE7mVLE5c52_eRsbCC_wEksrUis0,12295
79
80
  tests/online/test_async_client_online.py,sha256=LZp0QXk4l4FfuQOHNNg__degRTwh9K61StFtEmeUmZ8,10906
81
+ tests/online/test_async_index_verification_online.py,sha256=zevl8BKAIGRyJ0ndnUmW_oQpC_rc2_W0ATPb152OMag,10162
80
82
  tests/online/test_async_model_insert_online.py,sha256=YPlcNjuaeimvG355JjjgyZUPfa9J0HtC5FVJr9uIp_k,10857
81
83
  tests/online/test_async_orm_online.py,sha256=8wjQb9oXMyNh4HQ1YkwiFYfHo73_YVPicOR_aiXrbEg,11039
82
84
  tests/online/test_async_simple_query_online.py,sha256=nBMsCv-GaEiSRxh1juLNCiXNNsfUwWRApQvStzLeGr4,31021
@@ -91,6 +93,7 @@ tests/online/test_filter_logical_in_online.py,sha256=-OrmHSMyk6nYap5OCICaImt9AQ5
91
93
  tests/online/test_fulltext_comprehensive.py,sha256=lJ_XMkZnHe1HPsbXS9O5F9tBBsPKTBNUnx78KgKTv3w,74063
92
94
  tests/online/test_fulltext_label_online.py,sha256=1ExVrGIOA7dfG6UiB7Ul6xeLrLY9u6oAjjT4hYNtr8E,16534
93
95
  tests/online/test_fulltext_search_online.py,sha256=711WdigiDR71nwtqLlVl39_lNocbWlNnJR5W6hBhl50,33660
96
+ tests/online/test_index_verification_online.py,sha256=Uz8Gjvjm2SdNhpXeun_Y8KOQ1VOs601WLMmxWTMzNa0,7510
94
97
  tests/online/test_ivf_stats_online.py,sha256=Fdi_-bleJVXtWzHNfeA6eCgVdlvSvpEfop7GGUZTQ9E,19845
95
98
  tests/online/test_logger_integration.py,sha256=iv0TrKZGRg-oxjbp55fYixbfpdqLFWQDR8mXeaSKArg,11824
96
99
  tests/online/test_matrixone_query_orm.py,sha256=WYH6HxcfnNqtQ05kKHRcmwmWHXtDz8Ts0pUwuSdgo_U,20130
@@ -115,8 +118,8 @@ tests/online/test_transaction_query_methods.py,sha256=VaPvHOYskwJVyM7lvVvBJ6Hqhd
115
118
  tests/online/test_unified_filter_online.py,sha256=86JwEKUaBLNuLGJlR4E0zjzdWL5xVFyYLvyxbzkdEVI,21214
116
119
  tests/online/test_vector_comprehensive.py,sha256=vmnwwVXLA7lUI_zSK3fJcf1HKx2AvrVDcUT0_d-gQwg,26980
117
120
  tests/online/test_version_management.py,sha256=BU8Vc1fDKNCwhRliZi6XmEnd0HYdHuki9Xxi09vnriA,11416
118
- matrixone_python_sdk-0.1.2.dist-info/METADATA,sha256=eZci5V6XBGdSXpvmVs7SNdkFcLWvRCx2Lh9F-xBiG2c,20815
119
- matrixone_python_sdk-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
120
- matrixone_python_sdk-0.1.2.dist-info/entry_points.txt,sha256=4wUGPC_7_f5ZDR33JRo1LZmmTuyfkYAv41_5H5Qy-Ik,138
121
- matrixone_python_sdk-0.1.2.dist-info/top_level.txt,sha256=LQZabpBx_dtQk8JbKeH3MbjmC8HYDLE8UQeEf6NfQJA,16
122
- matrixone_python_sdk-0.1.2.dist-info/RECORD,,
121
+ matrixone_python_sdk-0.1.3.dist-info/METADATA,sha256=lNJxaTy1NjfSwoOdr8YOVZ-E9ISSjL2U-t9ZDtZsMnI,20815
122
+ matrixone_python_sdk-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
123
+ matrixone_python_sdk-0.1.3.dist-info/entry_points.txt,sha256=4wUGPC_7_f5ZDR33JRo1LZmmTuyfkYAv41_5H5Qy-Ik,138
124
+ matrixone_python_sdk-0.1.3.dist-info/top_level.txt,sha256=LQZabpBx_dtQk8JbKeH3MbjmC8HYDLE8UQeEf6NfQJA,16
125
+ matrixone_python_sdk-0.1.3.dist-info/RECORD,,
@@ -0,0 +1,259 @@
1
+ # Copyright 2021 - 2022 Matrix Origin
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Online tests for async secondary index verification functionality
17
+ """
18
+
19
+ import pytest
20
+ import pytest_asyncio
21
+ import os
22
+ import sys
23
+
24
+ # Add the matrixone package to the path
25
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
26
+
27
+ from matrixone import AsyncClient
28
+ from matrixone.orm import declarative_base
29
+ from sqlalchemy import Column, String, Integer, Index
30
+ from .test_config import online_config
31
+
32
+
33
+ class TestAsyncIndexVerificationOnline:
34
+ """Online tests for async secondary index verification"""
35
+
36
+ @pytest_asyncio.fixture(scope="function")
37
+ async def async_client_with_test_table(self):
38
+ """Create AsyncClient with test table"""
39
+ host, port, user, password, database = online_config.get_connection_params()
40
+ client = AsyncClient()
41
+ await client.connect(host=host, port=port, user=user, password=password, database=database)
42
+
43
+ test_table = "test_async_index_table"
44
+
45
+ try:
46
+ # Define ORM model with secondary indexes
47
+ Base = declarative_base()
48
+
49
+ class TestTable(Base):
50
+ __tablename__ = test_table
51
+
52
+ id = Column(Integer, primary_key=True)
53
+ name = Column(String(100))
54
+ category = Column(String(50))
55
+ value = Column(Integer)
56
+
57
+ # Define secondary indexes
58
+ __table_args__ = (
59
+ Index('idx_async_name', 'name'),
60
+ Index('idx_async_category', 'category'),
61
+ Index('idx_async_value', 'value'),
62
+ )
63
+
64
+ # Drop table if exists and create new one using SDK API
65
+ try:
66
+ await client.drop_table(TestTable)
67
+ except:
68
+ pass
69
+
70
+ # Create table with indexes using SDK API
71
+ await client.create_table(TestTable)
72
+
73
+ # Insert test data using SDK API
74
+ test_data = [
75
+ {'id': i, 'name': f'async_name_{i}', 'category': f'cat_{i % 5}', 'value': i * 10} for i in range(1, 101)
76
+ ]
77
+ await client.batch_insert(TestTable, test_data)
78
+
79
+ yield client, test_table, TestTable
80
+
81
+ finally:
82
+ # Clean up using SDK API
83
+ try:
84
+ await client.drop_table(TestTable)
85
+ await client.disconnect()
86
+ except Exception as e:
87
+ print(f"Cleanup failed: {e}")
88
+
89
+ @pytest.mark.asyncio
90
+ async def test_async_get_secondary_index_tables(self, async_client_with_test_table):
91
+ """Test async getting all secondary index table names"""
92
+ client, test_table, TestTable = async_client_with_test_table
93
+
94
+ index_tables = await client.get_secondary_index_tables(test_table)
95
+
96
+ # Should have 3 secondary indexes
97
+ assert len(index_tables) == 3, f"Expected 3 indexes, got {len(index_tables)}"
98
+
99
+ # All index tables should start with __mo_index_secondary_
100
+ for index_table in index_tables:
101
+ assert index_table.startswith(
102
+ '__mo_index_secondary_'
103
+ ), f"Index table name should start with '__mo_index_secondary_', got {index_table}"
104
+
105
+
106
+ class TestAsyncIndexVerificationOnline:
107
+ """Online tests for async secondary index verification"""
108
+
109
+ @pytest_asyncio.fixture(scope="function")
110
+ async def async_client_with_test_table(self):
111
+ """Create AsyncClient with test table"""
112
+ host, port, user, password, database = online_config.get_connection_params()
113
+ client = AsyncClient()
114
+ await client.connect(host=host, port=port, user=user, password=password, database=database)
115
+
116
+ test_table = "test_async_index_table"
117
+
118
+ try:
119
+ # Define ORM model with secondary indexes
120
+ Base = declarative_base()
121
+
122
+ class TestTable(Base):
123
+ __tablename__ = test_table
124
+
125
+ id = Column(Integer, primary_key=True)
126
+ name = Column(String(100))
127
+ category = Column(String(50))
128
+ value = Column(Integer)
129
+
130
+ # Define secondary indexes
131
+ __table_args__ = (
132
+ Index('idx_async_name', 'name'),
133
+ Index('idx_async_category', 'category'),
134
+ Index('idx_async_value', 'value'),
135
+ )
136
+
137
+ # Drop table if exists and create new one using SDK API
138
+ try:
139
+ await client.drop_table(TestTable)
140
+ except:
141
+ pass
142
+
143
+ # Create table with indexes using SDK API
144
+ await client.create_table(TestTable)
145
+
146
+ # Insert test data using SDK API
147
+ test_data = [
148
+ {'id': i, 'name': f'async_name_{i}', 'category': f'cat_{i % 5}', 'value': i * 10} for i in range(1, 101)
149
+ ]
150
+ await client.batch_insert(TestTable, test_data)
151
+
152
+ yield client, test_table, TestTable
153
+
154
+ finally:
155
+ # Clean up using SDK API
156
+ try:
157
+ await client.drop_table(TestTable)
158
+ await client.disconnect()
159
+ except Exception as e:
160
+ print(f"Cleanup failed: {e}")
161
+
162
+ @pytest.mark.asyncio
163
+ async def test_async_get_secondary_index_table_by_name(self, async_client_with_test_table):
164
+ """Test async getting physical table name of a secondary index by its name"""
165
+ client, test_table, TestTable = async_client_with_test_table
166
+
167
+ # Test getting idx_async_name
168
+ idx_name_table = await client.get_secondary_index_table_by_name(test_table, 'idx_async_name')
169
+ assert idx_name_table is not None, "idx_async_name should exist"
170
+ assert idx_name_table.startswith('__mo_index_secondary_')
171
+
172
+ # Test getting idx_async_category
173
+ idx_category_table = await client.get_secondary_index_table_by_name(test_table, 'idx_async_category')
174
+ assert idx_category_table is not None, "idx_async_category should exist"
175
+ assert idx_category_table.startswith('__mo_index_secondary_')
176
+
177
+ # Test getting idx_async_value
178
+ idx_value_table = await client.get_secondary_index_table_by_name(test_table, 'idx_async_value')
179
+ assert idx_value_table is not None, "idx_async_value should exist"
180
+ assert idx_value_table.startswith('__mo_index_secondary_')
181
+
182
+ # Test non-existent index
183
+ non_existent = await client.get_secondary_index_table_by_name(test_table, 'idx_non_existent')
184
+ assert non_existent is None, "Non-existent index should return None"
185
+
186
+ @pytest.mark.asyncio
187
+ async def test_async_verify_table_index_counts(self, async_client_with_test_table):
188
+ """Test async verify_table_index_counts when counts match"""
189
+ client, test_table, TestTable = async_client_with_test_table
190
+
191
+ # All indexes should have the same count as the main table
192
+ count = await client.verify_table_index_counts(test_table)
193
+
194
+ # Should return 100 (number of inserted rows)
195
+ assert count == 100, f"Expected count 100, got {count}"
196
+
197
+ # Verify count matches query result using SDK API
198
+ actual_count = await client.query(TestTable).count()
199
+ assert count == actual_count, "Verify result should match actual count"
200
+
201
+ @pytest.mark.asyncio
202
+ async def test_async_verify_table_without_indexes(self, async_client_with_test_table):
203
+ """Test async verify_table_index_counts on table without secondary indexes"""
204
+ client, test_table, TestTable = async_client_with_test_table
205
+
206
+ # Define a simple model without secondary indexes
207
+ Base = declarative_base()
208
+
209
+ class SimpleTable(Base):
210
+ __tablename__ = "test_async_simple_table"
211
+ id = Column(Integer, primary_key=True)
212
+ value = Column(Integer)
213
+
214
+ # Create table using SDK API
215
+ try:
216
+ await client.drop_table(SimpleTable)
217
+ except:
218
+ pass
219
+
220
+ await client.create_table(SimpleTable)
221
+
222
+ # Insert data using SDK API
223
+ test_data = [{'id': 1, 'value': 100}, {'id': 2, 'value': 200}]
224
+ await client.batch_insert(SimpleTable, test_data)
225
+
226
+ # Verification should succeed and return count
227
+ count = await client.verify_table_index_counts("test_async_simple_table")
228
+ assert count == 2, f"Expected count 2, got {count}"
229
+
230
+ # Cleanup using SDK API
231
+ await client.drop_table(SimpleTable)
232
+
233
+ @pytest.mark.asyncio
234
+ async def test_async_index_table_mapping(self, async_client_with_test_table):
235
+ """Test that all indexes can be retrieved both ways (async)"""
236
+ client, test_table, TestTable = async_client_with_test_table
237
+
238
+ # Get all index tables
239
+ all_index_tables = await client.get_secondary_index_tables(test_table)
240
+
241
+ # Get each index by name
242
+ index_names = ['idx_async_name', 'idx_async_category', 'idx_async_value']
243
+ retrieved_tables = []
244
+
245
+ for index_name in index_names:
246
+ table = await client.get_secondary_index_table_by_name(test_table, index_name)
247
+ assert table is not None, f"Index {index_name} should exist"
248
+ retrieved_tables.append(table)
249
+
250
+ # All retrieved tables should be in the all_index_tables list
251
+ for table in retrieved_tables:
252
+ assert table in all_index_tables, f"Retrieved table {table} should be in all index tables"
253
+
254
+ # Both lists should have the same length
255
+ assert len(retrieved_tables) == len(all_index_tables), "Number of retrieved tables should match total index tables"
256
+
257
+
258
+ if __name__ == '__main__':
259
+ pytest.main([__file__, '-v'])
@@ -0,0 +1,194 @@
1
+ # Copyright 2021 - 2022 Matrix Origin
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Online tests for secondary index verification functionality
17
+ """
18
+
19
+ import unittest
20
+ import os
21
+ import sys
22
+
23
+ # Add the matrixone package to the path
24
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
25
+
26
+ from matrixone import Client
27
+ from matrixone.orm import declarative_base
28
+ from sqlalchemy import Column, String, Integer, Index
29
+
30
+
31
+ class TestIndexVerificationOnline(unittest.TestCase):
32
+ """Online tests for secondary index verification"""
33
+
34
+ @classmethod
35
+ def setUpClass(cls):
36
+ """Set up test database connection"""
37
+ cls.client = Client(
38
+ host=os.getenv('MATRIXONE_HOST', '127.0.0.1'),
39
+ port=int(os.getenv('MATRIXONE_PORT', '6001')),
40
+ user=os.getenv('MATRIXONE_USER', 'root'),
41
+ password=os.getenv('MATRIXONE_PASSWORD', '111'),
42
+ database=os.getenv('MATRIXONE_DATABASE', 'test'),
43
+ )
44
+
45
+ cls.test_table = "test_index_table"
46
+
47
+ try:
48
+ # Define ORM model with secondary indexes
49
+ Base = declarative_base()
50
+
51
+ class TestTable(Base):
52
+ __tablename__ = cls.test_table
53
+
54
+ id = Column(Integer, primary_key=True)
55
+ name = Column(String(100))
56
+ category = Column(String(50))
57
+ value = Column(Integer)
58
+
59
+ # Define secondary indexes
60
+ __table_args__ = (
61
+ Index('idx_name', 'name'),
62
+ Index('idx_category', 'category'),
63
+ Index('idx_value', 'value'),
64
+ )
65
+
66
+ cls.TestTable = TestTable
67
+
68
+ # Drop table if exists and create new one
69
+ try:
70
+ cls.client.drop_table(TestTable)
71
+ except:
72
+ pass
73
+
74
+ # Create table with indexes using SDK API
75
+ cls.client.create_table(TestTable)
76
+
77
+ # Insert test data using SDK API
78
+ test_data = [{'id': i, 'name': f'name_{i}', 'category': f'cat_{i % 5}', 'value': i * 10} for i in range(1, 101)]
79
+ cls.client.batch_insert(TestTable, test_data)
80
+
81
+ except Exception as e:
82
+ print(f"Setup failed: {e}")
83
+ raise
84
+
85
+ @classmethod
86
+ def tearDownClass(cls):
87
+ """Clean up test database"""
88
+ try:
89
+ cls.client.drop_table(cls.TestTable)
90
+ except Exception as e:
91
+ print(f"Cleanup failed: {e}")
92
+
93
+ def test_get_secondary_index_tables(self):
94
+ """Test getting all secondary index table names"""
95
+ index_tables = self.client.get_secondary_index_tables(self.test_table)
96
+
97
+ # Should have 3 secondary indexes
98
+ self.assertEqual(len(index_tables), 3, f"Expected 3 indexes, got {len(index_tables)}")
99
+
100
+ # All index tables should start with __mo_index_secondary_
101
+ for index_table in index_tables:
102
+ self.assertTrue(
103
+ index_table.startswith('__mo_index_secondary_'),
104
+ f"Index table name should start with '__mo_index_secondary_', got {index_table}",
105
+ )
106
+
107
+ def test_get_secondary_index_table_by_name(self):
108
+ """Test getting physical table name of a secondary index by its name"""
109
+ # Test getting idx_name
110
+ idx_name_table = self.client.get_secondary_index_table_by_name(self.test_table, 'idx_name')
111
+ self.assertIsNotNone(idx_name_table, "idx_name should exist")
112
+ self.assertTrue(idx_name_table.startswith('__mo_index_secondary_'))
113
+
114
+ # Test getting idx_category
115
+ idx_category_table = self.client.get_secondary_index_table_by_name(self.test_table, 'idx_category')
116
+ self.assertIsNotNone(idx_category_table, "idx_category should exist")
117
+ self.assertTrue(idx_category_table.startswith('__mo_index_secondary_'))
118
+
119
+ # Test getting idx_value
120
+ idx_value_table = self.client.get_secondary_index_table_by_name(self.test_table, 'idx_value')
121
+ self.assertIsNotNone(idx_value_table, "idx_value should exist")
122
+ self.assertTrue(idx_value_table.startswith('__mo_index_secondary_'))
123
+
124
+ # Test non-existent index
125
+ non_existent = self.client.get_secondary_index_table_by_name(self.test_table, 'idx_non_existent')
126
+ self.assertIsNone(non_existent, "Non-existent index should return None")
127
+
128
+ def test_verify_table_index_counts(self):
129
+ """Test verify_table_index_counts when counts match"""
130
+ # All indexes should have the same count as the main table
131
+ count = self.client.verify_table_index_counts(self.test_table)
132
+
133
+ # Should return 100 (number of inserted rows)
134
+ self.assertEqual(count, 100, f"Expected count 100, got {count}")
135
+
136
+ # Verify count matches query result using SDK API
137
+ actual_count = self.client.query(self.TestTable).count()
138
+ self.assertEqual(count, actual_count, "Verify result should match actual count")
139
+
140
+ def test_verify_table_without_indexes(self):
141
+ """Test verify_table_index_counts on table without secondary indexes"""
142
+ # Define a simple model without secondary indexes
143
+ Base = declarative_base()
144
+
145
+ class SimpleTable(Base):
146
+ __tablename__ = "test_simple_table"
147
+ id = Column(Integer, primary_key=True)
148
+ value = Column(Integer)
149
+
150
+ # Create table using SDK API
151
+ try:
152
+ self.client.drop_table(SimpleTable)
153
+ except:
154
+ pass
155
+
156
+ self.client.create_table(SimpleTable)
157
+
158
+ # Insert data using SDK API
159
+ test_data = [{'id': 1, 'value': 100}, {'id': 2, 'value': 200}]
160
+ self.client.batch_insert(SimpleTable, test_data)
161
+
162
+ # Verification should succeed and return count
163
+ count = self.client.verify_table_index_counts("test_simple_table")
164
+ self.assertEqual(count, 2, f"Expected count 2, got {count}")
165
+
166
+ # Cleanup using SDK API
167
+ self.client.drop_table(SimpleTable)
168
+
169
+ def test_index_table_mapping(self):
170
+ """Test that all indexes can be retrieved both ways"""
171
+ # Get all index tables
172
+ all_index_tables = self.client.get_secondary_index_tables(self.test_table)
173
+
174
+ # Get each index by name
175
+ index_names = ['idx_name', 'idx_category', 'idx_value']
176
+ retrieved_tables = []
177
+
178
+ for index_name in index_names:
179
+ table = self.client.get_secondary_index_table_by_name(self.test_table, index_name)
180
+ self.assertIsNotNone(table, f"Index {index_name} should exist")
181
+ retrieved_tables.append(table)
182
+
183
+ # All retrieved tables should be in the all_index_tables list
184
+ for table in retrieved_tables:
185
+ self.assertIn(table, all_index_tables, f"Retrieved table {table} should be in all index tables")
186
+
187
+ # Both lists should have the same length
188
+ self.assertEqual(
189
+ len(retrieved_tables), len(all_index_tables), "Number of retrieved tables should match total index tables"
190
+ )
191
+
192
+
193
+ if __name__ == '__main__':
194
+ unittest.main()