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,311 @@
|
|
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
|
+
MatrixOne Async Metadata Operations
|
17
|
+
|
18
|
+
This module provides async metadata scanning capabilities for MatrixOne tables,
|
19
|
+
allowing users to analyze table statistics, column information, and data distribution.
|
20
|
+
"""
|
21
|
+
|
22
|
+
from typing import Optional, List, Dict, Any, Union
|
23
|
+
from sqlalchemy.engine import Result
|
24
|
+
from .metadata import BaseMetadataManager, MetadataColumn, MetadataRow
|
25
|
+
|
26
|
+
|
27
|
+
class AsyncMetadataManager(BaseMetadataManager):
|
28
|
+
"""
|
29
|
+
Async metadata manager for MatrixOne table metadata operations.
|
30
|
+
|
31
|
+
Provides async methods to scan table metadata including column statistics,
|
32
|
+
row counts, null counts, and data distribution information.
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self, client):
|
36
|
+
"""
|
37
|
+
Initialize async metadata manager.
|
38
|
+
|
39
|
+
Args:
|
40
|
+
|
41
|
+
client: MatrixOne AsyncClient instance
|
42
|
+
"""
|
43
|
+
self.client = client
|
44
|
+
|
45
|
+
async def _execute_sql(self, sql: str):
|
46
|
+
"""
|
47
|
+
Execute SQL query asynchronously.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
|
51
|
+
sql: SQL query to execute
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
|
55
|
+
SQLAlchemy Result object
|
56
|
+
"""
|
57
|
+
return await self.client.execute(sql)
|
58
|
+
|
59
|
+
async def scan(
|
60
|
+
self,
|
61
|
+
dbname: str,
|
62
|
+
tablename: str,
|
63
|
+
is_tombstone: Optional[bool] = None,
|
64
|
+
indexname: Optional[str] = None,
|
65
|
+
columns: Optional[List[Union[MetadataColumn, str]]] = None,
|
66
|
+
distinct_object_name: Optional[bool] = None,
|
67
|
+
) -> Union[Result, List[MetadataRow]]:
|
68
|
+
"""
|
69
|
+
Scan table metadata using metadata_scan function (async).
|
70
|
+
|
71
|
+
Args:
|
72
|
+
|
73
|
+
dbname: Database name
|
74
|
+
tablename: Table name
|
75
|
+
is_tombstone: Optional tombstone flag (True/False)
|
76
|
+
indexname: Optional index name
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
|
80
|
+
SQLAlchemy Result object containing metadata scan results
|
81
|
+
|
82
|
+
Example:
|
83
|
+
|
84
|
+
```python
|
85
|
+
# Scan all columns of a table
|
86
|
+
result = await client.metadata.scan("test_db", "users")
|
87
|
+
|
88
|
+
# Scan specific column
|
89
|
+
result = await client.metadata.scan("test_db", "users", indexname="id")
|
90
|
+
|
91
|
+
# Scan with tombstone filter
|
92
|
+
result = await client.metadata.scan("test_db", "users", is_tombstone=False)
|
93
|
+
|
94
|
+
# Scan tombstone objects
|
95
|
+
result = await client.metadata.scan("test_db", "users", is_tombstone=True)
|
96
|
+
|
97
|
+
# Scan specific index
|
98
|
+
result = await client.metadata.scan("test_db", "users", indexname="idx_name")
|
99
|
+
```
|
100
|
+
"""
|
101
|
+
# Build SQL query
|
102
|
+
sql = self._build_metadata_scan_sql(dbname, tablename, is_tombstone, indexname, distinct_object_name)
|
103
|
+
|
104
|
+
# Execute the query
|
105
|
+
result = await self._execute_sql(sql)
|
106
|
+
|
107
|
+
# Process result based on columns parameter
|
108
|
+
return self._process_scan_result(result, columns)
|
109
|
+
|
110
|
+
async def get_table_brief_stats(
|
111
|
+
self,
|
112
|
+
dbname: str,
|
113
|
+
tablename: str,
|
114
|
+
is_tombstone: Optional[bool] = None,
|
115
|
+
indexname: Optional[str] = None,
|
116
|
+
include_tombstone: bool = False,
|
117
|
+
include_indexes: Optional[List[str]] = None,
|
118
|
+
) -> Dict[str, Dict[str, Any]]:
|
119
|
+
"""
|
120
|
+
Get brief statistics for a table, tombstone, and indexes (async).
|
121
|
+
|
122
|
+
Args:
|
123
|
+
|
124
|
+
dbname: Database name
|
125
|
+
tablename: Table name
|
126
|
+
is_tombstone: Optional tombstone flag (True/False)
|
127
|
+
indexname: Optional index name
|
128
|
+
include_tombstone: Whether to include tombstone statistics
|
129
|
+
include_indexes: List of index names to include
|
130
|
+
|
131
|
+
Returns:
|
132
|
+
|
133
|
+
Dictionary with brief statistics for table, tombstone, and indexes
|
134
|
+
"""
|
135
|
+
|
136
|
+
# Create a wrapper function that awaits the async execution
|
137
|
+
async def async_execute_func(sql: str):
|
138
|
+
return await self._execute_sql(sql)
|
139
|
+
|
140
|
+
return await self._get_table_brief_stats_logic_async(
|
141
|
+
dbname, tablename, is_tombstone, indexname, include_tombstone, include_indexes, async_execute_func
|
142
|
+
)
|
143
|
+
|
144
|
+
async def get_table_detail_stats(
|
145
|
+
self,
|
146
|
+
dbname: str,
|
147
|
+
tablename: str,
|
148
|
+
is_tombstone: Optional[bool] = None,
|
149
|
+
indexname: Optional[str] = None,
|
150
|
+
include_tombstone: bool = False,
|
151
|
+
include_indexes: Optional[List[str]] = None,
|
152
|
+
) -> Dict[str, List[Dict[str, Any]]]:
|
153
|
+
"""
|
154
|
+
Get detailed statistics for a table, tombstone, and indexes (async).
|
155
|
+
|
156
|
+
Args:
|
157
|
+
|
158
|
+
dbname: Database name
|
159
|
+
tablename: Table name
|
160
|
+
is_tombstone: Optional tombstone flag (True/False)
|
161
|
+
indexname: Optional index name
|
162
|
+
include_tombstone: Whether to include tombstone statistics
|
163
|
+
include_indexes: List of index names to include
|
164
|
+
|
165
|
+
Returns:
|
166
|
+
|
167
|
+
Dictionary with detailed statistics for table, tombstone, and indexes
|
168
|
+
"""
|
169
|
+
|
170
|
+
# Create a wrapper function that awaits the async execution
|
171
|
+
async def async_execute_func(sql: str):
|
172
|
+
return await self._execute_sql(sql)
|
173
|
+
|
174
|
+
return await self._get_table_detail_stats_logic_async(
|
175
|
+
dbname, tablename, is_tombstone, indexname, include_tombstone, include_indexes, async_execute_func
|
176
|
+
)
|
177
|
+
|
178
|
+
|
179
|
+
class AsyncTransactionMetadataManager(BaseMetadataManager):
|
180
|
+
"""
|
181
|
+
Async transaction metadata manager for MatrixOne table metadata operations within transactions.
|
182
|
+
|
183
|
+
Provides async methods to scan table metadata including column statistics,
|
184
|
+
row counts, null counts, and data distribution information within a transaction context.
|
185
|
+
"""
|
186
|
+
|
187
|
+
def __init__(self, client, transaction_wrapper):
|
188
|
+
"""
|
189
|
+
Initialize async transaction metadata manager.
|
190
|
+
|
191
|
+
Args:
|
192
|
+
|
193
|
+
client: MatrixOne AsyncClient instance
|
194
|
+
transaction_wrapper: Async transaction wrapper instance
|
195
|
+
"""
|
196
|
+
self.client = client
|
197
|
+
self.transaction_wrapper = transaction_wrapper
|
198
|
+
|
199
|
+
async def _execute_sql(self, sql: str):
|
200
|
+
"""
|
201
|
+
Execute SQL query using async transaction wrapper.
|
202
|
+
|
203
|
+
Args:
|
204
|
+
|
205
|
+
sql: SQL query to execute
|
206
|
+
|
207
|
+
Returns:
|
208
|
+
|
209
|
+
SQLAlchemy Result object
|
210
|
+
"""
|
211
|
+
return await self.transaction_wrapper.execute(sql)
|
212
|
+
|
213
|
+
async def scan(
|
214
|
+
self,
|
215
|
+
dbname: str,
|
216
|
+
tablename: str,
|
217
|
+
is_tombstone: Optional[bool] = None,
|
218
|
+
indexname: Optional[str] = None,
|
219
|
+
columns: Optional[List[Union[MetadataColumn, str]]] = None,
|
220
|
+
distinct_object_name: Optional[bool] = None,
|
221
|
+
) -> Union[Result, List[MetadataRow]]:
|
222
|
+
"""
|
223
|
+
Scan table metadata using metadata_scan function within transaction (async).
|
224
|
+
|
225
|
+
Args:
|
226
|
+
|
227
|
+
dbname: Database name
|
228
|
+
tablename: Table name
|
229
|
+
is_tombstone: Optional tombstone flag (True/False)
|
230
|
+
indexname: Optional index name
|
231
|
+
|
232
|
+
Returns:
|
233
|
+
|
234
|
+
SQLAlchemy Result object containing metadata scan results
|
235
|
+
"""
|
236
|
+
# Build SQL query
|
237
|
+
sql = self._build_metadata_scan_sql(dbname, tablename, is_tombstone, indexname, distinct_object_name)
|
238
|
+
|
239
|
+
# Execute the query within transaction
|
240
|
+
result = await self._execute_sql(sql)
|
241
|
+
|
242
|
+
# Process result based on columns parameter
|
243
|
+
return self._process_scan_result(result, columns)
|
244
|
+
|
245
|
+
async def get_table_brief_stats(
|
246
|
+
self,
|
247
|
+
dbname: str,
|
248
|
+
tablename: str,
|
249
|
+
is_tombstone: Optional[bool] = None,
|
250
|
+
indexname: Optional[str] = None,
|
251
|
+
include_tombstone: bool = False,
|
252
|
+
include_indexes: Optional[List[str]] = None,
|
253
|
+
) -> Dict[str, Dict[str, Any]]:
|
254
|
+
"""
|
255
|
+
Get brief statistics for a table, tombstone, and indexes within transaction (async).
|
256
|
+
|
257
|
+
Args:
|
258
|
+
|
259
|
+
dbname: Database name
|
260
|
+
tablename: Table name
|
261
|
+
is_tombstone: Optional tombstone flag (True/False)
|
262
|
+
indexname: Optional index name
|
263
|
+
include_tombstone: Whether to include tombstone statistics
|
264
|
+
include_indexes: List of index names to include
|
265
|
+
|
266
|
+
Returns:
|
267
|
+
|
268
|
+
Dictionary with brief statistics for table, tombstone, and indexes
|
269
|
+
"""
|
270
|
+
|
271
|
+
# Create a wrapper function that awaits the async execution
|
272
|
+
async def async_execute_func(sql: str):
|
273
|
+
return await self._execute_sql(sql)
|
274
|
+
|
275
|
+
return await self._get_table_brief_stats_logic_async(
|
276
|
+
dbname, tablename, is_tombstone, indexname, include_tombstone, include_indexes, async_execute_func
|
277
|
+
)
|
278
|
+
|
279
|
+
async def get_table_detail_stats(
|
280
|
+
self,
|
281
|
+
dbname: str,
|
282
|
+
tablename: str,
|
283
|
+
is_tombstone: Optional[bool] = None,
|
284
|
+
indexname: Optional[str] = None,
|
285
|
+
include_tombstone: bool = False,
|
286
|
+
include_indexes: Optional[List[str]] = None,
|
287
|
+
) -> Dict[str, List[Dict[str, Any]]]:
|
288
|
+
"""
|
289
|
+
Get detailed statistics for a table, tombstone, and indexes within transaction (async).
|
290
|
+
|
291
|
+
Args:
|
292
|
+
|
293
|
+
dbname: Database name
|
294
|
+
tablename: Table name
|
295
|
+
is_tombstone: Optional tombstone flag (True/False)
|
296
|
+
indexname: Optional index name
|
297
|
+
include_tombstone: Whether to include tombstone statistics
|
298
|
+
include_indexes: List of index names to include
|
299
|
+
|
300
|
+
Returns:
|
301
|
+
|
302
|
+
Dictionary with detailed statistics for table, tombstone, and indexes
|
303
|
+
"""
|
304
|
+
|
305
|
+
# Create a wrapper function that awaits the async execution
|
306
|
+
async def async_execute_func(sql: str):
|
307
|
+
return await self._execute_sql(sql)
|
308
|
+
|
309
|
+
return await self._get_table_detail_stats_logic_async(
|
310
|
+
dbname, tablename, is_tombstone, indexname, include_tombstone, include_indexes, async_execute_func
|
311
|
+
)
|
matrixone/async_orm.py
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# Copyright 2021 - 2022 Matrix Origin
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""
|
16
|
+
MatrixOne Async ORM - SQLAlchemy-like interface for MatrixOne database (Async)
|
17
|
+
"""
|
18
|
+
|
19
|
+
from typing import Any, List, Optional, TypeVar
|
20
|
+
|
21
|
+
from .orm import BaseMatrixOneQuery
|
22
|
+
|
23
|
+
T = TypeVar("T")
|
24
|
+
|
25
|
+
|
26
|
+
class AsyncMatrixOneQuery(BaseMatrixOneQuery):
|
27
|
+
"""Async MatrixOne Query builder that mimics SQLAlchemy Query interface"""
|
28
|
+
|
29
|
+
def __init__(self, model_class, client, database: str = None, transaction_wrapper=None, snapshot=None):
|
30
|
+
super().__init__(model_class, client, transaction_wrapper, snapshot)
|
31
|
+
self.database = database
|
32
|
+
|
33
|
+
def _build_sql(self) -> tuple[str, List[Any]]:
|
34
|
+
"""Build SQL query with database prefix support"""
|
35
|
+
# Add database prefix if provided
|
36
|
+
original_table_name = self._table_name
|
37
|
+
if self.database:
|
38
|
+
self._table_name = f"{self.database}.{original_table_name}"
|
39
|
+
|
40
|
+
# Call parent's _build_sql method
|
41
|
+
sql, params = super()._build_sql()
|
42
|
+
|
43
|
+
# Restore original table name
|
44
|
+
self._table_name = original_table_name
|
45
|
+
|
46
|
+
return sql, params
|
47
|
+
|
48
|
+
async def _execute(self, sql, params=None):
|
49
|
+
"""Execute SQL using either transaction wrapper or client asynchronously"""
|
50
|
+
if self.transaction_wrapper:
|
51
|
+
return await self.transaction_wrapper.execute(sql, params)
|
52
|
+
else:
|
53
|
+
return await self.client.execute(sql, params)
|
54
|
+
|
55
|
+
async def all(self) -> List:
|
56
|
+
"""Execute query and return all results - SQLAlchemy style"""
|
57
|
+
sql, params = self._build_sql()
|
58
|
+
result = await self._execute(sql, params)
|
59
|
+
|
60
|
+
models = []
|
61
|
+
for row in result.rows:
|
62
|
+
# Check if this is an aggregate query (has custom select columns)
|
63
|
+
if self._select_columns:
|
64
|
+
# For aggregate queries, return raw row data as a simple object
|
65
|
+
select_cols = self._extract_select_columns()
|
66
|
+
row_data = self._create_row_data(row, select_cols)
|
67
|
+
models.append(row_data)
|
68
|
+
else:
|
69
|
+
# Regular model query
|
70
|
+
if self._is_sqlalchemy_model:
|
71
|
+
# For SQLAlchemy models, create instance directly
|
72
|
+
row_dict = {}
|
73
|
+
for i, col_name in enumerate(self._columns.keys()):
|
74
|
+
if i < len(row):
|
75
|
+
row_dict[col_name] = row[i]
|
76
|
+
|
77
|
+
# Create SQLAlchemy model instance
|
78
|
+
model = self.model_class(**row_dict)
|
79
|
+
models.append(model)
|
80
|
+
else:
|
81
|
+
# For non-SQLAlchemy models, create instance directly
|
82
|
+
row_dict = {}
|
83
|
+
for i, col_name in enumerate(self._columns.keys()):
|
84
|
+
if i < len(row):
|
85
|
+
row_dict[col_name] = row[i]
|
86
|
+
|
87
|
+
# Create model instance
|
88
|
+
model = self.model_class(**row_dict)
|
89
|
+
models.append(model)
|
90
|
+
|
91
|
+
return models
|
92
|
+
|
93
|
+
async def first(self) -> Optional:
|
94
|
+
"""Execute query and return first result - SQLAlchemy style"""
|
95
|
+
self._limit_count = 1
|
96
|
+
results = await self.all()
|
97
|
+
return results[0] if results else None
|
98
|
+
|
99
|
+
async def count(self) -> int:
|
100
|
+
"""Execute query and return count of results - SQLAlchemy style"""
|
101
|
+
sql, params = self._build_sql()
|
102
|
+
# Replace SELECT * with COUNT(*)
|
103
|
+
sql = sql.replace("SELECT *", "SELECT COUNT(*)")
|
104
|
+
|
105
|
+
result = await self._execute(sql, params)
|
106
|
+
return result.rows[0][0] if result.rows else 0
|
107
|
+
|
108
|
+
async def execute(self) -> Any:
|
109
|
+
"""Execute the query based on its type"""
|
110
|
+
if self._query_type == "SELECT":
|
111
|
+
sql, params = self._build_sql()
|
112
|
+
return await self._execute(sql, params)
|
113
|
+
elif self._query_type == "INSERT":
|
114
|
+
sql, params = self._build_insert_sql()
|
115
|
+
return await self._execute(sql, params)
|
116
|
+
elif self._query_type == "UPDATE":
|
117
|
+
sql, params = self._build_update_sql()
|
118
|
+
return await self._execute(sql, params)
|
119
|
+
elif self._query_type == "DELETE":
|
120
|
+
sql, params = self._build_delete_sql()
|
121
|
+
return await self._execute(sql, params)
|
122
|
+
else:
|
123
|
+
raise ValueError(f"Unknown query type: {self._query_type}")
|