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,90 @@
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
+ Configuration for online tests
17
+ """
18
+
19
+ import os
20
+ from typing import Optional, Tuple
21
+
22
+
23
+ class OnlineTestConfig:
24
+ """Configuration for online tests"""
25
+
26
+ def __init__(self):
27
+ # Connection parameters
28
+ self.host = os.getenv('MATRIXONE_HOST', '127.0.0.1')
29
+ self.port = int(os.getenv('MATRIXONE_PORT', '6001'))
30
+ self.user = os.getenv('MATRIXONE_USER', 'root')
31
+ self.password = os.getenv('MATRIXONE_PASSWORD', '111')
32
+ self.database = os.getenv('MATRIXONE_DATABASE', 'test')
33
+
34
+ # Test database names (can be overridden for different test environments)
35
+ self.test_database = os.getenv('MATRIXONE_TEST_DATABASE', self.database)
36
+ self.sys_database = os.getenv('MATRIXONE_SYS_DATABASE', 'sys')
37
+
38
+ # Test account (for multi-tenant scenarios)
39
+ self.test_account = os.getenv('MATRIXONE_TEST_ACCOUNT', 'sys')
40
+
41
+ # Test table prefixes (to avoid conflicts)
42
+ self.table_prefix = os.getenv('MATRIXONE_TABLE_PREFIX', 'test_')
43
+
44
+ # Snapshot and backup settings
45
+ self.snapshot_prefix = os.getenv('MATRIXONE_SNAPSHOT_PREFIX', 'testsnapshot')
46
+
47
+ # Vector test settings
48
+ self.vector_dimensions = int(os.getenv('MATRIXONE_VECTOR_DIMENSIONS', '64'))
49
+ self.vector_test_data_size = int(os.getenv('MATRIXONE_VECTOR_TEST_SIZE', '100'))
50
+
51
+ def get_connection_params(self) -> Tuple[str, int, str, str, str]:
52
+ """Get connection parameters as tuple"""
53
+ return self.host, self.port, self.user, self.password, self.database
54
+
55
+ def get_test_database(self) -> str:
56
+ """Get test database name"""
57
+ return self.test_database
58
+
59
+ def get_sys_database(self) -> str:
60
+ """Get system database name"""
61
+ return self.sys_database
62
+
63
+ def get_test_account(self) -> str:
64
+ """Get test account name"""
65
+ return self.test_account
66
+
67
+ def get_table_name(self, base_name: str) -> str:
68
+ """Get full table name with prefix"""
69
+ return f"{self.table_prefix}{base_name}"
70
+
71
+ def get_snapshot_name(self, base_name: str = None) -> str:
72
+ """Get snapshot name with prefix"""
73
+ if base_name:
74
+ return f"{self.snapshot_prefix}_{base_name}"
75
+ return self.snapshot_prefix
76
+
77
+ def get_vector_dimensions(self) -> int:
78
+ """Get vector dimensions for tests"""
79
+ return self.vector_dimensions
80
+
81
+ def get_vector_test_data_size(self) -> int:
82
+ """Get vector test data size"""
83
+ return self.vector_test_data_size
84
+
85
+ def __str__(self):
86
+ return f"OnlineTestConfig(host={self.host}, port={self.port}, user={self.user}, database={self.database}, test_database={self.test_database})"
87
+
88
+
89
+ # Global configuration instance
90
+ online_config = OnlineTestConfig()
@@ -0,0 +1,123 @@
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
+ Test configuration validation for online tests
17
+ """
18
+
19
+ import pytest
20
+ import os
21
+ from .test_config import online_config
22
+
23
+
24
+ class TestConfigValidation:
25
+ """Test that the configuration system works correctly"""
26
+
27
+ def test_config_has_all_required_attributes(self):
28
+ """Test that config has all required attributes"""
29
+ assert hasattr(online_config, 'host')
30
+ assert hasattr(online_config, 'port')
31
+ assert hasattr(online_config, 'user')
32
+ assert hasattr(online_config, 'password')
33
+ assert hasattr(online_config, 'database')
34
+ assert hasattr(online_config, 'test_database')
35
+ assert hasattr(online_config, 'sys_database')
36
+ assert hasattr(online_config, 'test_account')
37
+ assert hasattr(online_config, 'table_prefix')
38
+ assert hasattr(online_config, 'snapshot_prefix')
39
+ assert hasattr(online_config, 'vector_dimensions')
40
+ assert hasattr(online_config, 'vector_test_data_size')
41
+
42
+ def test_config_methods_work(self):
43
+ """Test that config methods work correctly"""
44
+ # Test connection params
45
+ host, port, user, password, database = online_config.get_connection_params()
46
+ assert isinstance(host, str)
47
+ assert isinstance(port, int)
48
+ assert isinstance(user, str)
49
+ assert isinstance(password, str)
50
+ assert isinstance(database, str)
51
+
52
+ # Test database names
53
+ test_db = online_config.get_test_database()
54
+ sys_db = online_config.get_sys_database()
55
+ assert isinstance(test_db, str)
56
+ assert isinstance(sys_db, str)
57
+
58
+ # Test account
59
+ account = online_config.get_test_account()
60
+ assert isinstance(account, str)
61
+
62
+ # Test table name generation
63
+ table_name = online_config.get_table_name("test_table")
64
+ assert table_name == f"{online_config.table_prefix}test_table"
65
+
66
+ # Test snapshot name generation
67
+ snapshot_name = online_config.get_snapshot_name("test_snapshot")
68
+ assert snapshot_name == f"{online_config.snapshot_prefix}_test_snapshot"
69
+
70
+ # Test vector settings
71
+ dimensions = online_config.get_vector_dimensions()
72
+ data_size = online_config.get_vector_test_data_size()
73
+ assert isinstance(dimensions, int)
74
+ assert isinstance(data_size, int)
75
+
76
+ def test_config_respects_environment_variables(self):
77
+ """Test that config respects environment variables"""
78
+ # Save original values
79
+ original_host = online_config.host
80
+ original_port = online_config.port
81
+ original_user = online_config.user
82
+ original_password = online_config.password
83
+ original_database = online_config.database
84
+
85
+ try:
86
+ # Set test environment variables
87
+ os.environ['MATRIXONE_HOST'] = 'test_host'
88
+ os.environ['MATRIXONE_PORT'] = '9999'
89
+ os.environ['MATRIXONE_USER'] = 'test_user'
90
+ os.environ['MATRIXONE_PASSWORD'] = 'test_password'
91
+ os.environ['MATRIXONE_DATABASE'] = 'test_db'
92
+
93
+ # Create new config instance to test env vars
94
+ from .test_config import OnlineTestConfig
95
+
96
+ test_config = OnlineTestConfig()
97
+
98
+ assert test_config.host == 'test_host'
99
+ assert test_config.port == 9999
100
+ assert test_config.user == 'test_user'
101
+ assert test_config.password == 'test_password'
102
+ assert test_config.database == 'test_db'
103
+
104
+ finally:
105
+ # Clean up environment variables
106
+ for key in [
107
+ 'MATRIXONE_HOST',
108
+ 'MATRIXONE_PORT',
109
+ 'MATRIXONE_USER',
110
+ 'MATRIXONE_PASSWORD',
111
+ 'MATRIXONE_DATABASE',
112
+ ]:
113
+ if key in os.environ:
114
+ del os.environ[key]
115
+
116
+ def test_config_string_representation(self):
117
+ """Test config string representation"""
118
+ config_str = str(online_config)
119
+ assert 'OnlineTestConfig' in config_str
120
+ assert online_config.host in config_str
121
+ assert str(online_config.port) in config_str
122
+ assert online_config.user in config_str
123
+ assert online_config.database in config_str
@@ -0,0 +1,217 @@
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
+ #!/usr/bin/env python3
16
+ """
17
+ Online tests for the new connection hooks functionality using SQLAlchemy events
18
+ """
19
+
20
+ import pytest
21
+ from matrixone import Client, AsyncClient
22
+ from matrixone.connection_hooks import ConnectionAction, create_connection_hook
23
+
24
+
25
+ class TestNewConnectionHooksOnline:
26
+ """Test the new connection hooks implementation with SQLAlchemy events"""
27
+
28
+ def test_sync_client_connection_hook_executes_on_each_connection(self, test_client):
29
+ """Test that connection hook executes on each new connection"""
30
+ # Create a new client instance
31
+ client = Client()
32
+
33
+ # Track how many times the hook is called
34
+ hook_call_count = 0
35
+
36
+ def count_hook_calls(client):
37
+ nonlocal hook_call_count
38
+ hook_call_count += 1
39
+ print(f"Hook called {hook_call_count} times")
40
+
41
+ # Connect with the hook
42
+ client.connect(
43
+ host=test_client._connection_params['host'],
44
+ port=test_client._connection_params['port'],
45
+ user=test_client._connection_params['user'],
46
+ password=test_client._connection_params['password'],
47
+ database=test_client._connection_params['database'],
48
+ on_connect=count_hook_calls,
49
+ )
50
+
51
+ # The hook should be called once for the initial connection
52
+ assert hook_call_count >= 1
53
+
54
+ # Force a new connection by executing a query
55
+ # This should trigger the hook again
56
+ result = client.execute("SELECT 1")
57
+ assert len(result.rows) == 1
58
+
59
+ # The hook should be called again for the new connection
60
+ assert hook_call_count >= 2
61
+
62
+ # Clean up
63
+ client.disconnect()
64
+
65
+ def test_sync_client_with_predefined_actions(self, test_client):
66
+ """Test sync client with predefined actions"""
67
+ client = Client()
68
+
69
+ # Connect with predefined actions
70
+ client.connect(
71
+ host=test_client._connection_params['host'],
72
+ port=test_client._connection_params['port'],
73
+ user=test_client._connection_params['user'],
74
+ password=test_client._connection_params['password'],
75
+ database=test_client._connection_params['database'],
76
+ on_connect=[ConnectionAction.ENABLE_FULLTEXT],
77
+ )
78
+
79
+ assert client.connected()
80
+
81
+ # Test that we can use fulltext operations
82
+ # (This would fail if fulltext wasn't enabled)
83
+ try:
84
+ # Create a simple table and test fulltext
85
+ client.execute("CREATE TABLE IF NOT EXISTS test_hook_table (id INT, content TEXT)")
86
+ client.execute("INSERT INTO test_hook_table VALUES (1, 'test content')")
87
+
88
+ # This should work if fulltext is enabled
89
+ result = (
90
+ client.query("test_hook_table.content")
91
+ .filter("MATCH(content) AGAINST('test' IN NATURAL LANGUAGE MODE)")
92
+ .execute()
93
+ )
94
+
95
+ # Clean up
96
+ client.execute("DROP TABLE IF EXISTS test_hook_table")
97
+
98
+ except Exception as e:
99
+ # If fulltext is not available, that's okay for this test
100
+ print(f"Fulltext test skipped: {e}")
101
+
102
+ # Clean up
103
+ client.disconnect()
104
+
105
+ @pytest.mark.asyncio
106
+ async def test_async_client_connection_hook_executes_on_each_connection(self, test_async_client):
107
+ """Test that async connection hook executes on each new connection"""
108
+ # Create a new client instance
109
+ client = AsyncClient()
110
+
111
+ # Track how many times the hook is called
112
+ hook_call_count = 0
113
+
114
+ def count_hook_calls(client):
115
+ nonlocal hook_call_count
116
+ hook_call_count += 1
117
+ print(f"Async hook called {hook_call_count} times")
118
+
119
+ # Connect with the hook
120
+ await client.connect(
121
+ host=test_async_client._connection_params['host'],
122
+ port=test_async_client._connection_params['port'],
123
+ user=test_async_client._connection_params['user'],
124
+ password=test_async_client._connection_params['password'],
125
+ database=test_async_client._connection_params['database'],
126
+ on_connect=count_hook_calls,
127
+ )
128
+
129
+ # The hook should be called once for the initial connection
130
+ assert hook_call_count >= 1
131
+
132
+ # Force a new connection by executing a query
133
+ # This should trigger the hook again
134
+ result = await client.execute("SELECT 1")
135
+ assert len(result.rows) == 1
136
+
137
+ # The hook should be called again for the new connection
138
+ assert hook_call_count >= 2
139
+
140
+ # Clean up
141
+ await client.disconnect()
142
+
143
+ @pytest.mark.asyncio
144
+ async def test_async_client_with_predefined_actions(self, test_async_client):
145
+ """Test async client with predefined actions"""
146
+ client = AsyncClient()
147
+
148
+ # Connect with predefined actions
149
+ await client.connect(
150
+ host=test_async_client._connection_params['host'],
151
+ port=test_async_client._connection_params['port'],
152
+ user=test_async_client._connection_params['user'],
153
+ password=test_async_client._connection_params['password'],
154
+ database=test_async_client._connection_params['database'],
155
+ on_connect=[ConnectionAction.ENABLE_FULLTEXT],
156
+ )
157
+
158
+ assert client.connected()
159
+
160
+ # Test that we can use fulltext operations
161
+ try:
162
+ # Create a simple table and test fulltext
163
+ await client.execute("CREATE TABLE IF NOT EXISTS test_async_hook_table (id INT, content TEXT)")
164
+ await client.execute("INSERT INTO test_async_hook_table VALUES (1, 'test content')")
165
+
166
+ # This should work if fulltext is enabled
167
+ result = (
168
+ await client.query("test_async_hook_table.content")
169
+ .filter("MATCH(content) AGAINST('test' IN NATURAL LANGUAGE MODE)")
170
+ .execute()
171
+ )
172
+
173
+ # Clean up
174
+ await client.execute("DROP TABLE IF EXISTS test_async_hook_table")
175
+
176
+ except Exception as e:
177
+ # If fulltext is not available, that's okay for this test
178
+ print(f"Async fulltext test skipped: {e}")
179
+
180
+ # Clean up
181
+ await client.disconnect()
182
+
183
+ def test_connection_hook_with_multiple_actions(self, test_client):
184
+ """Test connection hook with multiple actions"""
185
+ client = Client()
186
+
187
+ # Track which actions were executed
188
+ executed_actions = []
189
+
190
+ def track_actions(client):
191
+ executed_actions.append("custom_hook")
192
+
193
+ # Create hook with multiple actions
194
+ hook = create_connection_hook(
195
+ actions=[ConnectionAction.ENABLE_FULLTEXT, ConnectionAction.ENABLE_IVF], custom_hook=track_actions
196
+ )
197
+
198
+ # Connect with the hook
199
+ client.connect(
200
+ host=test_client._connection_params['host'],
201
+ port=test_client._connection_params['port'],
202
+ user=test_client._connection_params['user'],
203
+ password=test_client._connection_params['password'],
204
+ database=test_client._connection_params['database'],
205
+ on_connect=hook,
206
+ )
207
+
208
+ assert client.connected()
209
+
210
+ # Force a new connection to trigger the hook
211
+ client.execute("SELECT 1")
212
+
213
+ # The custom hook should have been called
214
+ assert "custom_hook" in executed_actions
215
+
216
+ # Clean up
217
+ client.disconnect()