matrixone-python-sdk 0.1.0__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.
Files changed (122) hide show
  1. matrixone/__init__.py +155 -0
  2. matrixone/account.py +723 -0
  3. matrixone/async_client.py +3913 -0
  4. matrixone/async_metadata_manager.py +311 -0
  5. matrixone/async_orm.py +123 -0
  6. matrixone/async_vector_index_manager.py +633 -0
  7. matrixone/base_client.py +208 -0
  8. matrixone/client.py +4672 -0
  9. matrixone/config.py +452 -0
  10. matrixone/connection_hooks.py +286 -0
  11. matrixone/exceptions.py +89 -0
  12. matrixone/logger.py +782 -0
  13. matrixone/metadata.py +820 -0
  14. matrixone/moctl.py +219 -0
  15. matrixone/orm.py +2277 -0
  16. matrixone/pitr.py +646 -0
  17. matrixone/pubsub.py +771 -0
  18. matrixone/restore.py +411 -0
  19. matrixone/search_vector_index.py +1176 -0
  20. matrixone/snapshot.py +550 -0
  21. matrixone/sql_builder.py +844 -0
  22. matrixone/sqlalchemy_ext/__init__.py +161 -0
  23. matrixone/sqlalchemy_ext/adapters.py +163 -0
  24. matrixone/sqlalchemy_ext/dialect.py +534 -0
  25. matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
  26. matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
  27. matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
  28. matrixone/sqlalchemy_ext/ivf_config.py +252 -0
  29. matrixone/sqlalchemy_ext/table_builder.py +351 -0
  30. matrixone/sqlalchemy_ext/vector_index.py +1721 -0
  31. matrixone/sqlalchemy_ext/vector_type.py +948 -0
  32. matrixone/version.py +580 -0
  33. matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
  34. matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
  35. matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
  36. matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
  37. matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
  38. matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
  39. tests/__init__.py +19 -0
  40. tests/offline/__init__.py +20 -0
  41. tests/offline/conftest.py +77 -0
  42. tests/offline/test_account.py +703 -0
  43. tests/offline/test_async_client_query_comprehensive.py +1218 -0
  44. tests/offline/test_basic.py +54 -0
  45. tests/offline/test_case_sensitivity.py +227 -0
  46. tests/offline/test_connection_hooks_offline.py +287 -0
  47. tests/offline/test_dialect_schema_handling.py +609 -0
  48. tests/offline/test_explain_methods.py +346 -0
  49. tests/offline/test_filter_logical_in.py +237 -0
  50. tests/offline/test_fulltext_search_comprehensive.py +795 -0
  51. tests/offline/test_ivf_config.py +249 -0
  52. tests/offline/test_join_methods.py +281 -0
  53. tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
  54. tests/offline/test_logical_in_method.py +237 -0
  55. tests/offline/test_matrixone_version_parsing.py +264 -0
  56. tests/offline/test_metadata_offline.py +557 -0
  57. tests/offline/test_moctl.py +300 -0
  58. tests/offline/test_moctl_simple.py +251 -0
  59. tests/offline/test_model_support_offline.py +359 -0
  60. tests/offline/test_model_support_simple.py +225 -0
  61. tests/offline/test_pinecone_filter_offline.py +377 -0
  62. tests/offline/test_pitr.py +585 -0
  63. tests/offline/test_pubsub.py +712 -0
  64. tests/offline/test_query_update.py +283 -0
  65. tests/offline/test_restore.py +445 -0
  66. tests/offline/test_snapshot_comprehensive.py +384 -0
  67. tests/offline/test_sql_escaping_edge_cases.py +551 -0
  68. tests/offline/test_sqlalchemy_integration.py +382 -0
  69. tests/offline/test_sqlalchemy_vector_integration.py +434 -0
  70. tests/offline/test_table_builder.py +198 -0
  71. tests/offline/test_unified_filter.py +398 -0
  72. tests/offline/test_unified_transaction.py +495 -0
  73. tests/offline/test_vector_index.py +238 -0
  74. tests/offline/test_vector_operations.py +688 -0
  75. tests/offline/test_vector_type.py +174 -0
  76. tests/offline/test_version_core.py +328 -0
  77. tests/offline/test_version_management.py +372 -0
  78. tests/offline/test_version_standalone.py +652 -0
  79. tests/online/__init__.py +20 -0
  80. tests/online/conftest.py +216 -0
  81. tests/online/test_account_management.py +194 -0
  82. tests/online/test_advanced_features.py +344 -0
  83. tests/online/test_async_client_interfaces.py +330 -0
  84. tests/online/test_async_client_online.py +285 -0
  85. tests/online/test_async_model_insert_online.py +293 -0
  86. tests/online/test_async_orm_online.py +300 -0
  87. tests/online/test_async_simple_query_online.py +802 -0
  88. tests/online/test_async_transaction_simple_query.py +300 -0
  89. tests/online/test_basic_connection.py +130 -0
  90. tests/online/test_client_online.py +238 -0
  91. tests/online/test_config.py +90 -0
  92. tests/online/test_config_validation.py +123 -0
  93. tests/online/test_connection_hooks_new_online.py +217 -0
  94. tests/online/test_dialect_schema_handling_online.py +331 -0
  95. tests/online/test_filter_logical_in_online.py +374 -0
  96. tests/online/test_fulltext_comprehensive.py +1773 -0
  97. tests/online/test_fulltext_label_online.py +433 -0
  98. tests/online/test_fulltext_search_online.py +842 -0
  99. tests/online/test_ivf_stats_online.py +506 -0
  100. tests/online/test_logger_integration.py +311 -0
  101. tests/online/test_matrixone_query_orm.py +540 -0
  102. tests/online/test_metadata_online.py +579 -0
  103. tests/online/test_model_insert_online.py +255 -0
  104. tests/online/test_mysql_driver_validation.py +213 -0
  105. tests/online/test_orm_advanced_features.py +2022 -0
  106. tests/online/test_orm_cte_integration.py +269 -0
  107. tests/online/test_orm_online.py +270 -0
  108. tests/online/test_pinecone_filter.py +708 -0
  109. tests/online/test_pubsub_operations.py +352 -0
  110. tests/online/test_query_methods.py +225 -0
  111. tests/online/test_query_update_online.py +433 -0
  112. tests/online/test_search_vector_index.py +557 -0
  113. tests/online/test_simple_fulltext_online.py +915 -0
  114. tests/online/test_snapshot_comprehensive.py +998 -0
  115. tests/online/test_sqlalchemy_engine_integration.py +336 -0
  116. tests/online/test_sqlalchemy_integration.py +425 -0
  117. tests/online/test_transaction_contexts.py +1219 -0
  118. tests/online/test_transaction_insert_methods.py +356 -0
  119. tests/online/test_transaction_query_methods.py +288 -0
  120. tests/online/test_unified_filter_online.py +529 -0
  121. tests/online/test_vector_comprehensive.py +706 -0
  122. tests/online/test_version_management.py +291 -0
