sqlspec 0.14.1__py3-none-any.whl → 0.16.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.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- sqlspec/__init__.py +50 -25
- sqlspec/__main__.py +1 -1
- sqlspec/__metadata__.py +1 -3
- sqlspec/_serialization.py +1 -2
- sqlspec/_sql.py +480 -121
- sqlspec/_typing.py +278 -142
- sqlspec/adapters/adbc/__init__.py +4 -3
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +115 -260
- sqlspec/adapters/adbc/driver.py +462 -367
- sqlspec/adapters/aiosqlite/__init__.py +18 -3
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +199 -129
- sqlspec/adapters/aiosqlite/driver.py +230 -269
- sqlspec/adapters/asyncmy/__init__.py +18 -3
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +80 -168
- sqlspec/adapters/asyncmy/driver.py +260 -225
- sqlspec/adapters/asyncpg/__init__.py +19 -4
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +82 -181
- sqlspec/adapters/asyncpg/driver.py +285 -383
- sqlspec/adapters/bigquery/__init__.py +17 -3
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +191 -258
- sqlspec/adapters/bigquery/driver.py +474 -646
- sqlspec/adapters/duckdb/__init__.py +14 -3
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +415 -351
- sqlspec/adapters/duckdb/driver.py +343 -413
- sqlspec/adapters/oracledb/__init__.py +19 -5
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +123 -379
- sqlspec/adapters/oracledb/driver.py +507 -560
- sqlspec/adapters/psqlpy/__init__.py +13 -3
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +93 -254
- sqlspec/adapters/psqlpy/driver.py +505 -234
- sqlspec/adapters/psycopg/__init__.py +19 -5
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +143 -403
- sqlspec/adapters/psycopg/driver.py +706 -872
- sqlspec/adapters/sqlite/__init__.py +14 -3
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +202 -118
- sqlspec/adapters/sqlite/driver.py +264 -303
- sqlspec/base.py +105 -9
- sqlspec/{statement/builder → builder}/__init__.py +12 -14
- sqlspec/{statement/builder → builder}/_base.py +120 -55
- sqlspec/{statement/builder → builder}/_column.py +17 -6
- sqlspec/{statement/builder → builder}/_ddl.py +46 -79
- sqlspec/{statement/builder → builder}/_ddl_utils.py +5 -10
- sqlspec/{statement/builder → builder}/_delete.py +6 -25
- sqlspec/{statement/builder → builder}/_insert.py +18 -65
- sqlspec/builder/_merge.py +56 -0
- sqlspec/{statement/builder → builder}/_parsing_utils.py +8 -11
- sqlspec/{statement/builder → builder}/_select.py +11 -56
- sqlspec/{statement/builder → builder}/_update.py +12 -18
- sqlspec/{statement/builder → builder}/mixins/__init__.py +10 -14
- sqlspec/{statement/builder → builder}/mixins/_cte_and_set_ops.py +48 -59
- sqlspec/{statement/builder → builder}/mixins/_insert_operations.py +34 -18
- sqlspec/{statement/builder → builder}/mixins/_join_operations.py +1 -3
- sqlspec/{statement/builder → builder}/mixins/_merge_operations.py +19 -9
- sqlspec/{statement/builder → builder}/mixins/_order_limit_operations.py +3 -3
- sqlspec/{statement/builder → builder}/mixins/_pivot_operations.py +4 -8
- sqlspec/{statement/builder → builder}/mixins/_select_operations.py +25 -38
- sqlspec/{statement/builder → builder}/mixins/_update_operations.py +15 -16
- sqlspec/{statement/builder → builder}/mixins/_where_clause.py +210 -137
- sqlspec/cli.py +4 -5
- sqlspec/config.py +180 -133
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.py +873 -0
- sqlspec/core/compiler.py +396 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.py +1209 -0
- sqlspec/core/result.py +664 -0
- sqlspec/{statement → core}/splitter.py +321 -191
- sqlspec/core/statement.py +666 -0
- sqlspec/driver/__init__.py +7 -10
- sqlspec/driver/_async.py +387 -176
- sqlspec/driver/_common.py +527 -289
- sqlspec/driver/_sync.py +390 -172
- sqlspec/driver/mixins/__init__.py +2 -19
- sqlspec/driver/mixins/_result_tools.py +164 -0
- sqlspec/driver/mixins/_sql_translator.py +6 -3
- sqlspec/exceptions.py +5 -252
- sqlspec/extensions/aiosql/adapter.py +93 -96
- sqlspec/extensions/litestar/cli.py +1 -1
- sqlspec/extensions/litestar/config.py +0 -1
- sqlspec/extensions/litestar/handlers.py +15 -26
- sqlspec/extensions/litestar/plugin.py +18 -16
- sqlspec/extensions/litestar/providers.py +17 -52
- sqlspec/loader.py +424 -105
- sqlspec/migrations/__init__.py +12 -0
- sqlspec/migrations/base.py +92 -68
- sqlspec/migrations/commands.py +24 -106
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +49 -51
- sqlspec/migrations/tracker.py +31 -44
- sqlspec/migrations/utils.py +64 -24
- sqlspec/protocols.py +7 -183
- sqlspec/storage/__init__.py +1 -1
- sqlspec/storage/backends/base.py +37 -40
- sqlspec/storage/backends/fsspec.py +136 -112
- sqlspec/storage/backends/obstore.py +138 -160
- sqlspec/storage/capabilities.py +5 -4
- sqlspec/storage/registry.py +57 -106
- sqlspec/typing.py +136 -115
- sqlspec/utils/__init__.py +2 -3
- sqlspec/utils/correlation.py +0 -3
- sqlspec/utils/deprecation.py +6 -6
- sqlspec/utils/fixtures.py +6 -6
- sqlspec/utils/logging.py +0 -2
- sqlspec/utils/module_loader.py +7 -12
- sqlspec/utils/singleton.py +0 -1
- sqlspec/utils/sync_tools.py +17 -38
- sqlspec/utils/text.py +12 -51
- sqlspec/utils/type_guards.py +443 -232
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/METADATA +7 -2
- sqlspec-0.16.0.dist-info/RECORD +134 -0
- sqlspec/adapters/adbc/transformers.py +0 -108
- sqlspec/driver/connection.py +0 -207
- sqlspec/driver/mixins/_cache.py +0 -114
- sqlspec/driver/mixins/_csv_writer.py +0 -91
- sqlspec/driver/mixins/_pipeline.py +0 -508
- sqlspec/driver/mixins/_query_tools.py +0 -796
- sqlspec/driver/mixins/_result_utils.py +0 -138
- sqlspec/driver/mixins/_storage.py +0 -912
- sqlspec/driver/mixins/_type_coercion.py +0 -128
- sqlspec/driver/parameters.py +0 -138
- sqlspec/statement/__init__.py +0 -21
- sqlspec/statement/builder/_merge.py +0 -95
- sqlspec/statement/cache.py +0 -50
- sqlspec/statement/filters.py +0 -625
- sqlspec/statement/parameters.py +0 -956
- sqlspec/statement/pipelines/__init__.py +0 -210
- sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
- sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
- sqlspec/statement/pipelines/context.py +0 -109
- sqlspec/statement/pipelines/transformers/__init__.py +0 -7
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
- sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
- sqlspec/statement/pipelines/validators/__init__.py +0 -23
- sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
- sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
- sqlspec/statement/pipelines/validators/_performance.py +0 -714
- sqlspec/statement/pipelines/validators/_security.py +0 -967
- sqlspec/statement/result.py +0 -435
- sqlspec/statement/sql.py +0 -1774
- sqlspec/utils/cached_property.py +0 -25
- sqlspec/utils/statement_hashing.py +0 -203
- sqlspec-0.14.1.dist-info/RECORD +0 -145
- /sqlspec/{statement/builder → builder}/mixins/_delete_operations.py +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.14.1.dist-info → sqlspec-0.16.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/statement/result.py
DELETED
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
"""SQL statement result classes for handling different types of SQL operations."""
|
|
2
|
-
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
from collections.abc import Mapping, Sequence
|
|
5
|
-
from dataclasses import dataclass, field
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Generic, Literal, Optional, Union
|
|
7
|
-
|
|
8
|
-
from typing_extensions import TypeVar
|
|
9
|
-
|
|
10
|
-
from sqlspec.typing import ArrowTable, RowT
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from sqlspec.statement.sql import SQL
|
|
14
|
-
|
|
15
|
-
__all__ = ("ArrowResult", "SQLResult", "StatementResult")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
T = TypeVar("T")
|
|
19
|
-
|
|
20
|
-
OperationType = Literal["SELECT", "INSERT", "UPDATE", "DELETE", "EXECUTE", "SCRIPT"]
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@dataclass
|
|
24
|
-
class StatementResult(ABC, Generic[RowT]):
|
|
25
|
-
"""Base class for SQL statement execution results.
|
|
26
|
-
|
|
27
|
-
This class provides a common interface for handling different types of
|
|
28
|
-
SQL operation results. Subclasses implement specific behavior for
|
|
29
|
-
SELECT, INSERT/UPDATE/DELETE, and script operations.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
statement: The original SQL statement that was executed.
|
|
33
|
-
data: The result data from the operation.
|
|
34
|
-
rows_affected: Number of rows affected by the operation (if applicable).
|
|
35
|
-
last_inserted_id: Last inserted ID (if applicable).
|
|
36
|
-
execution_time: Time taken to execute the statement in seconds.
|
|
37
|
-
metadata: Additional metadata about the operation.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
statement: "SQL"
|
|
41
|
-
"""The original SQL statement that was executed."""
|
|
42
|
-
data: "Any"
|
|
43
|
-
"""The result data from the operation."""
|
|
44
|
-
rows_affected: int = 0
|
|
45
|
-
"""Number of rows affected by the operation."""
|
|
46
|
-
last_inserted_id: Optional[Union[int, str]] = None
|
|
47
|
-
"""Last inserted ID from the operation."""
|
|
48
|
-
execution_time: Optional[float] = None
|
|
49
|
-
"""Time taken to execute the statement in seconds."""
|
|
50
|
-
metadata: "dict[str, Any]" = field(default_factory=dict)
|
|
51
|
-
"""Additional metadata about the operation."""
|
|
52
|
-
|
|
53
|
-
@abstractmethod
|
|
54
|
-
def is_success(self) -> bool:
|
|
55
|
-
"""Check if the operation was successful.
|
|
56
|
-
|
|
57
|
-
Returns:
|
|
58
|
-
True if the operation completed successfully, False otherwise.
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
@abstractmethod
|
|
62
|
-
def get_data(self) -> "Any":
|
|
63
|
-
"""Get the processed data from the result.
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
The processed result data in an appropriate format.
|
|
67
|
-
"""
|
|
68
|
-
|
|
69
|
-
def get_metadata(self, key: str, default: Any = None) -> Any:
|
|
70
|
-
"""Get metadata value by key.
|
|
71
|
-
|
|
72
|
-
Args:
|
|
73
|
-
key: The metadata key to retrieve.
|
|
74
|
-
default: Default value if key is not found.
|
|
75
|
-
|
|
76
|
-
Returns:
|
|
77
|
-
The metadata value or default.
|
|
78
|
-
"""
|
|
79
|
-
return self.metadata.get(key, default)
|
|
80
|
-
|
|
81
|
-
def set_metadata(self, key: str, value: Any) -> None:
|
|
82
|
-
"""Set metadata value by key.
|
|
83
|
-
|
|
84
|
-
Args:
|
|
85
|
-
key: The metadata key to set.
|
|
86
|
-
value: The value to set.
|
|
87
|
-
"""
|
|
88
|
-
self.metadata[key] = value
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@dataclass
|
|
92
|
-
class SQLResult(StatementResult[RowT], Generic[RowT]):
|
|
93
|
-
"""Unified result class for SQL operations that return a list of rows
|
|
94
|
-
or affect rows (e.g., SELECT, INSERT, UPDATE, DELETE).
|
|
95
|
-
|
|
96
|
-
For DML operations with RETURNING clauses, the returned data will be in `self.data`.
|
|
97
|
-
The `operation_type` attribute helps distinguish the nature of the operation.
|
|
98
|
-
|
|
99
|
-
For script execution, this class also tracks multiple statement results and errors.
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
data: "list[RowT]" = field(default_factory=list)
|
|
103
|
-
error: Optional[Exception] = None
|
|
104
|
-
operation_type: OperationType = "SELECT"
|
|
105
|
-
operation_index: Optional[int] = None
|
|
106
|
-
pipeline_sql: Optional["SQL"] = None
|
|
107
|
-
parameters: Optional[Any] = None
|
|
108
|
-
column_names: "list[str]" = field(default_factory=list)
|
|
109
|
-
total_count: Optional[int] = None
|
|
110
|
-
has_more: bool = False
|
|
111
|
-
inserted_ids: "list[Union[int, str]]" = field(default_factory=list)
|
|
112
|
-
statement_results: "list[SQLResult[Any]]" = field(default_factory=list)
|
|
113
|
-
"""Individual statement results when executing scripts."""
|
|
114
|
-
errors: "list[str]" = field(default_factory=list)
|
|
115
|
-
"""Errors encountered during script execution."""
|
|
116
|
-
total_statements: int = 0
|
|
117
|
-
"""Total number of statements in the script."""
|
|
118
|
-
successful_statements: int = 0
|
|
119
|
-
"""Number of statements that executed successfully."""
|
|
120
|
-
|
|
121
|
-
def __post_init__(self) -> None:
|
|
122
|
-
"""Post-initialization to infer column names and total count if not provided."""
|
|
123
|
-
if not self.column_names and self.data and isinstance(self.data[0], Mapping):
|
|
124
|
-
self.column_names = list(self.data[0].keys())
|
|
125
|
-
if self.total_count is None:
|
|
126
|
-
self.total_count = len(self.data) if self.data is not None else 0
|
|
127
|
-
|
|
128
|
-
def is_success(self) -> bool:
|
|
129
|
-
"""Check if the operation was successful.
|
|
130
|
-
|
|
131
|
-
- For SELECT: True if data is not None and rows_affected is not negative.
|
|
132
|
-
- For DML (INSERT, UPDATE, DELETE, EXECUTE): True if rows_affected is >= 0.
|
|
133
|
-
- For SCRIPT: True if no errors and all statements succeeded.
|
|
134
|
-
"""
|
|
135
|
-
op_type = self.operation_type.upper()
|
|
136
|
-
|
|
137
|
-
if op_type == "SCRIPT" or self.statement_results:
|
|
138
|
-
return not self.errors and self.total_statements == self.successful_statements
|
|
139
|
-
|
|
140
|
-
if op_type == "SELECT":
|
|
141
|
-
return self.data is not None and (self.rows_affected is None or self.rows_affected >= 0)
|
|
142
|
-
|
|
143
|
-
if op_type in {"INSERT", "UPDATE", "DELETE", "EXECUTE"}:
|
|
144
|
-
return self.rows_affected is not None and self.rows_affected >= 0
|
|
145
|
-
|
|
146
|
-
return False
|
|
147
|
-
|
|
148
|
-
def get_data(self) -> "Union[list[RowT], dict[str, Any]]":
|
|
149
|
-
"""Get the data from the result.
|
|
150
|
-
|
|
151
|
-
For regular operations, returns the list of rows.
|
|
152
|
-
For script operations, returns a summary dictionary.
|
|
153
|
-
"""
|
|
154
|
-
if self.operation_type.upper() == "SCRIPT" or self.statement_results:
|
|
155
|
-
return {
|
|
156
|
-
"total_statements": self.total_statements,
|
|
157
|
-
"successful_statements": self.successful_statements,
|
|
158
|
-
"failed_statements": self.total_statements - self.successful_statements,
|
|
159
|
-
"errors": self.errors,
|
|
160
|
-
"statement_results": self.statement_results,
|
|
161
|
-
"total_rows_affected": self.get_total_rows_affected(),
|
|
162
|
-
}
|
|
163
|
-
return self.data
|
|
164
|
-
|
|
165
|
-
def add_statement_result(self, result: "SQLResult[Any]") -> None:
|
|
166
|
-
"""Add a statement result to the script execution results."""
|
|
167
|
-
self.statement_results.append(result)
|
|
168
|
-
self.total_statements += 1
|
|
169
|
-
if result.is_success():
|
|
170
|
-
self.successful_statements += 1
|
|
171
|
-
|
|
172
|
-
def add_error(self, error: str) -> None:
|
|
173
|
-
"""Add an error message to the script execution errors."""
|
|
174
|
-
self.errors.append(error)
|
|
175
|
-
|
|
176
|
-
def get_statement_result(self, index: int) -> "Optional[SQLResult[Any]]":
|
|
177
|
-
"""Get a statement result by index."""
|
|
178
|
-
if 0 <= index < len(self.statement_results):
|
|
179
|
-
return self.statement_results[index]
|
|
180
|
-
return None
|
|
181
|
-
|
|
182
|
-
def get_total_rows_affected(self) -> int:
|
|
183
|
-
"""Get the total number of rows affected across all statements."""
|
|
184
|
-
if self.statement_results:
|
|
185
|
-
return sum(
|
|
186
|
-
stmt.rows_affected for stmt in self.statement_results if stmt.rows_affected and stmt.rows_affected > 0
|
|
187
|
-
)
|
|
188
|
-
return self.rows_affected if self.rows_affected and self.rows_affected > 0 else 0
|
|
189
|
-
|
|
190
|
-
@property
|
|
191
|
-
def num_rows(self) -> int:
|
|
192
|
-
return self.get_total_rows_affected()
|
|
193
|
-
|
|
194
|
-
@property
|
|
195
|
-
def num_columns(self) -> int:
|
|
196
|
-
"""Get the number of columns in the result data."""
|
|
197
|
-
return len(self.column_names) if self.column_names else 0
|
|
198
|
-
|
|
199
|
-
def get_errors(self) -> "list[str]":
|
|
200
|
-
"""Get all errors from script execution."""
|
|
201
|
-
return self.errors.copy()
|
|
202
|
-
|
|
203
|
-
def has_errors(self) -> bool:
|
|
204
|
-
"""Check if there are any errors from script execution."""
|
|
205
|
-
return len(self.errors) > 0
|
|
206
|
-
|
|
207
|
-
def get_first(self) -> "Optional[RowT]":
|
|
208
|
-
"""Get the first row from the result, if any."""
|
|
209
|
-
return self.data[0] if self.data else None
|
|
210
|
-
|
|
211
|
-
def get_count(self) -> int:
|
|
212
|
-
"""Get the number of rows in the current result set (e.g., a page of data)."""
|
|
213
|
-
return len(self.data) if self.data is not None else 0
|
|
214
|
-
|
|
215
|
-
def is_empty(self) -> bool:
|
|
216
|
-
"""Check if the result set (self.data) is empty."""
|
|
217
|
-
return not self.data
|
|
218
|
-
|
|
219
|
-
def get_affected_count(self) -> int:
|
|
220
|
-
"""Get the number of rows affected by a DML operation."""
|
|
221
|
-
return self.rows_affected or 0
|
|
222
|
-
|
|
223
|
-
def was_inserted(self) -> bool:
|
|
224
|
-
"""Check if this was an INSERT operation."""
|
|
225
|
-
return self.operation_type.upper() == "INSERT"
|
|
226
|
-
|
|
227
|
-
def was_updated(self) -> bool:
|
|
228
|
-
"""Check if this was an UPDATE operation."""
|
|
229
|
-
return self.operation_type.upper() == "UPDATE"
|
|
230
|
-
|
|
231
|
-
def was_deleted(self) -> bool:
|
|
232
|
-
"""Check if this was a DELETE operation."""
|
|
233
|
-
return self.operation_type.upper() == "DELETE"
|
|
234
|
-
|
|
235
|
-
def __len__(self) -> int:
|
|
236
|
-
"""Get the number of rows in the result set.
|
|
237
|
-
|
|
238
|
-
Returns:
|
|
239
|
-
Number of rows in the data.
|
|
240
|
-
"""
|
|
241
|
-
return len(self.data) if self.data is not None else 0
|
|
242
|
-
|
|
243
|
-
def __getitem__(self, index: int) -> "RowT":
|
|
244
|
-
"""Get a row by index.
|
|
245
|
-
|
|
246
|
-
Args:
|
|
247
|
-
index: Row index
|
|
248
|
-
|
|
249
|
-
Returns:
|
|
250
|
-
The row at the specified index
|
|
251
|
-
|
|
252
|
-
Raises:
|
|
253
|
-
TypeError: If data is None
|
|
254
|
-
"""
|
|
255
|
-
if self.data is None:
|
|
256
|
-
msg = "No data available"
|
|
257
|
-
raise TypeError(msg)
|
|
258
|
-
return self.data[index]
|
|
259
|
-
|
|
260
|
-
def all(self) -> "list[RowT]":
|
|
261
|
-
"""Return all rows as a list.
|
|
262
|
-
|
|
263
|
-
Returns:
|
|
264
|
-
List of all rows in the result
|
|
265
|
-
"""
|
|
266
|
-
if self.data is None:
|
|
267
|
-
return []
|
|
268
|
-
return self.data
|
|
269
|
-
|
|
270
|
-
def one(self) -> "RowT":
|
|
271
|
-
"""Return exactly one row.
|
|
272
|
-
|
|
273
|
-
Returns:
|
|
274
|
-
The single row
|
|
275
|
-
|
|
276
|
-
Raises:
|
|
277
|
-
ValueError: If no results or more than one result
|
|
278
|
-
"""
|
|
279
|
-
if self.data is None or len(self.data) == 0:
|
|
280
|
-
msg = "No result found, exactly one row expected"
|
|
281
|
-
raise ValueError(msg)
|
|
282
|
-
if len(self.data) > 1:
|
|
283
|
-
msg = f"Multiple results found ({len(self.data)}), exactly one row expected"
|
|
284
|
-
raise ValueError(msg)
|
|
285
|
-
return self.data[0]
|
|
286
|
-
|
|
287
|
-
def one_or_none(self) -> "Optional[RowT]":
|
|
288
|
-
"""Return at most one row.
|
|
289
|
-
|
|
290
|
-
Returns:
|
|
291
|
-
The single row or None if no results
|
|
292
|
-
|
|
293
|
-
Raises:
|
|
294
|
-
ValueError: If more than one result
|
|
295
|
-
"""
|
|
296
|
-
if self.data is None or len(self.data) == 0:
|
|
297
|
-
return None
|
|
298
|
-
if len(self.data) > 1:
|
|
299
|
-
msg = f"Multiple results found ({len(self.data)}), at most one row expected"
|
|
300
|
-
raise ValueError(msg)
|
|
301
|
-
return self.data[0]
|
|
302
|
-
|
|
303
|
-
def scalar(self) -> Any:
|
|
304
|
-
"""Return the first column of the first row.
|
|
305
|
-
|
|
306
|
-
Returns:
|
|
307
|
-
The scalar value from first column of first row
|
|
308
|
-
|
|
309
|
-
Raises:
|
|
310
|
-
ValueError: If no results
|
|
311
|
-
"""
|
|
312
|
-
row = self.one()
|
|
313
|
-
if isinstance(row, Mapping):
|
|
314
|
-
if not row:
|
|
315
|
-
msg = "Row has no columns"
|
|
316
|
-
raise ValueError(msg)
|
|
317
|
-
first_key = next(iter(row.keys()))
|
|
318
|
-
return row[first_key]
|
|
319
|
-
if isinstance(row, Sequence) and not isinstance(row, (str, bytes)):
|
|
320
|
-
if len(row) == 0:
|
|
321
|
-
msg = "Row has no columns"
|
|
322
|
-
raise ValueError(msg)
|
|
323
|
-
return row[0]
|
|
324
|
-
return row
|
|
325
|
-
|
|
326
|
-
def scalar_or_none(self) -> Any:
|
|
327
|
-
"""Return the first column of the first row, or None if no results.
|
|
328
|
-
|
|
329
|
-
Returns:
|
|
330
|
-
The scalar value from first column of first row, or None
|
|
331
|
-
"""
|
|
332
|
-
row = self.one_or_none()
|
|
333
|
-
if row is None:
|
|
334
|
-
return None
|
|
335
|
-
|
|
336
|
-
if isinstance(row, Mapping):
|
|
337
|
-
if not row:
|
|
338
|
-
return None
|
|
339
|
-
first_key = next(iter(row.keys()))
|
|
340
|
-
return row[first_key]
|
|
341
|
-
if isinstance(row, Sequence) and not isinstance(row, (str, bytes)):
|
|
342
|
-
if len(row) == 0:
|
|
343
|
-
return None
|
|
344
|
-
return row[0]
|
|
345
|
-
return row
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
@dataclass
|
|
349
|
-
class ArrowResult(StatementResult[ArrowTable]):
|
|
350
|
-
"""Result class for SQL operations that return Apache Arrow data.
|
|
351
|
-
|
|
352
|
-
This class is used when database drivers support returning results as
|
|
353
|
-
Apache Arrow format for high-performance data interchange, especially
|
|
354
|
-
useful for analytics workloads and data science applications.
|
|
355
|
-
|
|
356
|
-
Args:
|
|
357
|
-
statement: The original SQL statement that was executed.
|
|
358
|
-
data: The Apache Arrow Table containing the result data.
|
|
359
|
-
schema: Optional Arrow schema information.
|
|
360
|
-
"""
|
|
361
|
-
|
|
362
|
-
schema: Optional["dict[str, Any]"] = None
|
|
363
|
-
"""Optional Arrow schema information."""
|
|
364
|
-
data: "ArrowTable"
|
|
365
|
-
"""The result data from the operation."""
|
|
366
|
-
|
|
367
|
-
def is_success(self) -> bool:
|
|
368
|
-
"""Check if the operation was successful.
|
|
369
|
-
|
|
370
|
-
Returns:
|
|
371
|
-
True if Arrow table data is available, False otherwise.
|
|
372
|
-
"""
|
|
373
|
-
return self.data is not None
|
|
374
|
-
|
|
375
|
-
def get_data(self) -> "ArrowTable":
|
|
376
|
-
"""Get the Apache Arrow Table from the result.
|
|
377
|
-
|
|
378
|
-
Returns:
|
|
379
|
-
The Arrow table containing the result data.
|
|
380
|
-
|
|
381
|
-
Raises:
|
|
382
|
-
ValueError: If no Arrow table is available.
|
|
383
|
-
"""
|
|
384
|
-
if self.data is None:
|
|
385
|
-
msg = "No Arrow table available for this result"
|
|
386
|
-
raise ValueError(msg)
|
|
387
|
-
return self.data
|
|
388
|
-
|
|
389
|
-
@property
|
|
390
|
-
def column_names(self) -> "list[str]":
|
|
391
|
-
"""Get the column names from the Arrow table.
|
|
392
|
-
|
|
393
|
-
Returns:
|
|
394
|
-
List of column names.
|
|
395
|
-
|
|
396
|
-
Raises:
|
|
397
|
-
ValueError: If no Arrow table is available.
|
|
398
|
-
"""
|
|
399
|
-
if self.data is None:
|
|
400
|
-
msg = "No Arrow table available"
|
|
401
|
-
raise ValueError(msg)
|
|
402
|
-
|
|
403
|
-
return self.data.column_names
|
|
404
|
-
|
|
405
|
-
@property
|
|
406
|
-
def num_rows(self) -> int:
|
|
407
|
-
"""Get the number of rows in the Arrow table.
|
|
408
|
-
|
|
409
|
-
Returns:
|
|
410
|
-
Number of rows.
|
|
411
|
-
|
|
412
|
-
Raises:
|
|
413
|
-
ValueError: If no Arrow table is available.
|
|
414
|
-
"""
|
|
415
|
-
if self.data is None:
|
|
416
|
-
msg = "No Arrow table available"
|
|
417
|
-
raise ValueError(msg)
|
|
418
|
-
|
|
419
|
-
return self.data.num_rows
|
|
420
|
-
|
|
421
|
-
@property
|
|
422
|
-
def num_columns(self) -> int:
|
|
423
|
-
"""Get the number of columns in the Arrow table.
|
|
424
|
-
|
|
425
|
-
Returns:
|
|
426
|
-
Number of columns.
|
|
427
|
-
|
|
428
|
-
Raises:
|
|
429
|
-
ValueError: If no Arrow table is available.
|
|
430
|
-
"""
|
|
431
|
-
if self.data is None:
|
|
432
|
-
msg = "No Arrow table available"
|
|
433
|
-
raise ValueError(msg)
|
|
434
|
-
|
|
435
|
-
return self.data.num_columns
|