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,356 @@
|
|
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 insert and batch_insert methods in transaction contexts
|
17
|
+
"""
|
18
|
+
|
19
|
+
import pytest
|
20
|
+
import pytest_asyncio
|
21
|
+
from matrixone import Client, AsyncClient
|
22
|
+
from tests.online.conftest import online_config
|
23
|
+
|
24
|
+
|
25
|
+
class TestTransactionInsertMethods:
|
26
|
+
"""Test insert and batch_insert methods in transaction contexts"""
|
27
|
+
|
28
|
+
@pytest.fixture(scope="function")
|
29
|
+
def sync_client_setup(self):
|
30
|
+
"""Setup sync client for testing"""
|
31
|
+
client = Client()
|
32
|
+
host, port, user, password, database = online_config.get_connection_params()
|
33
|
+
client.connect(host, port, user, password, database)
|
34
|
+
|
35
|
+
# Create test database
|
36
|
+
test_db = "sync_insert_test"
|
37
|
+
client.execute(f"CREATE DATABASE IF NOT EXISTS {test_db}")
|
38
|
+
client.execute(f"USE {test_db}")
|
39
|
+
|
40
|
+
# Create test table
|
41
|
+
client.execute(
|
42
|
+
f"""
|
43
|
+
CREATE TABLE {test_db}.test_table (
|
44
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
45
|
+
name VARCHAR(255),
|
46
|
+
age INT,
|
47
|
+
email VARCHAR(255)
|
48
|
+
)
|
49
|
+
"""
|
50
|
+
)
|
51
|
+
|
52
|
+
yield client, test_db
|
53
|
+
|
54
|
+
# Cleanup
|
55
|
+
try:
|
56
|
+
client.execute(f"DROP DATABASE {test_db}")
|
57
|
+
except Exception as e:
|
58
|
+
print(f"Cleanup warning: {e}")
|
59
|
+
finally:
|
60
|
+
client.disconnect()
|
61
|
+
|
62
|
+
@pytest_asyncio.fixture(scope="function")
|
63
|
+
async def async_client_setup(self):
|
64
|
+
"""Setup async client for testing"""
|
65
|
+
client = AsyncClient()
|
66
|
+
host, port, user, password, database = online_config.get_connection_params()
|
67
|
+
await client.connect(host, port, user, password, database)
|
68
|
+
|
69
|
+
# Create test database
|
70
|
+
test_db = "async_insert_test"
|
71
|
+
await client.execute(f"CREATE DATABASE IF NOT EXISTS {test_db}")
|
72
|
+
await client.execute(f"USE {test_db}")
|
73
|
+
|
74
|
+
# Create test table
|
75
|
+
await client.execute(
|
76
|
+
f"""
|
77
|
+
CREATE TABLE {test_db}.test_table (
|
78
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
79
|
+
name VARCHAR(255),
|
80
|
+
age INT,
|
81
|
+
email VARCHAR(255)
|
82
|
+
)
|
83
|
+
"""
|
84
|
+
)
|
85
|
+
|
86
|
+
yield client, test_db
|
87
|
+
|
88
|
+
# Cleanup
|
89
|
+
try:
|
90
|
+
await client.execute(f"DROP DATABASE {test_db}")
|
91
|
+
except Exception as e:
|
92
|
+
print(f"Cleanup warning: {e}")
|
93
|
+
finally:
|
94
|
+
await client.disconnect()
|
95
|
+
|
96
|
+
def test_sync_transaction_insert(self, sync_client_setup):
|
97
|
+
"""Test insert method in sync transaction context"""
|
98
|
+
client, test_db = sync_client_setup
|
99
|
+
|
100
|
+
with client.transaction() as tx:
|
101
|
+
# Test single insert
|
102
|
+
data = {'name': 'John Doe', 'age': 30, 'email': 'john@example.com'}
|
103
|
+
result = tx.insert(f"{test_db}.test_table", data)
|
104
|
+
assert result.affected_rows == 1
|
105
|
+
|
106
|
+
# Verify insert
|
107
|
+
result = tx.execute(f"SELECT * FROM {test_db}.test_table WHERE name = 'John Doe'")
|
108
|
+
assert len(result.rows) == 1
|
109
|
+
assert result.rows[0][1] == 'John Doe'
|
110
|
+
assert result.rows[0][2] == 30
|
111
|
+
assert result.rows[0][3] == 'john@example.com'
|
112
|
+
|
113
|
+
def test_sync_transaction_batch_insert(self, sync_client_setup):
|
114
|
+
"""Test batch_insert method in sync transaction context"""
|
115
|
+
client, test_db = sync_client_setup
|
116
|
+
|
117
|
+
with client.transaction() as tx:
|
118
|
+
# Test batch insert
|
119
|
+
data_list = [
|
120
|
+
{'name': 'Alice', 'age': 25, 'email': 'alice@example.com'},
|
121
|
+
{'name': 'Bob', 'age': 35, 'email': 'bob@example.com'},
|
122
|
+
{'name': 'Charlie', 'age': 28, 'email': 'charlie@example.com'},
|
123
|
+
]
|
124
|
+
result = tx.batch_insert(f"{test_db}.test_table", data_list)
|
125
|
+
assert result.affected_rows == 3
|
126
|
+
|
127
|
+
# Verify batch insert
|
128
|
+
result = tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
129
|
+
assert result.rows[0][0] == 3
|
130
|
+
|
131
|
+
# Verify individual records
|
132
|
+
result = tx.execute(f"SELECT * FROM {test_db}.test_table ORDER BY name")
|
133
|
+
assert len(result.rows) == 3
|
134
|
+
assert result.rows[0][1] == 'Alice'
|
135
|
+
assert result.rows[1][1] == 'Bob'
|
136
|
+
assert result.rows[2][1] == 'Charlie'
|
137
|
+
|
138
|
+
def test_sync_transaction_insert_with_vectors(self, sync_client_setup):
|
139
|
+
"""Test insert method with vector data in sync transaction context"""
|
140
|
+
client, test_db = sync_client_setup
|
141
|
+
|
142
|
+
# Create table with vector column
|
143
|
+
client.execute(
|
144
|
+
f"""
|
145
|
+
CREATE TABLE {test_db}.vector_table (
|
146
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
147
|
+
name VARCHAR(255),
|
148
|
+
vector JSON
|
149
|
+
)
|
150
|
+
"""
|
151
|
+
)
|
152
|
+
|
153
|
+
with client.transaction() as tx:
|
154
|
+
# Test insert with vector data
|
155
|
+
data = {'name': 'Vector Test', 'vector': [1.0, 2.0, 3.0, 4.0]}
|
156
|
+
result = tx.insert(f"{test_db}.vector_table", data)
|
157
|
+
assert result.affected_rows == 1
|
158
|
+
|
159
|
+
# Verify insert
|
160
|
+
result = tx.execute(f"SELECT * FROM {test_db}.vector_table WHERE name = 'Vector Test'")
|
161
|
+
assert len(result.rows) == 1
|
162
|
+
assert result.rows[0][1] == 'Vector Test'
|
163
|
+
|
164
|
+
def test_sync_transaction_empty_batch_insert(self, sync_client_setup):
|
165
|
+
"""Test batch_insert with empty list in sync transaction context"""
|
166
|
+
client, test_db = sync_client_setup
|
167
|
+
|
168
|
+
with client.transaction() as tx:
|
169
|
+
# Test empty batch insert
|
170
|
+
result = tx.batch_insert(f"{test_db}.test_table", [])
|
171
|
+
assert result.affected_rows == 0
|
172
|
+
assert len(result.rows) == 0
|
173
|
+
|
174
|
+
@pytest.mark.asyncio
|
175
|
+
async def test_async_transaction_insert(self, async_client_setup):
|
176
|
+
"""Test insert method in async transaction context"""
|
177
|
+
client, test_db = async_client_setup
|
178
|
+
|
179
|
+
async with client.transaction() as tx:
|
180
|
+
# Test single insert
|
181
|
+
data = {'name': 'Jane Doe', 'age': 32, 'email': 'jane@example.com'}
|
182
|
+
result = await tx.insert(f"{test_db}.test_table", data)
|
183
|
+
assert result.affected_rows == 1
|
184
|
+
|
185
|
+
# Verify insert
|
186
|
+
result = await tx.execute(f"SELECT * FROM {test_db}.test_table WHERE name = 'Jane Doe'")
|
187
|
+
assert len(result.rows) == 1
|
188
|
+
assert result.rows[0][1] == 'Jane Doe'
|
189
|
+
assert result.rows[0][2] == 32
|
190
|
+
assert result.rows[0][3] == 'jane@example.com'
|
191
|
+
|
192
|
+
@pytest.mark.asyncio
|
193
|
+
async def test_async_transaction_batch_insert(self, async_client_setup):
|
194
|
+
"""Test batch_insert method in async transaction context"""
|
195
|
+
client, test_db = async_client_setup
|
196
|
+
|
197
|
+
async with client.transaction() as tx:
|
198
|
+
# Test batch insert
|
199
|
+
data_list = [
|
200
|
+
{'name': 'David', 'age': 27, 'email': 'david@example.com'},
|
201
|
+
{'name': 'Eve', 'age': 33, 'email': 'eve@example.com'},
|
202
|
+
{'name': 'Frank', 'age': 29, 'email': 'frank@example.com'},
|
203
|
+
]
|
204
|
+
result = await tx.batch_insert(f"{test_db}.test_table", data_list)
|
205
|
+
assert result.affected_rows == 3
|
206
|
+
|
207
|
+
# Verify batch insert
|
208
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
209
|
+
assert result.rows[0][0] == 3
|
210
|
+
|
211
|
+
# Verify individual records
|
212
|
+
result = await tx.execute(f"SELECT * FROM {test_db}.test_table ORDER BY name")
|
213
|
+
assert len(result.rows) == 3
|
214
|
+
assert result.rows[0][1] == 'David'
|
215
|
+
assert result.rows[1][1] == 'Eve'
|
216
|
+
assert result.rows[2][1] == 'Frank'
|
217
|
+
|
218
|
+
@pytest.mark.asyncio
|
219
|
+
async def test_async_transaction_insert_with_vectors(self, async_client_setup):
|
220
|
+
"""Test insert method with vector data in async transaction context"""
|
221
|
+
client, test_db = async_client_setup
|
222
|
+
|
223
|
+
# Create table with vector column
|
224
|
+
await client.execute(
|
225
|
+
f"""
|
226
|
+
CREATE TABLE {test_db}.vector_table (
|
227
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
228
|
+
name VARCHAR(255),
|
229
|
+
vector JSON
|
230
|
+
)
|
231
|
+
"""
|
232
|
+
)
|
233
|
+
|
234
|
+
async with client.transaction() as tx:
|
235
|
+
# Test insert with vector data
|
236
|
+
data = {'name': 'Async Vector Test', 'vector': [5.0, 6.0, 7.0, 8.0]}
|
237
|
+
result = await tx.insert(f"{test_db}.vector_table", data)
|
238
|
+
assert result.affected_rows == 1
|
239
|
+
|
240
|
+
# Verify insert
|
241
|
+
result = await tx.execute(f"SELECT * FROM {test_db}.vector_table WHERE name = 'Async Vector Test'")
|
242
|
+
assert len(result.rows) == 1
|
243
|
+
assert result.rows[0][1] == 'Async Vector Test'
|
244
|
+
|
245
|
+
@pytest.mark.asyncio
|
246
|
+
async def test_async_transaction_empty_batch_insert(self, async_client_setup):
|
247
|
+
"""Test batch_insert with empty list in async transaction context"""
|
248
|
+
client, test_db = async_client_setup
|
249
|
+
|
250
|
+
async with client.transaction() as tx:
|
251
|
+
# Test empty batch insert
|
252
|
+
result = await tx.batch_insert(f"{test_db}.test_table", [])
|
253
|
+
assert result.affected_rows == 0
|
254
|
+
assert len(result.rows) == 0
|
255
|
+
|
256
|
+
def test_sync_transaction_rollback_insert(self, sync_client_setup):
|
257
|
+
"""Test that insert operations are rolled back on transaction failure"""
|
258
|
+
client, test_db = sync_client_setup
|
259
|
+
|
260
|
+
try:
|
261
|
+
with client.transaction() as tx:
|
262
|
+
# Insert data
|
263
|
+
data = {'name': 'Rollback Test', 'age': 40, 'email': 'rollback@example.com'}
|
264
|
+
result = tx.insert(f"{test_db}.test_table", data)
|
265
|
+
assert result.affected_rows == 1
|
266
|
+
|
267
|
+
# Force rollback by raising an exception
|
268
|
+
raise Exception("Force rollback")
|
269
|
+
except Exception:
|
270
|
+
pass
|
271
|
+
|
272
|
+
# Verify data was rolled back
|
273
|
+
result = client.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
274
|
+
assert result.rows[0][0] == 0
|
275
|
+
|
276
|
+
@pytest.mark.asyncio
|
277
|
+
async def test_async_transaction_rollback_insert(self, async_client_setup):
|
278
|
+
"""Test that insert operations are rolled back on async transaction failure"""
|
279
|
+
client, test_db = async_client_setup
|
280
|
+
|
281
|
+
try:
|
282
|
+
async with client.transaction() as tx:
|
283
|
+
# Insert data
|
284
|
+
data = {
|
285
|
+
'name': 'Async Rollback Test',
|
286
|
+
'age': 42,
|
287
|
+
'email': 'async_rollback@example.com',
|
288
|
+
}
|
289
|
+
result = await tx.insert(f"{test_db}.test_table", data)
|
290
|
+
assert result.affected_rows == 1
|
291
|
+
|
292
|
+
# Force rollback by raising an exception
|
293
|
+
raise Exception("Force rollback")
|
294
|
+
except Exception:
|
295
|
+
pass
|
296
|
+
|
297
|
+
# Verify data was rolled back
|
298
|
+
result = await client.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
299
|
+
assert result.rows[0][0] == 0
|
300
|
+
|
301
|
+
def test_sync_transaction_mixed_operations(self, sync_client_setup):
|
302
|
+
"""Test mixing insert, batch_insert, and execute in sync transaction"""
|
303
|
+
client, test_db = sync_client_setup
|
304
|
+
|
305
|
+
with client.transaction() as tx:
|
306
|
+
# Single insert
|
307
|
+
data1 = {'name': 'Mixed Test 1', 'age': 20, 'email': 'mixed1@example.com'}
|
308
|
+
result = tx.insert(f"{test_db}.test_table", data1)
|
309
|
+
assert result.affected_rows == 1
|
310
|
+
|
311
|
+
# Batch insert
|
312
|
+
data_list = [
|
313
|
+
{'name': 'Mixed Test 2', 'age': 21, 'email': 'mixed2@example.com'},
|
314
|
+
{'name': 'Mixed Test 3', 'age': 22, 'email': 'mixed3@example.com'},
|
315
|
+
]
|
316
|
+
result = tx.batch_insert(f"{test_db}.test_table", data_list)
|
317
|
+
assert result.affected_rows == 2
|
318
|
+
|
319
|
+
# Direct execute
|
320
|
+
result = tx.execute(
|
321
|
+
f"INSERT INTO {test_db}.test_table (name, age, email) VALUES ('Mixed Test 4', 23, 'mixed4@example.com')"
|
322
|
+
)
|
323
|
+
assert result.affected_rows == 1
|
324
|
+
|
325
|
+
# Verify all operations
|
326
|
+
result = tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
327
|
+
assert result.rows[0][0] == 4
|
328
|
+
|
329
|
+
@pytest.mark.asyncio
|
330
|
+
async def test_async_transaction_mixed_operations(self, async_client_setup):
|
331
|
+
"""Test mixing insert, batch_insert, and execute in async transaction"""
|
332
|
+
client, test_db = async_client_setup
|
333
|
+
|
334
|
+
async with client.transaction() as tx:
|
335
|
+
# Single insert
|
336
|
+
data1 = {'name': 'Async Mixed Test 1', 'age': 24, 'email': 'async_mixed1@example.com'}
|
337
|
+
result = await tx.insert(f"{test_db}.test_table", data1)
|
338
|
+
assert result.affected_rows == 1
|
339
|
+
|
340
|
+
# Batch insert
|
341
|
+
data_list = [
|
342
|
+
{'name': 'Async Mixed Test 2', 'age': 25, 'email': 'async_mixed2@example.com'},
|
343
|
+
{'name': 'Async Mixed Test 3', 'age': 26, 'email': 'async_mixed3@example.com'},
|
344
|
+
]
|
345
|
+
result = await tx.batch_insert(f"{test_db}.test_table", data_list)
|
346
|
+
assert result.affected_rows == 2
|
347
|
+
|
348
|
+
# Direct execute
|
349
|
+
result = await tx.execute(
|
350
|
+
f"INSERT INTO {test_db}.test_table (name, age, email) VALUES ('Async Mixed Test 4', 27, 'async_mixed4@example.com')"
|
351
|
+
)
|
352
|
+
assert result.affected_rows == 1
|
353
|
+
|
354
|
+
# Verify all operations
|
355
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
356
|
+
assert result.rows[0][0] == 4
|
@@ -0,0 +1,288 @@
|
|
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 query methods in transaction contexts
|
17
|
+
"""
|
18
|
+
|
19
|
+
import pytest
|
20
|
+
import pytest_asyncio
|
21
|
+
from matrixone import Client, AsyncClient
|
22
|
+
from tests.online.conftest import online_config
|
23
|
+
|
24
|
+
|
25
|
+
class TestTransactionQueryMethods:
|
26
|
+
"""Test query methods in transaction contexts"""
|
27
|
+
|
28
|
+
@pytest.fixture(scope="function")
|
29
|
+
def sync_client_setup(self):
|
30
|
+
"""Setup sync client for testing"""
|
31
|
+
client = Client()
|
32
|
+
host, port, user, password, database = online_config.get_connection_params()
|
33
|
+
client.connect(host, port, user, password, database)
|
34
|
+
|
35
|
+
# Create test database
|
36
|
+
test_db = "sync_query_test"
|
37
|
+
client.execute(f"CREATE DATABASE IF NOT EXISTS {test_db}")
|
38
|
+
client.execute(f"USE {test_db}")
|
39
|
+
|
40
|
+
# Create test table
|
41
|
+
client.execute(
|
42
|
+
f"""
|
43
|
+
CREATE TABLE {test_db}.test_table (
|
44
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
45
|
+
name VARCHAR(255),
|
46
|
+
age INT,
|
47
|
+
email VARCHAR(255)
|
48
|
+
)
|
49
|
+
"""
|
50
|
+
)
|
51
|
+
|
52
|
+
# Insert test data
|
53
|
+
client.execute(
|
54
|
+
f"""
|
55
|
+
INSERT INTO {test_db}.test_table (name, age, email) VALUES
|
56
|
+
('Alice', 25, 'alice@example.com'),
|
57
|
+
('Bob', 30, 'bob@example.com'),
|
58
|
+
('Charlie', 35, 'charlie@example.com')
|
59
|
+
"""
|
60
|
+
)
|
61
|
+
|
62
|
+
yield client, test_db
|
63
|
+
|
64
|
+
# Cleanup
|
65
|
+
try:
|
66
|
+
client.execute(f"DROP DATABASE {test_db}")
|
67
|
+
except Exception as e:
|
68
|
+
print(f"Cleanup warning: {e}")
|
69
|
+
finally:
|
70
|
+
client.disconnect()
|
71
|
+
|
72
|
+
@pytest_asyncio.fixture(scope="function")
|
73
|
+
async def async_client_setup(self):
|
74
|
+
"""Setup async client for testing"""
|
75
|
+
client = AsyncClient()
|
76
|
+
host, port, user, password, database = online_config.get_connection_params()
|
77
|
+
await client.connect(host, port, user, password, database)
|
78
|
+
|
79
|
+
# Create test database
|
80
|
+
test_db = "async_query_test"
|
81
|
+
await client.execute(f"CREATE DATABASE IF NOT EXISTS {test_db}")
|
82
|
+
await client.execute(f"USE {test_db}")
|
83
|
+
|
84
|
+
# Create test table
|
85
|
+
await client.execute(
|
86
|
+
f"""
|
87
|
+
CREATE TABLE {test_db}.test_table (
|
88
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
89
|
+
name VARCHAR(255),
|
90
|
+
age INT,
|
91
|
+
email VARCHAR(255)
|
92
|
+
)
|
93
|
+
"""
|
94
|
+
)
|
95
|
+
|
96
|
+
# Insert test data
|
97
|
+
await client.execute(
|
98
|
+
f"""
|
99
|
+
INSERT INTO {test_db}.test_table (name, age, email) VALUES
|
100
|
+
('David', 28, 'david@example.com'),
|
101
|
+
('Eve', 32, 'eve@example.com'),
|
102
|
+
('Frank', 38, 'frank@example.com')
|
103
|
+
"""
|
104
|
+
)
|
105
|
+
|
106
|
+
yield client, test_db
|
107
|
+
|
108
|
+
# Cleanup
|
109
|
+
try:
|
110
|
+
await client.execute(f"DROP DATABASE {test_db}")
|
111
|
+
except Exception as e:
|
112
|
+
print(f"Cleanup warning: {e}")
|
113
|
+
finally:
|
114
|
+
await client.disconnect()
|
115
|
+
|
116
|
+
def test_sync_transaction_query_method_exists(self, sync_client_setup):
|
117
|
+
"""Test that query method exists in sync transaction context"""
|
118
|
+
client, test_db = sync_client_setup
|
119
|
+
|
120
|
+
with client.transaction() as tx:
|
121
|
+
# Test that query method exists
|
122
|
+
assert hasattr(tx, 'query'), "TransactionWrapper should have query method"
|
123
|
+
|
124
|
+
# Test that query method is callable
|
125
|
+
assert callable(tx.query), "TransactionWrapper.query should be callable"
|
126
|
+
|
127
|
+
@pytest.mark.asyncio
|
128
|
+
async def test_async_transaction_query_method_exists(self, async_client_setup):
|
129
|
+
"""Test that query method exists in async transaction context"""
|
130
|
+
client, test_db = async_client_setup
|
131
|
+
|
132
|
+
async with client.transaction() as tx:
|
133
|
+
# Test that query method exists
|
134
|
+
assert hasattr(tx, 'query'), "AsyncTransactionWrapper should have query method"
|
135
|
+
|
136
|
+
# Test that query method is callable
|
137
|
+
assert callable(tx.query), "AsyncTransactionWrapper.query should be callable"
|
138
|
+
|
139
|
+
def test_sync_transaction_query_with_simple_select(self, sync_client_setup):
|
140
|
+
"""Test query method with simple SELECT in sync transaction context"""
|
141
|
+
client, test_db = sync_client_setup
|
142
|
+
|
143
|
+
with client.transaction() as tx:
|
144
|
+
# Test simple query using execute (since we don't have ORM models in this test)
|
145
|
+
result = tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
146
|
+
assert result.rows[0][0] == 3
|
147
|
+
|
148
|
+
# Test that we can insert and query within the same transaction
|
149
|
+
tx.execute(
|
150
|
+
f"INSERT INTO {test_db}.test_table (name, age, email) VALUES ('Transaction Test', 40, 'tx@example.com')"
|
151
|
+
)
|
152
|
+
|
153
|
+
# Verify the insert
|
154
|
+
result = tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
155
|
+
assert result.rows[0][0] == 4
|
156
|
+
|
157
|
+
@pytest.mark.asyncio
|
158
|
+
async def test_async_transaction_query_with_simple_select(self, async_client_setup):
|
159
|
+
"""Test query method with simple SELECT in async transaction context"""
|
160
|
+
client, test_db = async_client_setup
|
161
|
+
|
162
|
+
async with client.transaction() as tx:
|
163
|
+
# Test simple query using execute (since we don't have ORM models in this test)
|
164
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
165
|
+
assert result.rows[0][0] == 3
|
166
|
+
|
167
|
+
# Test that we can insert and query within the same transaction
|
168
|
+
await tx.execute(
|
169
|
+
f"INSERT INTO {test_db}.test_table (name, age, email) VALUES ('Async Transaction Test', 42, 'async_tx@example.com')"
|
170
|
+
)
|
171
|
+
|
172
|
+
# Verify the insert
|
173
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
174
|
+
assert result.rows[0][0] == 4
|
175
|
+
|
176
|
+
def test_sync_transaction_query_rollback(self, sync_client_setup):
|
177
|
+
"""Test that query operations are rolled back on transaction failure"""
|
178
|
+
client, test_db = sync_client_setup
|
179
|
+
|
180
|
+
try:
|
181
|
+
with client.transaction() as tx:
|
182
|
+
# Insert data
|
183
|
+
tx.execute(
|
184
|
+
f"INSERT INTO {test_db}.test_table (name, age, email) VALUES ('Rollback Test', 50, 'rollback@example.com')"
|
185
|
+
)
|
186
|
+
|
187
|
+
# Verify insert
|
188
|
+
result = tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
189
|
+
assert result.rows[0][0] == 4
|
190
|
+
|
191
|
+
# Force rollback
|
192
|
+
raise Exception("Force rollback")
|
193
|
+
except Exception:
|
194
|
+
pass
|
195
|
+
|
196
|
+
# Verify data was rolled back
|
197
|
+
result = client.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
198
|
+
assert result.rows[0][0] == 3
|
199
|
+
|
200
|
+
@pytest.mark.asyncio
|
201
|
+
async def test_async_transaction_query_rollback(self, async_client_setup):
|
202
|
+
"""Test that query operations are rolled back on async transaction failure"""
|
203
|
+
client, test_db = async_client_setup
|
204
|
+
|
205
|
+
try:
|
206
|
+
async with client.transaction() as tx:
|
207
|
+
# Insert data
|
208
|
+
await tx.execute(
|
209
|
+
f"INSERT INTO {test_db}.test_table (name, age, email) VALUES ('Async Rollback Test', 52, 'async_rollback@example.com')"
|
210
|
+
)
|
211
|
+
|
212
|
+
# Verify insert
|
213
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
214
|
+
assert result.rows[0][0] == 4
|
215
|
+
|
216
|
+
# Force rollback
|
217
|
+
raise Exception("Force rollback")
|
218
|
+
except Exception:
|
219
|
+
pass
|
220
|
+
|
221
|
+
# Verify data was rolled back
|
222
|
+
result = await client.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
223
|
+
assert result.rows[0][0] == 3
|
224
|
+
|
225
|
+
def test_sync_transaction_mixed_operations(self, sync_client_setup):
|
226
|
+
"""Test mixing query, insert, and batch_insert in sync transaction"""
|
227
|
+
client, test_db = sync_client_setup
|
228
|
+
|
229
|
+
with client.transaction() as tx:
|
230
|
+
# Query initial count
|
231
|
+
result = tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
232
|
+
initial_count = result.rows[0][0]
|
233
|
+
assert initial_count == 3
|
234
|
+
|
235
|
+
# Insert single record
|
236
|
+
tx.execute(
|
237
|
+
f"INSERT INTO {test_db}.test_table (name, age, email) VALUES ('Mixed Test 1', 45, 'mixed1@example.com')"
|
238
|
+
)
|
239
|
+
|
240
|
+
# Query count after insert
|
241
|
+
result = tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
242
|
+
assert result.rows[0][0] == initial_count + 1
|
243
|
+
|
244
|
+
# Insert multiple records
|
245
|
+
tx.execute(
|
246
|
+
f"""
|
247
|
+
INSERT INTO {test_db}.test_table (name, age, email) VALUES
|
248
|
+
('Mixed Test 2', 46, 'mixed2@example.com'),
|
249
|
+
('Mixed Test 3', 47, 'mixed3@example.com')
|
250
|
+
"""
|
251
|
+
)
|
252
|
+
|
253
|
+
# Query final count
|
254
|
+
result = tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
255
|
+
assert result.rows[0][0] == initial_count + 3
|
256
|
+
|
257
|
+
@pytest.mark.asyncio
|
258
|
+
async def test_async_transaction_mixed_operations(self, async_client_setup):
|
259
|
+
"""Test mixing query, insert, and batch_insert in async transaction"""
|
260
|
+
client, test_db = async_client_setup
|
261
|
+
|
262
|
+
async with client.transaction() as tx:
|
263
|
+
# Query initial count
|
264
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
265
|
+
initial_count = result.rows[0][0]
|
266
|
+
assert initial_count == 3
|
267
|
+
|
268
|
+
# Insert single record
|
269
|
+
await tx.execute(
|
270
|
+
f"INSERT INTO {test_db}.test_table (name, age, email) VALUES ('Async Mixed Test 1', 48, 'async_mixed1@example.com')"
|
271
|
+
)
|
272
|
+
|
273
|
+
# Query count after insert
|
274
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
275
|
+
assert result.rows[0][0] == initial_count + 1
|
276
|
+
|
277
|
+
# Insert multiple records
|
278
|
+
await tx.execute(
|
279
|
+
f"""
|
280
|
+
INSERT INTO {test_db}.test_table (name, age, email) VALUES
|
281
|
+
('Async Mixed Test 2', 49, 'async_mixed2@example.com'),
|
282
|
+
('Async Mixed Test 3', 50, 'async_mixed3@example.com')
|
283
|
+
"""
|
284
|
+
)
|
285
|
+
|
286
|
+
# Query final count
|
287
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.test_table")
|
288
|
+
assert result.rows[0][0] == initial_count + 3
|