@@ -0,0 +1,286 @@
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
+ Connection hooks for MatrixOne clients
17
+ """
18
+
19
+ from enum import Enum
20
+ from typing import Callable, List, Optional, Union
21
+ from sqlalchemy import event
22
+ from sqlalchemy.engine import Engine
23
+ from sqlalchemy.ext.asyncio import AsyncEngine
24
+
25
+
26
+ class ConnectionAction(Enum):
27
+ """Predefined connection actions that can be executed after connecting"""
28
+
29
+ ENABLE_IVF = "enable_ivf"
30
+ ENABLE_HNSW = "enable_hnsw"
31
+ ENABLE_FULLTEXT = "enable_fulltext"
32
+ ENABLE_VECTOR = "enable_vector" # Enables both IVF and HNSW
33
+ ENABLE_ALL = "enable_all" # Enables all features
34
+
35
+
36
+ class ConnectionHook:
37
+ """Connection hook that executes actions after successful connection"""
38
+
39
+ def __init__(self, actions: Optional[List[Union[ConnectionAction, str]]] = None, custom_hook: Optional[Callable] = None):
40
+ """
41
+ Initialize connection hook
42
+
43
+ Args:
44
+
45
+ actions: List of predefined actions to execute
46
+ custom_hook: Custom callback function to execute
47
+ """
48
+ self.actions = actions or []
49
+ self.custom_hook = custom_hook
50
+ self._action_handlers = {
51
+ ConnectionAction.ENABLE_IVF: self._enable_ivf_with_connection,
52
+ ConnectionAction.ENABLE_HNSW: self._enable_hnsw_with_connection,
53
+ ConnectionAction.ENABLE_FULLTEXT: self._enable_fulltext_with_connection,
54
+ ConnectionAction.ENABLE_VECTOR: self._enable_vector_with_connection,
55
+ ConnectionAction.ENABLE_ALL: self._enable_all_with_connection,
56
+ }
57
+ self._client_ref = None # Will be set when hook is attached to a client
58
+ self._executed_connections = set() # Track which connections have executed the hook
59
+
60
+ def set_client(self, client):
61
+ """Set the client reference for this hook"""
62
+ self._client_ref = client
63
+
64
+ def attach_to_engine(self, engine: Union[Engine, AsyncEngine]):
65
+ """Attach this hook to a SQLAlchemy engine to listen for connection events"""
66
+ if isinstance(engine, AsyncEngine):
67
+ # For async engines, listen to both connect and before_cursor_execute events
68
+ event.listen(engine.sync_engine, "connect", self._on_connect_sync)
69
+ event.listen(engine.sync_engine, "before_cursor_execute", self._on_before_cursor_execute)
70
+ if hasattr(self._client_ref, 'logger'):
71
+ self._client_ref.logger.info("Attached connection hook to async engine")
72
+ else:
73
+ # For sync engines, listen to both connect and before_cursor_execute events
74
+ event.listen(engine, "connect", self._on_connect_sync)
75
+ event.listen(engine, "before_cursor_execute", self._on_before_cursor_execute)
76
+ if hasattr(self._client_ref, 'logger'):
77
+ self._client_ref.logger.info("Attached connection hook to sync engine")
78
+
79
+ def _on_connect_sync(self, dbapi_connection, connection_record):
80
+ """SQLAlchemy event handler for new connections (sync)"""
81
+ if self._client_ref:
82
+ # Get connection ID to track which connections have executed the hook
83
+ conn_id = id(dbapi_connection)
84
+ if conn_id not in self._executed_connections:
85
+ try:
86
+ # Log that the hook is being executed
87
+ if hasattr(self._client_ref, 'logger'):
88
+ self._client_ref.logger.info(f"Executing connection hook on new connection {conn_id}")
89
+ # Pass the connection to avoid creating new connections
90
+ self.execute_sync_with_connection(self._client_ref, dbapi_connection)
91
+ self._executed_connections.add(conn_id)
92
+ except Exception as e:
93
+ # Log error but don't fail the connection
94
+ if hasattr(self._client_ref, 'logger'):
95
+ self._client_ref.logger.warning(f"Connection hook execution failed: {e}")
96
+
97
+ def _on_before_cursor_execute(self, conn, cursor, statement, parameters, context, executemany):
98
+ """SQLAlchemy event handler for before cursor execute"""
99
+ if self._client_ref:
100
+ # Get connection ID to track which connections have executed the hook
101
+ conn_id = id(conn.connection)
102
+ if conn_id not in self._executed_connections:
103
+ try:
104
+ # Log that the hook is being executed
105
+ if hasattr(self._client_ref, 'logger'):
106
+ self._client_ref.logger.info(f"Executing connection hook on connection {conn_id}")
107
+ # Use the connection to avoid creating new connections
108
+ self.execute_sync_with_connection(self._client_ref, conn.connection)
109
+ self._executed_connections.add(conn_id)
110
+ except Exception as e:
111
+ # Log error but don't fail the query
112
+ if hasattr(self._client_ref, 'logger'):
113
+ self._client_ref.logger.warning(f"Connection hook execution failed: {e}")
114
+
115
+ async def execute_async(self, client) -> None:
116
+ """Execute hook actions asynchronously (for immediate execution)"""
117
+ try:
118
+ # For immediate execution, we need to get a connection from the client
119
+ # This is a fallback for when we don't have a specific connection
120
+ if hasattr(client, '_engine') and client._engine:
121
+ async with client._engine.begin() as conn:
122
+ # For async connections, use get_raw_connection() method
123
+ raw_conn = await conn.get_raw_connection()
124
+ await self.execute_async_with_connection(client, raw_conn)
125
+ else:
126
+ client.logger.warning("No engine available for connection hook execution")
127
+
128
+ except Exception as e:
129
+ client.logger.warning(f"Connection hook execution failed: {e}")
130
+
131
+ async def execute_async_with_connection(self, client, dbapi_connection) -> None:
132
+ """Execute hook actions asynchronously using the provided connection"""
133
+ try:
134
+ # Execute predefined actions with connection
135
+ for action in self.actions:
136
+ if isinstance(action, str):
137
+ action = ConnectionAction(action)
138
+
139
+ if action in self._action_handlers:
140
+ # For async, we still use the sync methods since they work with direct connection
141
+ self._action_handlers[action](client, dbapi_connection)
142
+ else:
143
+ client.logger.warning(f"Unknown connection action: {action}")
144
+
145
+ # Execute custom hook if provided
146
+ if self.custom_hook:
147
+ if hasattr(self.custom_hook, '__call__'):
148
+ # Check if it's an async function
149
+ if hasattr(self.custom_hook, '__code__') and self.custom_hook.__code__.co_flags & 0x80: # Check if async
150
+ await self.custom_hook(client)
151
+ else:
152
+ # Try to call it as sync, but handle potential async functions
153
+ try:
154
+ result = self.custom_hook(client)
155
+ # If it returns a coroutine, await it
156
+ if hasattr(result, '__await__'):
157
+ await result
158
+ except TypeError as e:
159
+ if "object NoneType can't be used in 'await' expression" in str(e):
160
+ # This is likely an async function being called without await
161
+ client.logger.warning("Custom hook appears to be async but was called synchronously")
162
+ else:
163
+ raise
164
+ else:
165
+ client.logger.warning("Custom hook is not callable")
166
+
167
+ except Exception as e:
168
+ client.logger.warning(f"Connection hook execution failed: {e}")
169
+
170
+ def execute_sync(self, client) -> None:
171
+ """Execute hook actions synchronously (for immediate execution)"""
172
+ try:
173
+ # For immediate execution, we need to get a connection from the client
174
+ # This is a fallback for when we don't have a specific connection
175
+ if hasattr(client, '_engine') and client._engine:
176
+ with client._engine.connect() as conn:
177
+ self.execute_sync_with_connection(client, conn.connection)
178
+ else:
179
+ client.logger.warning("No engine available for connection hook execution")
180
+
181
+ except Exception as e:
182
+ client.logger.warning(f"Connection hook execution failed: {e}")
183
+
184
+ def execute_sync_with_connection(self, client, dbapi_connection) -> None:
185
+ """Execute hook actions synchronously using the provided connection"""
186
+ try:
187
+ # Execute predefined actions with connection
188
+ for action in self.actions:
189
+ if isinstance(action, str):
190
+ action = ConnectionAction(action)
191
+
192
+ if action in self._action_handlers:
193
+ self._action_handlers[action](client, dbapi_connection)
194
+ else:
195
+ client.logger.warning(f"Unknown connection action: {action}")
196
+
197
+ # Execute custom hook if provided
198
+ if self.custom_hook:
199
+ if hasattr(self.custom_hook, '__call__'):
200
+ self.custom_hook(client)
201
+ else:
202
+ client.logger.warning("Custom hook is not callable")
203
+
204
+ except Exception as e:
205
+ client.logger.warning(f"Connection hook execution failed: {e}")
206
+
207
+ def _enable_ivf_with_connection(self, client, dbapi_connection) -> None:
208
+ """Enable IVF vector operations using provided connection"""
209
+ try:
210
+ # Execute SQL directly on the connection to avoid creating new connections
211
+ cursor = dbapi_connection.cursor()
212
+ cursor.execute("SET experimental_ivf_index = 1")
213
+ cursor.close()
214
+ client.logger.info("✓ Enabled IVF vector operations")
215
+ except Exception as e:
216
+ client.logger.warning(f"Failed to enable IVF: {e}")
217
+
218
+ def _enable_hnsw_with_connection(self, client, dbapi_connection) -> None:
219
+ """Enable HNSW vector operations using provided connection"""
220
+ try:
221
+ # Execute SQL directly on the connection to avoid creating new connections
222
+ cursor = dbapi_connection.cursor()
223
+ cursor.execute("SET experimental_hnsw_index = 1")
224
+ cursor.close()
225
+ client.logger.info("✓ Enabled HNSW vector operations")
226
+ except Exception as e:
227
+ client.logger.warning(f"Failed to enable HNSW: {e}")
228
+
229
+ def _enable_fulltext_with_connection(self, client, dbapi_connection) -> None:
230
+ """Enable fulltext search operations using provided connection"""
231
+ try:
232
+ # Execute SQL directly on the connection to avoid creating new connections
233
+ cursor = dbapi_connection.cursor()
234
+ cursor.execute("SET experimental_fulltext_index = 1")
235
+ cursor.close()
236
+ client.logger.info("✓ Enabled fulltext search operations")
237
+ except Exception as e:
238
+ client.logger.warning(f"Failed to enable fulltext: {e}")
239
+
240
+ def _enable_vector_with_connection(self, client, dbapi_connection) -> None:
241
+ """Enable both IVF and HNSW vector operations using provided connection"""
242
+ self._enable_ivf_with_connection(client, dbapi_connection)
243
+ self._enable_hnsw_with_connection(client, dbapi_connection)
244
+
245
+ def _enable_all_with_connection(self, client, dbapi_connection) -> None:
246
+ """Enable all available operations using provided connection"""
247
+ self._enable_vector_with_connection(client, dbapi_connection)
248
+ self._enable_fulltext_with_connection(client, dbapi_connection)
249
+
250
+
251
+ def create_connection_hook(
252
+ actions: Optional[List[Union[ConnectionAction, str]]] = None, custom_hook: Optional[Callable] = None
253
+ ) -> ConnectionHook:
254
+ """
255
+ Create a connection hook with predefined actions and/or custom callback
256
+
257
+ Args:
258
+
259
+ actions: List of predefined actions to execute
260
+ custom_hook: Custom callback function to execute
261
+
262
+ Returns:
263
+
264
+ ConnectionHook: Configured connection hook
265
+
266
+ Examples:
267
+
268
+ # Enable all features
269
+ hook = create_connection_hook([ConnectionAction.ENABLE_ALL])
270
+
271
+ # Enable only vector operations
272
+ hook = create_connection_hook([ConnectionAction.ENABLE_VECTOR])
273
+
274
+ # Custom hook
275
+ def my_hook(client):
276
+ print(f"Connected to {client._connection_params['host']}")
277
+
278
+ hook = create_connection_hook(custom_hook=my_hook)
279
+
280
+ # Mixed actions
281
+ hook = create_connection_hook(
282
+ actions=[ConnectionAction.ENABLE_FULLTEXT],
283
+ custom_hook=my_hook
284
+ )
285
+ """
286
+ return ConnectionHook(actions, custom_hook)
@@ -0,0 +1,89 @@
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
+ MatrixOne SDK Exceptions
17
+ """
18
+
19
+
20
+ class MatrixOneError(Exception):
21
+ """Base exception for all MatrixOne SDK errors"""
22
+
23
+ pass
24
+
25
+
26
+ class ConnectionError(MatrixOneError):
27
+ """Raised when connection to MatrixOne fails"""
28
+
29
+ pass
30
+
31
+
32
+ class QueryError(MatrixOneError):
33
+ """Raised when SQL query execution fails"""
34
+
35
+ pass
36
+
37
+
38
+ class ConfigurationError(MatrixOneError):
39
+ """Raised when configuration is invalid"""
40
+
41
+ pass
42
+
43
+
44
+ class SnapshotError(MatrixOneError):
45
+ """Raised when snapshot operations fail"""
46
+
47
+ pass
48
+
49
+
50
+ class CloneError(MatrixOneError):
51
+ """Raised when clone operations fail"""
52
+
53
+ pass
54
+
55
+
56
+ class MoCtlError(MatrixOneError):
57
+ """Raised when mo_ctl operations fail"""
58
+
59
+ pass
60
+
61
+
62
+ class RestoreError(MatrixOneError):
63
+ """Raised when restore operations fail"""
64
+
65
+ pass
66
+
67
+
68
+ class PitrError(MatrixOneError):
69
+ """Raised when PITR operations fail"""
70
+
71
+ pass
72
+
73
+
74
+ class PubSubError(MatrixOneError):
75
+ """Raised when publish-subscribe operations fail"""
76
+
77
+ pass
78
+
79
+
80
+ class AccountError(MatrixOneError):
81
+ """Raised when account management operations fail"""
82
+
83
+ pass
84
+
85
+
86
+ class VersionError(MatrixOneError):
87
+ """Raised when version compatibility check fails"""
88
+
89
+ pass