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.
- matrixone/__init__.py +155 -0
- matrixone/account.py +723 -0
- matrixone/async_client.py +3913 -0
- matrixone/async_metadata_manager.py +311 -0
- matrixone/async_orm.py +123 -0
- matrixone/async_vector_index_manager.py +633 -0
- matrixone/base_client.py +208 -0
- matrixone/client.py +4672 -0
- matrixone/config.py +452 -0
- matrixone/connection_hooks.py +286 -0
- matrixone/exceptions.py +89 -0
- matrixone/logger.py +782 -0
- matrixone/metadata.py +820 -0
- matrixone/moctl.py +219 -0
- matrixone/orm.py +2277 -0
- matrixone/pitr.py +646 -0
- matrixone/pubsub.py +771 -0
- matrixone/restore.py +411 -0
- matrixone/search_vector_index.py +1176 -0
- matrixone/snapshot.py +550 -0
- matrixone/sql_builder.py +844 -0
- matrixone/sqlalchemy_ext/__init__.py +161 -0
- matrixone/sqlalchemy_ext/adapters.py +163 -0
- matrixone/sqlalchemy_ext/dialect.py +534 -0
- matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
- matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
- matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
- matrixone/sqlalchemy_ext/ivf_config.py +252 -0
- matrixone/sqlalchemy_ext/table_builder.py +351 -0
- matrixone/sqlalchemy_ext/vector_index.py +1721 -0
- matrixone/sqlalchemy_ext/vector_type.py +948 -0
- matrixone/version.py +580 -0
- matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
- matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
- matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
- matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
- matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
- matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +19 -0
- tests/offline/__init__.py +20 -0
- tests/offline/conftest.py +77 -0
- tests/offline/test_account.py +703 -0
- tests/offline/test_async_client_query_comprehensive.py +1218 -0
- tests/offline/test_basic.py +54 -0
- tests/offline/test_case_sensitivity.py +227 -0
- tests/offline/test_connection_hooks_offline.py +287 -0
- tests/offline/test_dialect_schema_handling.py +609 -0
- tests/offline/test_explain_methods.py +346 -0
- tests/offline/test_filter_logical_in.py +237 -0
- tests/offline/test_fulltext_search_comprehensive.py +795 -0
- tests/offline/test_ivf_config.py +249 -0
- tests/offline/test_join_methods.py +281 -0
- tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
- tests/offline/test_logical_in_method.py +237 -0
- tests/offline/test_matrixone_version_parsing.py +264 -0
- tests/offline/test_metadata_offline.py +557 -0
- tests/offline/test_moctl.py +300 -0
- tests/offline/test_moctl_simple.py +251 -0
- tests/offline/test_model_support_offline.py +359 -0
- tests/offline/test_model_support_simple.py +225 -0
- tests/offline/test_pinecone_filter_offline.py +377 -0
- tests/offline/test_pitr.py +585 -0
- tests/offline/test_pubsub.py +712 -0
- tests/offline/test_query_update.py +283 -0
- tests/offline/test_restore.py +445 -0
- tests/offline/test_snapshot_comprehensive.py +384 -0
- tests/offline/test_sql_escaping_edge_cases.py +551 -0
- tests/offline/test_sqlalchemy_integration.py +382 -0
- tests/offline/test_sqlalchemy_vector_integration.py +434 -0
- tests/offline/test_table_builder.py +198 -0
- tests/offline/test_unified_filter.py +398 -0
- tests/offline/test_unified_transaction.py +495 -0
- tests/offline/test_vector_index.py +238 -0
- tests/offline/test_vector_operations.py +688 -0
- tests/offline/test_vector_type.py +174 -0
- tests/offline/test_version_core.py +328 -0
- tests/offline/test_version_management.py +372 -0
- tests/offline/test_version_standalone.py +652 -0
- tests/online/__init__.py +20 -0
- tests/online/conftest.py +216 -0
- tests/online/test_account_management.py +194 -0
- tests/online/test_advanced_features.py +344 -0
- tests/online/test_async_client_interfaces.py +330 -0
- tests/online/test_async_client_online.py +285 -0
- tests/online/test_async_model_insert_online.py +293 -0
- tests/online/test_async_orm_online.py +300 -0
- tests/online/test_async_simple_query_online.py +802 -0
- tests/online/test_async_transaction_simple_query.py +300 -0
- tests/online/test_basic_connection.py +130 -0
- tests/online/test_client_online.py +238 -0
- tests/online/test_config.py +90 -0
- tests/online/test_config_validation.py +123 -0
- tests/online/test_connection_hooks_new_online.py +217 -0
- tests/online/test_dialect_schema_handling_online.py +331 -0
- tests/online/test_filter_logical_in_online.py +374 -0
- tests/online/test_fulltext_comprehensive.py +1773 -0
- tests/online/test_fulltext_label_online.py +433 -0
- tests/online/test_fulltext_search_online.py +842 -0
- tests/online/test_ivf_stats_online.py +506 -0
- tests/online/test_logger_integration.py +311 -0
- tests/online/test_matrixone_query_orm.py +540 -0
- tests/online/test_metadata_online.py +579 -0
- tests/online/test_model_insert_online.py +255 -0
- tests/online/test_mysql_driver_validation.py +213 -0
- tests/online/test_orm_advanced_features.py +2022 -0
- tests/online/test_orm_cte_integration.py +269 -0
- tests/online/test_orm_online.py +270 -0
- tests/online/test_pinecone_filter.py +708 -0
- tests/online/test_pubsub_operations.py +352 -0
- tests/online/test_query_methods.py +225 -0
- tests/online/test_query_update_online.py +433 -0
- tests/online/test_search_vector_index.py +557 -0
- tests/online/test_simple_fulltext_online.py +915 -0
- tests/online/test_snapshot_comprehensive.py +998 -0
- tests/online/test_sqlalchemy_engine_integration.py +336 -0
- tests/online/test_sqlalchemy_integration.py +425 -0
- tests/online/test_transaction_contexts.py +1219 -0
- tests/online/test_transaction_insert_methods.py +356 -0
- tests/online/test_transaction_query_methods.py +288 -0
- tests/online/test_unified_filter_online.py +529 -0
- tests/online/test_vector_comprehensive.py +706 -0
- tests/online/test_version_management.py +291 -0
@@ -0,0 +1,283 @@
|
|
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
|
+
Offline tests for query update functionality
|
17
|
+
"""
|
18
|
+
|
19
|
+
import pytest
|
20
|
+
import os
|
21
|
+
import sys
|
22
|
+
from unittest.mock import Mock, MagicMock
|
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.orm import BaseMatrixOneQuery, MatrixOneQuery
|
28
|
+
from sqlalchemy import func
|
29
|
+
|
30
|
+
|
31
|
+
class MockUser:
|
32
|
+
"""Mock SQLAlchemy model for testing"""
|
33
|
+
|
34
|
+
__tablename__ = "users"
|
35
|
+
__name__ = "MockUser" # Add __name__ attribute
|
36
|
+
|
37
|
+
def __init__(self):
|
38
|
+
self.id = Mock()
|
39
|
+
self.name = Mock()
|
40
|
+
self.email = Mock()
|
41
|
+
self.age = Mock()
|
42
|
+
self.login_count = Mock()
|
43
|
+
self.last_login = Mock()
|
44
|
+
self.status = Mock()
|
45
|
+
|
46
|
+
|
47
|
+
class TestQueryUpdate:
|
48
|
+
"""Test query update functionality"""
|
49
|
+
|
50
|
+
def setup_method(self):
|
51
|
+
"""Set up test fixtures"""
|
52
|
+
self.mock_client = Mock()
|
53
|
+
self.mock_client.execute.return_value = Mock()
|
54
|
+
self.mock_client.execute.return_value.rows = []
|
55
|
+
|
56
|
+
# Create mock user model
|
57
|
+
self.user_model = MockUser()
|
58
|
+
|
59
|
+
# Create query instances
|
60
|
+
self.base_query = BaseMatrixOneQuery(self.user_model, self.mock_client)
|
61
|
+
self.matrixone_query = MatrixOneQuery(self.user_model, self.mock_client)
|
62
|
+
|
63
|
+
def test_simple_update(self):
|
64
|
+
"""Test simple update with key-value pairs"""
|
65
|
+
query = self.base_query.update(name="New Name", email="new@example.com")
|
66
|
+
|
67
|
+
# Check that query type is set to UPDATE
|
68
|
+
assert query._query_type == "UPDATE"
|
69
|
+
|
70
|
+
# Check that update columns are set correctly
|
71
|
+
assert "name = ?" in query._update_set_columns
|
72
|
+
assert "email = ?" in query._update_set_columns
|
73
|
+
|
74
|
+
# Check that update values are set correctly
|
75
|
+
assert "New Name" in query._update_set_values
|
76
|
+
assert "new@example.com" in query._update_set_values
|
77
|
+
|
78
|
+
def test_update_with_filter(self):
|
79
|
+
"""Test update with filter conditions"""
|
80
|
+
query = self.base_query.update(status="inactive").filter("age > ?", 65)
|
81
|
+
|
82
|
+
# Check that query type is set to UPDATE
|
83
|
+
assert query._query_type == "UPDATE"
|
84
|
+
|
85
|
+
# Check that update columns are set
|
86
|
+
assert "status = ?" in query._update_set_columns
|
87
|
+
assert "inactive" in query._update_set_values
|
88
|
+
|
89
|
+
# Check that filter conditions are set
|
90
|
+
assert "age > 65" in query._where_conditions
|
91
|
+
|
92
|
+
def test_update_with_sqlalchemy_expressions(self):
|
93
|
+
"""Test update with SQLAlchemy expressions"""
|
94
|
+
# Mock SQLAlchemy expression
|
95
|
+
mock_expression = Mock()
|
96
|
+
mock_expression.compile.return_value = Mock()
|
97
|
+
mock_expression.compile.return_value.__str__ = Mock(return_value="NOW()")
|
98
|
+
|
99
|
+
query = self.base_query.update(last_login=mock_expression)
|
100
|
+
|
101
|
+
# Check that query type is set to UPDATE
|
102
|
+
assert query._query_type == "UPDATE"
|
103
|
+
|
104
|
+
# Check that SQLAlchemy expression is handled correctly
|
105
|
+
assert "last_login = NOW()" in query._update_set_columns
|
106
|
+
|
107
|
+
def test_update_with_complex_expressions(self):
|
108
|
+
"""Test update with complex SQLAlchemy expressions"""
|
109
|
+
# Mock complex expression
|
110
|
+
mock_expression = Mock()
|
111
|
+
mock_expression.compile.return_value = Mock()
|
112
|
+
mock_expression.compile.return_value.__str__ = Mock(return_value="login_count + 1")
|
113
|
+
|
114
|
+
query = self.base_query.update(login_count=mock_expression)
|
115
|
+
|
116
|
+
# Check that query type is set to UPDATE
|
117
|
+
assert query._query_type == "UPDATE"
|
118
|
+
|
119
|
+
# Check that complex expression is handled correctly
|
120
|
+
assert "login_count = login_count + 1" in query._update_set_columns
|
121
|
+
|
122
|
+
def test_update_build_sql_simple(self):
|
123
|
+
"""Test building UPDATE SQL with simple values"""
|
124
|
+
query = self.base_query.update(name="New Name", email="new@example.com").filter("id = ?", 1)
|
125
|
+
|
126
|
+
sql, params = query._build_update_sql()
|
127
|
+
|
128
|
+
# Check SQL structure
|
129
|
+
assert "UPDATE mockuser SET" in sql
|
130
|
+
assert "name = ?" in sql
|
131
|
+
assert "email = ?" in sql
|
132
|
+
assert "WHERE id = 1" in sql
|
133
|
+
|
134
|
+
# Check parameters
|
135
|
+
assert "New Name" in params
|
136
|
+
assert "new@example.com" in params
|
137
|
+
|
138
|
+
def test_update_build_sql_with_expressions(self):
|
139
|
+
"""Test building UPDATE SQL with SQLAlchemy expressions"""
|
140
|
+
# Mock SQLAlchemy expression
|
141
|
+
mock_expression = Mock()
|
142
|
+
mock_compiled = Mock()
|
143
|
+
mock_compiled.__str__ = Mock(return_value="NOW()")
|
144
|
+
mock_expression.compile.return_value = mock_compiled
|
145
|
+
|
146
|
+
query = self.base_query.update(last_login=mock_expression, status="active").filter("id = ?", 1)
|
147
|
+
|
148
|
+
sql, params = query._build_update_sql()
|
149
|
+
|
150
|
+
# Check SQL structure
|
151
|
+
assert "UPDATE mockuser SET" in sql
|
152
|
+
assert "last_login = NOW()" in sql
|
153
|
+
assert "status = ?" in sql
|
154
|
+
assert "WHERE id = 1" in sql
|
155
|
+
|
156
|
+
# Check parameters
|
157
|
+
assert "active" in params
|
158
|
+
|
159
|
+
def test_update_execute(self):
|
160
|
+
"""Test executing UPDATE query"""
|
161
|
+
query = self.base_query.update(name="New Name").filter("id = ?", 1)
|
162
|
+
|
163
|
+
# Mock the _execute method
|
164
|
+
query._execute = Mock(return_value=Mock())
|
165
|
+
|
166
|
+
# Use MatrixOneQuery which has execute method
|
167
|
+
matrixone_query = MatrixOneQuery(self.user_model, self.mock_client)
|
168
|
+
matrixone_query._query_type = "UPDATE"
|
169
|
+
matrixone_query._update_set_columns = ["name = ?"]
|
170
|
+
matrixone_query._update_set_values = ["New Name"]
|
171
|
+
matrixone_query._where_conditions = ["id = 1"]
|
172
|
+
matrixone_query._where_params = []
|
173
|
+
matrixone_query._table_name = "mockuser"
|
174
|
+
matrixone_query._execute = Mock(return_value=Mock())
|
175
|
+
|
176
|
+
result = matrixone_query.execute()
|
177
|
+
|
178
|
+
# Check that _execute was called
|
179
|
+
matrixone_query._execute.assert_called_once()
|
180
|
+
|
181
|
+
# Check that _build_update_sql was called
|
182
|
+
sql, params = matrixone_query._execute.call_args[0]
|
183
|
+
assert "UPDATE mockuser SET" in sql
|
184
|
+
assert "name = ?" in sql
|
185
|
+
assert "WHERE id = 1" in sql
|
186
|
+
|
187
|
+
def test_update_no_set_clauses_error(self):
|
188
|
+
"""Test that ValueError is raised when no SET clauses are provided"""
|
189
|
+
query = self.base_query.filter("id = ?", 1)
|
190
|
+
|
191
|
+
with pytest.raises(ValueError, match="No SET clauses provided for UPDATE"):
|
192
|
+
query._build_update_sql()
|
193
|
+
|
194
|
+
def test_update_with_multiple_filters(self):
|
195
|
+
"""Test update with multiple filter conditions"""
|
196
|
+
query = self.base_query.update(status="inactive").filter("age > ?", 65).filter("last_login < ?", "2023-01-01")
|
197
|
+
|
198
|
+
# Check that query type is set to UPDATE
|
199
|
+
assert query._query_type == "UPDATE"
|
200
|
+
|
201
|
+
# Check that update columns are set
|
202
|
+
assert "status = ?" in query._update_set_columns
|
203
|
+
assert "inactive" in query._update_set_values
|
204
|
+
|
205
|
+
# Check that multiple filter conditions are set
|
206
|
+
assert "age > 65" in query._where_conditions
|
207
|
+
assert "last_login < '2023-01-01'" in query._where_conditions
|
208
|
+
|
209
|
+
def test_update_with_sqlalchemy_quoted_columns(self):
|
210
|
+
"""Test update with SQLAlchemy quoted column names"""
|
211
|
+
# Mock SQLAlchemy expression with quoted columns
|
212
|
+
mock_expression = Mock()
|
213
|
+
mock_expression.compile.return_value = Mock()
|
214
|
+
mock_expression.compile.return_value.__str__ = Mock(return_value="COUNT('id')")
|
215
|
+
|
216
|
+
query = self.base_query.update(user_count=mock_expression)
|
217
|
+
|
218
|
+
# Check that quoted columns are fixed
|
219
|
+
assert "user_count = COUNT(id)" in query._update_set_columns
|
220
|
+
|
221
|
+
def test_matrixone_query_update(self):
|
222
|
+
"""Test MatrixOneQuery update functionality"""
|
223
|
+
query = self.matrixone_query.update(name="New Name", email="new@example.com").filter("id = ?", 1)
|
224
|
+
|
225
|
+
# Check that query type is set to UPDATE
|
226
|
+
assert query._query_type == "UPDATE"
|
227
|
+
|
228
|
+
# Check that update columns are set correctly
|
229
|
+
assert "name = ?" in query._update_set_columns
|
230
|
+
assert "email = ?" in query._update_set_columns
|
231
|
+
|
232
|
+
# Check that update values are set correctly
|
233
|
+
assert "New Name" in query._update_set_values
|
234
|
+
assert "new@example.com" in query._update_set_values
|
235
|
+
|
236
|
+
def test_update_method_chaining(self):
|
237
|
+
"""Test that update method returns self for chaining"""
|
238
|
+
query = self.base_query.update(name="New Name")
|
239
|
+
|
240
|
+
# Check that the same instance is returned
|
241
|
+
assert query is self.base_query
|
242
|
+
|
243
|
+
# Check that chaining works
|
244
|
+
chained_query = query.filter("id = ?", 1).filter("status = ?", "active")
|
245
|
+
|
246
|
+
assert chained_query is self.base_query
|
247
|
+
assert query._query_type == "UPDATE"
|
248
|
+
assert "id = 1" in query._where_conditions
|
249
|
+
assert "status = 'active'" in query._where_conditions
|
250
|
+
|
251
|
+
def test_update_with_none_values(self):
|
252
|
+
"""Test update with None values"""
|
253
|
+
query = self.base_query.update(name=None, email="new@example.com")
|
254
|
+
|
255
|
+
# Check that None values are handled correctly
|
256
|
+
assert "name = ?" in query._update_set_columns
|
257
|
+
assert "email = ?" in query._update_set_columns
|
258
|
+
assert None in query._update_set_values
|
259
|
+
assert "new@example.com" in query._update_set_values
|
260
|
+
|
261
|
+
def test_update_with_numeric_values(self):
|
262
|
+
"""Test update with numeric values"""
|
263
|
+
query = self.base_query.update(age=25, salary=50000.50)
|
264
|
+
|
265
|
+
# Check that numeric values are handled correctly
|
266
|
+
assert "age = ?" in query._update_set_columns
|
267
|
+
assert "salary = ?" in query._update_set_columns
|
268
|
+
assert 25 in query._update_set_values
|
269
|
+
assert 50000.50 in query._update_set_values
|
270
|
+
|
271
|
+
def test_update_with_boolean_values(self):
|
272
|
+
"""Test update with boolean values"""
|
273
|
+
query = self.base_query.update(active=True, verified=False)
|
274
|
+
|
275
|
+
# Check that boolean values are handled correctly
|
276
|
+
assert "active = ?" in query._update_set_columns
|
277
|
+
assert "verified = ?" in query._update_set_columns
|
278
|
+
assert True in query._update_set_values
|
279
|
+
assert False in query._update_set_values
|
280
|
+
|
281
|
+
|
282
|
+
if __name__ == "__main__":
|
283
|
+
pytest.main([__file__])
|