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,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