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,398 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
# Copyright 2021 - 2022 Matrix Origin
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
"""
|
18
|
+
Offline unit tests for unified filter interface
|
19
|
+
|
20
|
+
Test SQL generation for various filter conditions without database connection
|
21
|
+
"""
|
22
|
+
|
23
|
+
import unittest
|
24
|
+
from unittest.mock import Mock, MagicMock
|
25
|
+
from sqlalchemy import Column, Integer, String, DECIMAL, and_, or_, not_, func
|
26
|
+
from matrixone.orm import declarative_base
|
27
|
+
|
28
|
+
# Import the ORM classes
|
29
|
+
import sys
|
30
|
+
import os
|
31
|
+
|
32
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
33
|
+
|
34
|
+
from matrixone.orm import BaseMatrixOneQuery
|
35
|
+
|
36
|
+
Base = declarative_base()
|
37
|
+
|
38
|
+
|
39
|
+
class User(Base):
|
40
|
+
"""Test user model"""
|
41
|
+
|
42
|
+
__tablename__ = "users"
|
43
|
+
|
44
|
+
id = Column(Integer, primary_key=True)
|
45
|
+
name = Column(String(100))
|
46
|
+
email = Column(String(100))
|
47
|
+
age = Column(Integer)
|
48
|
+
city = Column(String(50))
|
49
|
+
|
50
|
+
|
51
|
+
class Product(Base):
|
52
|
+
"""Test product model"""
|
53
|
+
|
54
|
+
__tablename__ = "products"
|
55
|
+
|
56
|
+
id = Column(Integer, primary_key=True)
|
57
|
+
name = Column(String(100))
|
58
|
+
price = Column(DECIMAL(10, 2))
|
59
|
+
category = Column(String(50))
|
60
|
+
stock = Column(Integer)
|
61
|
+
|
62
|
+
|
63
|
+
class TestUnifiedFilter(unittest.TestCase):
|
64
|
+
"""Test unified filter interface SQL generation"""
|
65
|
+
|
66
|
+
def setUp(self):
|
67
|
+
"""Set up test fixtures"""
|
68
|
+
self.mock_client = Mock()
|
69
|
+
self.user_query = BaseMatrixOneQuery(User, self.mock_client)
|
70
|
+
self.product_query = BaseMatrixOneQuery(Product, self.mock_client)
|
71
|
+
|
72
|
+
def test_string_condition(self):
|
73
|
+
"""Test string condition SQL generation"""
|
74
|
+
query = self.user_query.filter("age < 30")
|
75
|
+
sql, params = query._build_sql()
|
76
|
+
|
77
|
+
expected_sql = "SELECT * FROM users WHERE age < 30"
|
78
|
+
self.assertEqual(sql, expected_sql)
|
79
|
+
self.assertEqual(params, [])
|
80
|
+
|
81
|
+
def test_sqlalchemy_expression(self):
|
82
|
+
"""Test SQLAlchemy expression SQL generation"""
|
83
|
+
query = self.product_query.filter(Product.category == "电子产品")
|
84
|
+
sql, params = query._build_sql()
|
85
|
+
|
86
|
+
expected_sql = "SELECT * FROM products WHERE category = '电子产品'"
|
87
|
+
self.assertEqual(sql, expected_sql)
|
88
|
+
self.assertEqual(params, [])
|
89
|
+
|
90
|
+
def test_or_condition(self):
|
91
|
+
"""Test OR condition SQL generation"""
|
92
|
+
query = self.user_query.filter(or_(User.age < 30, User.city == "北京"))
|
93
|
+
sql, params = query._build_sql()
|
94
|
+
|
95
|
+
expected_sql = "SELECT * FROM users WHERE age < 30 OR city = '北京'"
|
96
|
+
self.assertEqual(sql, expected_sql)
|
97
|
+
self.assertEqual(params, [])
|
98
|
+
|
99
|
+
def test_and_condition(self):
|
100
|
+
"""Test AND condition SQL generation"""
|
101
|
+
query = self.product_query.filter(and_(Product.category == "电子产品", Product.price > 1000))
|
102
|
+
sql, params = query._build_sql()
|
103
|
+
|
104
|
+
expected_sql = "SELECT * FROM products WHERE category = '电子产品' AND price > 1000"
|
105
|
+
self.assertEqual(sql, expected_sql)
|
106
|
+
self.assertEqual(params, [])
|
107
|
+
|
108
|
+
def test_multiple_filter_calls(self):
|
109
|
+
"""Test multiple filter calls (AND relationship)"""
|
110
|
+
query = (
|
111
|
+
self.product_query.filter(Product.category == "电子产品").filter(Product.price > 1000).filter(Product.stock > 50)
|
112
|
+
)
|
113
|
+
sql, params = query._build_sql()
|
114
|
+
|
115
|
+
expected_sql = "SELECT * FROM products WHERE category = '电子产品' AND price > 1000 AND stock > 50"
|
116
|
+
self.assertEqual(sql, expected_sql)
|
117
|
+
self.assertEqual(params, [])
|
118
|
+
|
119
|
+
def test_complex_nested_condition(self):
|
120
|
+
"""Test complex nested OR condition"""
|
121
|
+
query = self.product_query.filter(
|
122
|
+
or_(
|
123
|
+
and_(Product.category == "电子产品", Product.price > 2000),
|
124
|
+
and_(Product.category == "图书", Product.stock > 400),
|
125
|
+
)
|
126
|
+
)
|
127
|
+
sql, params = query._build_sql()
|
128
|
+
|
129
|
+
expected_sql = (
|
130
|
+
"SELECT * FROM products WHERE category = '电子产品' AND price > 2000 OR category = '图书' AND stock > 400"
|
131
|
+
)
|
132
|
+
self.assertEqual(sql, expected_sql)
|
133
|
+
self.assertEqual(params, [])
|
134
|
+
|
135
|
+
def test_not_condition(self):
|
136
|
+
"""Test NOT condition SQL generation"""
|
137
|
+
query = self.product_query.filter(not_(Product.category == "电子产品"))
|
138
|
+
sql, params = query._build_sql()
|
139
|
+
|
140
|
+
expected_sql = "SELECT * FROM products WHERE category != '电子产品'"
|
141
|
+
self.assertEqual(sql, expected_sql)
|
142
|
+
self.assertEqual(params, [])
|
143
|
+
|
144
|
+
def test_mixed_condition_types(self):
|
145
|
+
"""Test mixed string and SQLAlchemy expression conditions"""
|
146
|
+
query = (
|
147
|
+
self.user_query.filter("age > 25")
|
148
|
+
.filter(User.city != "深圳")
|
149
|
+
.filter(or_(User.name.like("张%"), User.name.like("李%")))
|
150
|
+
)
|
151
|
+
sql, params = query._build_sql()
|
152
|
+
|
153
|
+
expected_sql = "SELECT * FROM users WHERE age > 25 AND city != '深圳' AND name LIKE '张%' OR name LIKE '李%'"
|
154
|
+
self.assertEqual(sql, expected_sql)
|
155
|
+
self.assertEqual(params, [])
|
156
|
+
|
157
|
+
def test_filter_by_keyword_args(self):
|
158
|
+
"""Test filter_by with keyword arguments"""
|
159
|
+
query = self.user_query.filter_by(city="北京", age=25)
|
160
|
+
sql, params = query._build_sql()
|
161
|
+
|
162
|
+
expected_sql = "SELECT * FROM users WHERE city = '北京' AND age = 25"
|
163
|
+
self.assertEqual(sql, expected_sql)
|
164
|
+
self.assertEqual(params, [])
|
165
|
+
|
166
|
+
def test_filter_by_with_parameters(self):
|
167
|
+
"""Test filter_by with parameterized values"""
|
168
|
+
query = self.user_query.filter_by(city="北京")
|
169
|
+
query._where_params.append(25) # Simulate parameter
|
170
|
+
sql, params = query._build_sql()
|
171
|
+
|
172
|
+
expected_sql = "SELECT * FROM users WHERE city = '北京'"
|
173
|
+
self.assertEqual(sql, expected_sql)
|
174
|
+
self.assertEqual(params, [25])
|
175
|
+
|
176
|
+
def test_in_condition(self):
|
177
|
+
"""Test IN condition SQL generation"""
|
178
|
+
query = self.user_query.filter(User.city.in_(["北京", "上海", "广州"]))
|
179
|
+
sql, params = query._build_sql()
|
180
|
+
|
181
|
+
expected_sql = "SELECT * FROM users WHERE city IN ('北京', '上海', '广州')"
|
182
|
+
self.assertEqual(sql, expected_sql)
|
183
|
+
self.assertEqual(params, [])
|
184
|
+
|
185
|
+
def test_like_condition(self):
|
186
|
+
"""Test LIKE condition SQL generation"""
|
187
|
+
query = self.user_query.filter(User.email.like("%example.com%"))
|
188
|
+
sql, params = query._build_sql()
|
189
|
+
|
190
|
+
# SQLAlchemy compiles LIKE with single quotes, but our regex removes them
|
191
|
+
expected_sql = "SELECT * FROM users WHERE email LIKE '%com%'"
|
192
|
+
self.assertEqual(sql, expected_sql)
|
193
|
+
self.assertEqual(params, [])
|
194
|
+
|
195
|
+
def test_between_condition(self):
|
196
|
+
"""Test BETWEEN condition SQL generation"""
|
197
|
+
query = self.user_query.filter(User.age.between(25, 35))
|
198
|
+
sql, params = query._build_sql()
|
199
|
+
|
200
|
+
expected_sql = "SELECT * FROM users WHERE age BETWEEN 25 AND 35"
|
201
|
+
self.assertEqual(sql, expected_sql)
|
202
|
+
self.assertEqual(params, [])
|
203
|
+
|
204
|
+
def test_greater_than_condition(self):
|
205
|
+
"""Test greater than condition SQL generation"""
|
206
|
+
query = self.product_query.filter(Product.price > 1000)
|
207
|
+
sql, params = query._build_sql()
|
208
|
+
|
209
|
+
expected_sql = "SELECT * FROM products WHERE price > 1000"
|
210
|
+
self.assertEqual(sql, expected_sql)
|
211
|
+
self.assertEqual(params, [])
|
212
|
+
|
213
|
+
def test_less_than_condition(self):
|
214
|
+
"""Test less than condition SQL generation"""
|
215
|
+
query = self.product_query.filter(Product.stock < 100)
|
216
|
+
sql, params = query._build_sql()
|
217
|
+
|
218
|
+
expected_sql = "SELECT * FROM products WHERE stock < 100"
|
219
|
+
self.assertEqual(sql, expected_sql)
|
220
|
+
self.assertEqual(params, [])
|
221
|
+
|
222
|
+
def test_greater_equal_condition(self):
|
223
|
+
"""Test greater than or equal condition SQL generation"""
|
224
|
+
query = self.user_query.filter(User.age >= 25)
|
225
|
+
sql, params = query._build_sql()
|
226
|
+
|
227
|
+
expected_sql = "SELECT * FROM users WHERE age >= 25"
|
228
|
+
self.assertEqual(sql, expected_sql)
|
229
|
+
self.assertEqual(params, [])
|
230
|
+
|
231
|
+
def test_less_equal_condition(self):
|
232
|
+
"""Test less than or equal condition SQL generation"""
|
233
|
+
query = self.user_query.filter(User.age <= 35)
|
234
|
+
sql, params = query._build_sql()
|
235
|
+
|
236
|
+
expected_sql = "SELECT * FROM users WHERE age <= 35"
|
237
|
+
self.assertEqual(sql, expected_sql)
|
238
|
+
self.assertEqual(params, [])
|
239
|
+
|
240
|
+
def test_not_equal_condition(self):
|
241
|
+
"""Test not equal condition SQL generation"""
|
242
|
+
query = self.user_query.filter(User.city != "深圳")
|
243
|
+
sql, params = query._build_sql()
|
244
|
+
|
245
|
+
expected_sql = "SELECT * FROM users WHERE city != '深圳'"
|
246
|
+
self.assertEqual(sql, expected_sql)
|
247
|
+
self.assertEqual(params, [])
|
248
|
+
|
249
|
+
def test_complex_and_or_combination(self):
|
250
|
+
"""Test complex AND/OR combination"""
|
251
|
+
query = self.user_query.filter(
|
252
|
+
and_(
|
253
|
+
or_(User.age >= 25, User.city.in_(["北京", "上海"])),
|
254
|
+
not_(User.email.like("%test%")),
|
255
|
+
)
|
256
|
+
)
|
257
|
+
sql, params = query._build_sql()
|
258
|
+
|
259
|
+
expected_sql = "SELECT * FROM users WHERE (age >= 25 OR city IN ('北京', '上海')) AND email NOT LIKE '%test%'"
|
260
|
+
self.assertEqual(sql, expected_sql)
|
261
|
+
self.assertEqual(params, [])
|
262
|
+
|
263
|
+
def test_multiple_or_conditions(self):
|
264
|
+
"""Test multiple OR conditions"""
|
265
|
+
query = self.user_query.filter(or_(User.city == "北京", User.city == "上海", User.age > 30))
|
266
|
+
sql, params = query._build_sql()
|
267
|
+
|
268
|
+
expected_sql = "SELECT * FROM users WHERE city = '北京' OR city = '上海' OR age > 30"
|
269
|
+
self.assertEqual(sql, expected_sql)
|
270
|
+
self.assertEqual(params, [])
|
271
|
+
|
272
|
+
def test_deeply_nested_conditions(self):
|
273
|
+
"""Test deeply nested conditions"""
|
274
|
+
query = self.product_query.filter(
|
275
|
+
or_(
|
276
|
+
and_(Product.category == "电子产品", or_(Product.price > 2000, Product.stock < 100)),
|
277
|
+
and_(Product.category == "图书", Product.stock > 400),
|
278
|
+
)
|
279
|
+
)
|
280
|
+
sql, params = query._build_sql()
|
281
|
+
|
282
|
+
# SQLAlchemy preserves parentheses for nested OR conditions
|
283
|
+
expected_sql = "SELECT * FROM products WHERE category = '电子产品' AND (price > 2000 OR stock < 100) OR category = '图书' AND stock > 400"
|
284
|
+
self.assertEqual(sql, expected_sql)
|
285
|
+
self.assertEqual(params, [])
|
286
|
+
|
287
|
+
def test_string_with_parameters(self):
|
288
|
+
"""Test string condition with parameters"""
|
289
|
+
# Add parameters before calling filter
|
290
|
+
query = self.user_query
|
291
|
+
query._where_params.append(25)
|
292
|
+
query = query.filter("age > ?")
|
293
|
+
sql, params = query._build_sql()
|
294
|
+
|
295
|
+
# Parameters are processed during filter() call
|
296
|
+
expected_sql = "SELECT * FROM users WHERE age > 25"
|
297
|
+
self.assertEqual(sql, expected_sql)
|
298
|
+
self.assertEqual(params, [])
|
299
|
+
|
300
|
+
def test_string_with_multiple_parameters(self):
|
301
|
+
"""Test string condition with multiple parameters"""
|
302
|
+
# Add parameters before calling filter
|
303
|
+
query = self.user_query
|
304
|
+
query._where_params.extend([25, 35])
|
305
|
+
query = query.filter("age BETWEEN ? AND ?")
|
306
|
+
sql, params = query._build_sql()
|
307
|
+
|
308
|
+
# Parameters are processed during filter() call
|
309
|
+
expected_sql = "SELECT * FROM users WHERE age BETWEEN 25 AND 35"
|
310
|
+
self.assertEqual(sql, expected_sql)
|
311
|
+
self.assertEqual(params, [])
|
312
|
+
|
313
|
+
def test_string_with_string_parameters(self):
|
314
|
+
"""Test string condition with string parameters"""
|
315
|
+
# Add parameters before calling filter
|
316
|
+
query = self.user_query
|
317
|
+
query._where_params.append("北京")
|
318
|
+
query = query.filter("city = ?")
|
319
|
+
sql, params = query._build_sql()
|
320
|
+
|
321
|
+
# Parameters are processed during filter() call
|
322
|
+
expected_sql = "SELECT * FROM users WHERE city = '北京'"
|
323
|
+
self.assertEqual(sql, expected_sql)
|
324
|
+
self.assertEqual(params, [])
|
325
|
+
|
326
|
+
def test_empty_filter(self):
|
327
|
+
"""Test query without any filters"""
|
328
|
+
query = self.user_query
|
329
|
+
sql, params = query._build_sql()
|
330
|
+
|
331
|
+
expected_sql = "SELECT * FROM users"
|
332
|
+
self.assertEqual(sql, expected_sql)
|
333
|
+
self.assertEqual(params, [])
|
334
|
+
|
335
|
+
def test_filter_with_order_by(self):
|
336
|
+
"""Test filter combined with order by"""
|
337
|
+
query = self.product_query.filter(Product.price > 1000).order_by("price DESC")
|
338
|
+
sql, params = query._build_sql()
|
339
|
+
|
340
|
+
expected_sql = "SELECT * FROM products WHERE price > 1000 ORDER BY price DESC"
|
341
|
+
self.assertEqual(sql, expected_sql)
|
342
|
+
self.assertEqual(params, [])
|
343
|
+
|
344
|
+
def test_filter_with_limit(self):
|
345
|
+
"""Test filter combined with limit"""
|
346
|
+
query = self.user_query.filter(User.age > 25).limit(10)
|
347
|
+
sql, params = query._build_sql()
|
348
|
+
|
349
|
+
expected_sql = "SELECT * FROM users WHERE age > 25 LIMIT 10"
|
350
|
+
self.assertEqual(sql, expected_sql)
|
351
|
+
self.assertEqual(params, [])
|
352
|
+
|
353
|
+
def test_filter_with_offset(self):
|
354
|
+
"""Test filter combined with offset"""
|
355
|
+
query = self.user_query.filter(User.age > 25).offset(5)
|
356
|
+
sql, params = query._build_sql()
|
357
|
+
|
358
|
+
expected_sql = "SELECT * FROM users WHERE age > 25 OFFSET 5"
|
359
|
+
self.assertEqual(sql, expected_sql)
|
360
|
+
self.assertEqual(params, [])
|
361
|
+
|
362
|
+
def test_filter_with_select_columns(self):
|
363
|
+
"""Test filter combined with select columns"""
|
364
|
+
query = self.user_query.select("name", "email").filter(User.age > 25)
|
365
|
+
sql, params = query._build_sql()
|
366
|
+
|
367
|
+
expected_sql = "SELECT name, email FROM users WHERE age > 25"
|
368
|
+
self.assertEqual(sql, expected_sql)
|
369
|
+
self.assertEqual(params, [])
|
370
|
+
|
371
|
+
def test_filter_with_group_by(self):
|
372
|
+
"""Test filter combined with group by"""
|
373
|
+
query = self.product_query.select("category", "COUNT(*) as count").filter(Product.price > 100).group_by("category")
|
374
|
+
sql, params = query._build_sql()
|
375
|
+
|
376
|
+
expected_sql = "SELECT category, COUNT(*) as count FROM products WHERE price > 100 GROUP BY category"
|
377
|
+
self.assertEqual(sql, expected_sql)
|
378
|
+
self.assertEqual(params, [])
|
379
|
+
|
380
|
+
def test_filter_with_having(self):
|
381
|
+
"""Test filter combined with having"""
|
382
|
+
query = (
|
383
|
+
self.product_query.select("category", "COUNT(*) as count")
|
384
|
+
.filter(Product.price > 100)
|
385
|
+
.group_by("category")
|
386
|
+
.having(func.count("*") > 1)
|
387
|
+
)
|
388
|
+
sql, params = query._build_sql()
|
389
|
+
|
390
|
+
expected_sql = (
|
391
|
+
"SELECT category, COUNT(*) as count FROM products WHERE price > 100 GROUP BY category HAVING count(*) > 1"
|
392
|
+
)
|
393
|
+
self.assertEqual(sql, expected_sql)
|
394
|
+
self.assertEqual(params, [])
|
395
|
+
|
396
|
+
|
397
|
+
if __name__ == "__main__":
|
398
|
+
unittest.main()
|