sqlspec 0.10.0__py3-none-any.whl → 0.11.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/_typing.py +24 -32
- sqlspec/adapters/adbc/config.py +1 -1
- sqlspec/adapters/adbc/driver.py +336 -165
- sqlspec/adapters/aiosqlite/driver.py +211 -126
- sqlspec/adapters/asyncmy/driver.py +164 -68
- sqlspec/adapters/asyncpg/config.py +3 -1
- sqlspec/adapters/asyncpg/driver.py +190 -231
- sqlspec/adapters/bigquery/driver.py +178 -169
- sqlspec/adapters/duckdb/driver.py +175 -84
- sqlspec/adapters/oracledb/driver.py +224 -90
- sqlspec/adapters/psqlpy/driver.py +267 -187
- sqlspec/adapters/psycopg/driver.py +138 -184
- sqlspec/adapters/sqlite/driver.py +153 -121
- sqlspec/base.py +57 -45
- sqlspec/extensions/litestar/__init__.py +3 -12
- sqlspec/extensions/litestar/config.py +22 -7
- sqlspec/extensions/litestar/handlers.py +142 -85
- sqlspec/extensions/litestar/plugin.py +9 -8
- sqlspec/extensions/litestar/providers.py +521 -0
- sqlspec/filters.py +214 -11
- sqlspec/mixins.py +152 -2
- sqlspec/statement.py +276 -271
- sqlspec/typing.py +18 -1
- sqlspec/utils/__init__.py +2 -2
- sqlspec/utils/singleton.py +35 -0
- sqlspec/utils/sync_tools.py +90 -151
- sqlspec/utils/text.py +68 -5
- {sqlspec-0.10.0.dist-info → sqlspec-0.11.0.dist-info}/METADATA +5 -1
- {sqlspec-0.10.0.dist-info → sqlspec-0.11.0.dist-info}/RECORD +32 -30
- {sqlspec-0.10.0.dist-info → sqlspec-0.11.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.10.0.dist-info → sqlspec-0.11.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.10.0.dist-info → sqlspec-0.11.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import datetime
|
|
3
|
+
import logging
|
|
3
4
|
from collections.abc import Iterator, Sequence
|
|
4
5
|
from decimal import Decimal
|
|
5
6
|
from typing import (
|
|
@@ -12,19 +13,21 @@ from typing import (
|
|
|
12
13
|
overload,
|
|
13
14
|
)
|
|
14
15
|
|
|
15
|
-
import sqlglot
|
|
16
16
|
from google.cloud import bigquery
|
|
17
17
|
from google.cloud.bigquery import Client
|
|
18
18
|
from google.cloud.bigquery.job import QueryJob, QueryJobConfig
|
|
19
19
|
from google.cloud.exceptions import NotFound
|
|
20
20
|
|
|
21
21
|
from sqlspec.base import SyncDriverAdapterProtocol
|
|
22
|
-
from sqlspec.exceptions import NotFoundError, SQLSpecError
|
|
22
|
+
from sqlspec.exceptions import NotFoundError, ParameterStyleMismatchError, SQLSpecError
|
|
23
|
+
from sqlspec.filters import StatementFilter
|
|
23
24
|
from sqlspec.mixins import (
|
|
25
|
+
ResultConverter,
|
|
24
26
|
SQLTranslatorMixin,
|
|
25
27
|
SyncArrowBulkOperationsMixin,
|
|
26
28
|
SyncParquetExportMixin,
|
|
27
29
|
)
|
|
30
|
+
from sqlspec.statement import SQLStatement
|
|
28
31
|
from sqlspec.typing import ArrowTable, ModelDTOT, StatementParameterType, T
|
|
29
32
|
|
|
30
33
|
if TYPE_CHECKING:
|
|
@@ -35,12 +38,15 @@ __all__ = ("BigQueryConnection", "BigQueryDriver")
|
|
|
35
38
|
|
|
36
39
|
BigQueryConnection = Client
|
|
37
40
|
|
|
41
|
+
logger = logging.getLogger("sqlspec")
|
|
42
|
+
|
|
38
43
|
|
|
39
44
|
class BigQueryDriver(
|
|
40
45
|
SyncDriverAdapterProtocol["BigQueryConnection"],
|
|
41
46
|
SyncArrowBulkOperationsMixin["BigQueryConnection"],
|
|
42
47
|
SyncParquetExportMixin["BigQueryConnection"],
|
|
43
48
|
SQLTranslatorMixin["BigQueryConnection"],
|
|
49
|
+
ResultConverter,
|
|
44
50
|
):
|
|
45
51
|
"""Synchronous BigQuery Driver Adapter."""
|
|
46
52
|
|
|
@@ -55,7 +61,7 @@ class BigQueryDriver(
|
|
|
55
61
|
)
|
|
56
62
|
|
|
57
63
|
@staticmethod
|
|
58
|
-
def _get_bq_param_type(value: Any) -> "tuple[Optional[str], Optional[str]]":
|
|
64
|
+
def _get_bq_param_type(value: Any) -> "tuple[Optional[str], Optional[str]]":
|
|
59
65
|
if isinstance(value, bool):
|
|
60
66
|
return "BOOL", None
|
|
61
67
|
if isinstance(value, int):
|
|
@@ -105,10 +111,56 @@ class BigQueryDriver(
|
|
|
105
111
|
|
|
106
112
|
return None, None # Unsupported type
|
|
107
113
|
|
|
108
|
-
def
|
|
114
|
+
def _process_sql_params(
|
|
109
115
|
self,
|
|
110
116
|
sql: str,
|
|
111
117
|
parameters: "Optional[StatementParameterType]" = None,
|
|
118
|
+
/,
|
|
119
|
+
*filters: StatementFilter,
|
|
120
|
+
**kwargs: Any,
|
|
121
|
+
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
|
|
122
|
+
"""Process SQL and parameters using SQLStatement with dialect support.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
sql: The SQL statement to process.
|
|
126
|
+
parameters: The parameters to bind to the statement.
|
|
127
|
+
*filters: Statement filters to apply.
|
|
128
|
+
**kwargs: Additional keyword arguments.
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
ParameterStyleMismatchError: If pre-formatted BigQuery parameters are mixed with keyword arguments.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
A tuple of (sql, parameters) ready for execution.
|
|
135
|
+
"""
|
|
136
|
+
# Special case: check for pre-formatted BQ parameters
|
|
137
|
+
if (
|
|
138
|
+
isinstance(parameters, (list, tuple))
|
|
139
|
+
and parameters
|
|
140
|
+
and all(isinstance(p, (bigquery.ScalarQueryParameter, bigquery.ArrayQueryParameter)) for p in parameters)
|
|
141
|
+
):
|
|
142
|
+
if kwargs:
|
|
143
|
+
msg = "Cannot mix pre-formatted BigQuery parameters with keyword arguments."
|
|
144
|
+
raise ParameterStyleMismatchError(msg)
|
|
145
|
+
return sql, parameters
|
|
146
|
+
|
|
147
|
+
statement = SQLStatement(sql, parameters, kwargs=kwargs, dialect=self.dialect)
|
|
148
|
+
|
|
149
|
+
# Apply any filters
|
|
150
|
+
for filter_obj in filters:
|
|
151
|
+
statement = statement.apply_filter(filter_obj)
|
|
152
|
+
|
|
153
|
+
# Process the statement for execution
|
|
154
|
+
processed_sql, processed_params, _ = statement.process()
|
|
155
|
+
|
|
156
|
+
return processed_sql, processed_params
|
|
157
|
+
|
|
158
|
+
def _run_query_job(
|
|
159
|
+
self,
|
|
160
|
+
sql: str,
|
|
161
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
162
|
+
/,
|
|
163
|
+
*filters: StatementFilter,
|
|
112
164
|
connection: "Optional[BigQueryConnection]" = None,
|
|
113
165
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
114
166
|
is_script: bool = False,
|
|
@@ -125,108 +177,62 @@ class BigQueryDriver(
|
|
|
125
177
|
else:
|
|
126
178
|
final_job_config = QueryJobConfig() # Create a fresh config
|
|
127
179
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
param_style: Optional[str] = None # 'named' (@), 'qmark' (?)
|
|
131
|
-
use_preformatted_params = False
|
|
132
|
-
final_sql = sql # Default to original SQL
|
|
180
|
+
# Process SQL and parameters
|
|
181
|
+
final_sql, processed_params = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
133
182
|
|
|
134
|
-
#
|
|
183
|
+
# Handle pre-formatted parameters
|
|
135
184
|
if (
|
|
136
|
-
isinstance(
|
|
137
|
-
and
|
|
138
|
-
and all(
|
|
185
|
+
isinstance(processed_params, (list, tuple))
|
|
186
|
+
and processed_params
|
|
187
|
+
and all(
|
|
188
|
+
isinstance(p, (bigquery.ScalarQueryParameter, bigquery.ArrayQueryParameter)) for p in processed_params
|
|
189
|
+
)
|
|
139
190
|
):
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
final_job_config.query_parameters =
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if all(
|
|
158
|
-
not isinstance(p, (bigquery.ScalarQueryParameter, bigquery.ArrayQueryParameter)) for p in parameters
|
|
159
|
-
):
|
|
160
|
-
params = list(parameters)
|
|
161
|
-
param_style = "qmark"
|
|
162
|
-
else:
|
|
163
|
-
# Mixed list or non-BQ parameter objects
|
|
164
|
-
msg = "Invalid mix of parameter types in list. Use only primitive values or only BigQuery QueryParameter objects."
|
|
165
|
-
raise SQLSpecError(msg)
|
|
166
|
-
|
|
167
|
-
elif kwargs:
|
|
168
|
-
params = kwargs
|
|
169
|
-
param_style = "named"
|
|
170
|
-
elif parameters is not None and not isinstance(
|
|
171
|
-
parameters, (bigquery.ScalarQueryParameter, bigquery.ArrayQueryParameter)
|
|
172
|
-
):
|
|
173
|
-
# Could be a single primitive value for positional
|
|
174
|
-
params = [parameters]
|
|
175
|
-
param_style = "qmark"
|
|
176
|
-
elif parameters is not None: # Single BQ parameter object
|
|
177
|
-
msg = "Single BigQuery QueryParameter objects should be passed within a list."
|
|
178
|
-
raise SQLSpecError(msg)
|
|
179
|
-
|
|
180
|
-
# Use sqlglot to transpile ONLY if not a script and not preformatted
|
|
181
|
-
if not is_script and not use_preformatted_params:
|
|
182
|
-
try:
|
|
183
|
-
# Transpile for syntax normalization/dialect conversion if needed
|
|
184
|
-
# Use BigQuery dialect for both reading and writing
|
|
185
|
-
final_sql = sqlglot.transpile(sql, read=self.dialect, write=self.dialect)[0]
|
|
186
|
-
except Exception as e:
|
|
187
|
-
# Catch potential sqlglot errors
|
|
188
|
-
msg = f"SQL transpilation failed using sqlglot: {e!s}" # Adjusted message
|
|
189
|
-
raise SQLSpecError(msg) from e
|
|
190
|
-
# else: If preformatted_params, final_sql remains the original sql
|
|
191
|
-
|
|
192
|
-
# --- Parameter Handling Logic --- (Moved outside the transpilation try/except)
|
|
193
|
-
# Prepare BQ parameters based on style, ONLY if not preformatted
|
|
194
|
-
if not use_preformatted_params:
|
|
195
|
-
if param_style == "named" and params:
|
|
196
|
-
# Convert dict params to BQ ScalarQueryParameter
|
|
197
|
-
if isinstance(params, dict):
|
|
198
|
-
final_job_config.query_parameters = [
|
|
199
|
-
bigquery.ScalarQueryParameter(name, self._get_bq_param_type(value)[0], value)
|
|
200
|
-
for name, value in params.items()
|
|
201
|
-
]
|
|
202
|
-
else:
|
|
203
|
-
# This path should ideally not be reached if param_style logic is correct
|
|
204
|
-
msg = f"Internal error: Parameter style is 'named' but parameters are not a dict: {type(params)}"
|
|
205
|
-
raise SQLSpecError(msg)
|
|
206
|
-
elif param_style == "qmark" and params:
|
|
207
|
-
# Convert list params to BQ ScalarQueryParameter
|
|
208
|
-
final_job_config.query_parameters = [
|
|
209
|
-
bigquery.ScalarQueryParameter(None, self._get_bq_param_type(value)[0], value) for value in params
|
|
210
|
-
]
|
|
211
|
-
|
|
212
|
-
# --- Parameter Handling Logic --- End
|
|
213
|
-
|
|
214
|
-
# Determine which kwargs to pass to the actual query method.
|
|
215
|
-
# We only want to pass kwargs that were *not* treated as SQL parameters.
|
|
191
|
+
final_job_config.query_parameters = list(processed_params)
|
|
192
|
+
# Convert regular parameters to BigQuery parameters
|
|
193
|
+
elif isinstance(processed_params, dict):
|
|
194
|
+
# Convert dict params to BQ ScalarQueryParameter
|
|
195
|
+
final_job_config.query_parameters = [
|
|
196
|
+
bigquery.ScalarQueryParameter(name, self._get_bq_param_type(value)[0], value)
|
|
197
|
+
for name, value in processed_params.items()
|
|
198
|
+
]
|
|
199
|
+
elif isinstance(processed_params, (list, tuple)):
|
|
200
|
+
# Convert list params to BQ ScalarQueryParameter
|
|
201
|
+
final_job_config.query_parameters = [
|
|
202
|
+
bigquery.ScalarQueryParameter(None, self._get_bq_param_type(value)[0], value)
|
|
203
|
+
for value in processed_params
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
# Determine which kwargs to pass to the actual query method
|
|
207
|
+
# We only want to pass kwargs that were *not* treated as SQL parameters
|
|
216
208
|
final_query_kwargs = {}
|
|
217
209
|
if parameters is not None and kwargs: # Params came via arg, kwargs are separate
|
|
218
210
|
final_query_kwargs = kwargs
|
|
219
|
-
# Else: If params came via kwargs, they are already handled, so don't pass them again
|
|
211
|
+
# Else: If params came via kwargs, they are already handled, so don't pass them again
|
|
220
212
|
|
|
221
213
|
# Execute query
|
|
222
214
|
return conn.query(
|
|
223
215
|
final_sql,
|
|
224
216
|
job_config=final_job_config,
|
|
225
|
-
**final_query_kwargs,
|
|
217
|
+
**final_query_kwargs,
|
|
226
218
|
)
|
|
227
219
|
|
|
228
|
-
@
|
|
220
|
+
@overload
|
|
229
221
|
def _rows_to_results(
|
|
222
|
+
self,
|
|
223
|
+
rows: "Iterator[Row]",
|
|
224
|
+
schema: "Sequence[SchemaField]",
|
|
225
|
+
schema_type: "type[ModelDTOT]",
|
|
226
|
+
) -> Sequence[ModelDTOT]: ...
|
|
227
|
+
@overload
|
|
228
|
+
def _rows_to_results(
|
|
229
|
+
self,
|
|
230
|
+
rows: "Iterator[Row]",
|
|
231
|
+
schema: "Sequence[SchemaField]",
|
|
232
|
+
schema_type: None = None,
|
|
233
|
+
) -> Sequence[dict[str, Any]]: ...
|
|
234
|
+
def _rows_to_results(
|
|
235
|
+
self,
|
|
230
236
|
rows: "Iterator[Row]",
|
|
231
237
|
schema: "Sequence[SchemaField]",
|
|
232
238
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
@@ -249,14 +255,8 @@ class BigQueryDriver(
|
|
|
249
255
|
row_dict[key] = value # type: ignore[assignment]
|
|
250
256
|
else:
|
|
251
257
|
row_dict[key] = value
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
processed_results.append(schema_type(**row_dict))
|
|
255
|
-
else:
|
|
256
|
-
processed_results.append(row_dict) # type: ignore[arg-type]
|
|
257
|
-
if schema_type:
|
|
258
|
-
return cast("Sequence[ModelDTOT]", processed_results)
|
|
259
|
-
return cast("Sequence[dict[str, Any]]", processed_results)
|
|
258
|
+
processed_results.append(row_dict)
|
|
259
|
+
return self.to_schema(processed_results, schema_type=schema_type)
|
|
260
260
|
|
|
261
261
|
@overload
|
|
262
262
|
def select(
|
|
@@ -264,7 +264,7 @@ class BigQueryDriver(
|
|
|
264
264
|
sql: str,
|
|
265
265
|
parameters: "Optional[StatementParameterType]" = None,
|
|
266
266
|
/,
|
|
267
|
-
|
|
267
|
+
*filters: StatementFilter,
|
|
268
268
|
connection: "Optional[BigQueryConnection]" = None,
|
|
269
269
|
schema_type: None = None,
|
|
270
270
|
**kwargs: Any,
|
|
@@ -275,7 +275,7 @@ class BigQueryDriver(
|
|
|
275
275
|
sql: str,
|
|
276
276
|
parameters: "Optional[StatementParameterType]" = None,
|
|
277
277
|
/,
|
|
278
|
-
|
|
278
|
+
*filters: StatementFilter,
|
|
279
279
|
connection: "Optional[BigQueryConnection]" = None,
|
|
280
280
|
schema_type: "type[ModelDTOT]",
|
|
281
281
|
**kwargs: Any,
|
|
@@ -285,13 +285,29 @@ class BigQueryDriver(
|
|
|
285
285
|
sql: str,
|
|
286
286
|
parameters: "Optional[StatementParameterType]" = None,
|
|
287
287
|
/,
|
|
288
|
-
|
|
288
|
+
*filters: StatementFilter,
|
|
289
289
|
connection: "Optional[BigQueryConnection]" = None,
|
|
290
290
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
291
291
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
292
292
|
**kwargs: Any,
|
|
293
293
|
) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
|
|
294
|
-
|
|
294
|
+
"""Fetch data from the database.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
sql: The SQL query string.
|
|
298
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
299
|
+
*filters: Statement filters to apply.
|
|
300
|
+
connection: Optional connection override.
|
|
301
|
+
schema_type: Optional schema class for the result.
|
|
302
|
+
job_config: Optional job configuration.
|
|
303
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
List of row data as either model instances or dictionaries.
|
|
307
|
+
"""
|
|
308
|
+
query_job = self._run_query_job(
|
|
309
|
+
sql, parameters, *filters, connection=connection, job_config=job_config, **kwargs
|
|
310
|
+
)
|
|
295
311
|
return self._rows_to_results(query_job.result(), query_job.result().schema, schema_type)
|
|
296
312
|
|
|
297
313
|
@overload
|
|
@@ -300,7 +316,7 @@ class BigQueryDriver(
|
|
|
300
316
|
sql: str,
|
|
301
317
|
parameters: "Optional[StatementParameterType]" = None,
|
|
302
318
|
/,
|
|
303
|
-
|
|
319
|
+
*filters: StatementFilter,
|
|
304
320
|
connection: "Optional[BigQueryConnection]" = None,
|
|
305
321
|
schema_type: None = None,
|
|
306
322
|
**kwargs: Any,
|
|
@@ -311,7 +327,7 @@ class BigQueryDriver(
|
|
|
311
327
|
sql: str,
|
|
312
328
|
parameters: "Optional[StatementParameterType]" = None,
|
|
313
329
|
/,
|
|
314
|
-
|
|
330
|
+
*filters: StatementFilter,
|
|
315
331
|
connection: "Optional[BigQueryConnection]" = None,
|
|
316
332
|
schema_type: "type[ModelDTOT]",
|
|
317
333
|
**kwargs: Any,
|
|
@@ -321,13 +337,15 @@ class BigQueryDriver(
|
|
|
321
337
|
sql: str,
|
|
322
338
|
parameters: "Optional[StatementParameterType]" = None,
|
|
323
339
|
/,
|
|
324
|
-
|
|
340
|
+
*filters: StatementFilter,
|
|
325
341
|
connection: "Optional[BigQueryConnection]" = None,
|
|
326
342
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
327
343
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
328
344
|
**kwargs: Any,
|
|
329
345
|
) -> "Union[ModelDTOT, dict[str, Any]]":
|
|
330
|
-
query_job = self._run_query_job(
|
|
346
|
+
query_job = self._run_query_job(
|
|
347
|
+
sql, parameters, *filters, connection=connection, job_config=job_config, **kwargs
|
|
348
|
+
)
|
|
331
349
|
rows_iterator = query_job.result()
|
|
332
350
|
try:
|
|
333
351
|
# Pass the iterator containing only the first row to _rows_to_results
|
|
@@ -350,7 +368,7 @@ class BigQueryDriver(
|
|
|
350
368
|
sql: str,
|
|
351
369
|
parameters: "Optional[StatementParameterType]" = None,
|
|
352
370
|
/,
|
|
353
|
-
|
|
371
|
+
*filters: StatementFilter,
|
|
354
372
|
connection: "Optional[BigQueryConnection]" = None,
|
|
355
373
|
schema_type: None = None,
|
|
356
374
|
**kwargs: Any,
|
|
@@ -361,7 +379,7 @@ class BigQueryDriver(
|
|
|
361
379
|
sql: str,
|
|
362
380
|
parameters: "Optional[StatementParameterType]" = None,
|
|
363
381
|
/,
|
|
364
|
-
|
|
382
|
+
*filters: StatementFilter,
|
|
365
383
|
connection: "Optional[BigQueryConnection]" = None,
|
|
366
384
|
schema_type: "type[ModelDTOT]",
|
|
367
385
|
**kwargs: Any,
|
|
@@ -371,13 +389,15 @@ class BigQueryDriver(
|
|
|
371
389
|
sql: str,
|
|
372
390
|
parameters: "Optional[StatementParameterType]" = None,
|
|
373
391
|
/,
|
|
374
|
-
|
|
392
|
+
*filters: StatementFilter,
|
|
375
393
|
connection: "Optional[BigQueryConnection]" = None,
|
|
376
394
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
377
395
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
378
396
|
**kwargs: Any,
|
|
379
397
|
) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
|
|
380
|
-
query_job = self._run_query_job(
|
|
398
|
+
query_job = self._run_query_job(
|
|
399
|
+
sql, parameters, *filters, connection=connection, job_config=job_config, **kwargs
|
|
400
|
+
)
|
|
381
401
|
rows_iterator = query_job.result()
|
|
382
402
|
try:
|
|
383
403
|
first_row = next(rows_iterator)
|
|
@@ -395,7 +415,7 @@ class BigQueryDriver(
|
|
|
395
415
|
sql: str,
|
|
396
416
|
parameters: "Optional[StatementParameterType]" = None,
|
|
397
417
|
/,
|
|
398
|
-
|
|
418
|
+
*filters: StatementFilter,
|
|
399
419
|
connection: "Optional[BigQueryConnection]" = None,
|
|
400
420
|
schema_type: "Optional[type[T]]" = None,
|
|
401
421
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
@@ -407,7 +427,7 @@ class BigQueryDriver(
|
|
|
407
427
|
sql: str,
|
|
408
428
|
parameters: "Optional[StatementParameterType]" = None,
|
|
409
429
|
/,
|
|
410
|
-
|
|
430
|
+
*filters: StatementFilter,
|
|
411
431
|
connection: "Optional[BigQueryConnection]" = None,
|
|
412
432
|
schema_type: "type[T]",
|
|
413
433
|
**kwargs: Any,
|
|
@@ -417,14 +437,14 @@ class BigQueryDriver(
|
|
|
417
437
|
sql: str,
|
|
418
438
|
parameters: "Optional[StatementParameterType]" = None,
|
|
419
439
|
/,
|
|
420
|
-
|
|
440
|
+
*filters: StatementFilter,
|
|
421
441
|
connection: "Optional[BigQueryConnection]" = None,
|
|
422
442
|
schema_type: "Optional[type[T]]" = None,
|
|
423
443
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
424
444
|
**kwargs: Any,
|
|
425
445
|
) -> Union[T, Any]:
|
|
426
446
|
query_job = self._run_query_job(
|
|
427
|
-
sql
|
|
447
|
+
sql, parameters, *filters, connection=connection, job_config=job_config, **kwargs
|
|
428
448
|
)
|
|
429
449
|
rows = query_job.result()
|
|
430
450
|
try:
|
|
@@ -447,7 +467,7 @@ class BigQueryDriver(
|
|
|
447
467
|
sql: str,
|
|
448
468
|
parameters: "Optional[StatementParameterType]" = None,
|
|
449
469
|
/,
|
|
450
|
-
|
|
470
|
+
*filters: StatementFilter,
|
|
451
471
|
connection: "Optional[BigQueryConnection]" = None,
|
|
452
472
|
schema_type: None = None,
|
|
453
473
|
**kwargs: Any,
|
|
@@ -458,7 +478,7 @@ class BigQueryDriver(
|
|
|
458
478
|
sql: str,
|
|
459
479
|
parameters: "Optional[StatementParameterType]" = None,
|
|
460
480
|
/,
|
|
461
|
-
|
|
481
|
+
*filters: StatementFilter,
|
|
462
482
|
connection: "Optional[BigQueryConnection]" = None,
|
|
463
483
|
schema_type: "type[T]",
|
|
464
484
|
**kwargs: Any,
|
|
@@ -468,14 +488,19 @@ class BigQueryDriver(
|
|
|
468
488
|
sql: str,
|
|
469
489
|
parameters: "Optional[StatementParameterType]" = None,
|
|
470
490
|
/,
|
|
471
|
-
|
|
491
|
+
*filters: StatementFilter,
|
|
472
492
|
connection: "Optional[BigQueryConnection]" = None,
|
|
473
493
|
schema_type: "Optional[type[T]]" = None,
|
|
474
494
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
475
495
|
**kwargs: Any,
|
|
476
496
|
) -> "Optional[Union[T, Any]]":
|
|
477
497
|
query_job = self._run_query_job(
|
|
478
|
-
sql
|
|
498
|
+
sql,
|
|
499
|
+
parameters,
|
|
500
|
+
*filters,
|
|
501
|
+
connection=connection,
|
|
502
|
+
job_config=job_config,
|
|
503
|
+
**kwargs,
|
|
479
504
|
)
|
|
480
505
|
rows = query_job.result()
|
|
481
506
|
try:
|
|
@@ -496,7 +521,7 @@ class BigQueryDriver(
|
|
|
496
521
|
sql: str,
|
|
497
522
|
parameters: Optional[StatementParameterType] = None,
|
|
498
523
|
/,
|
|
499
|
-
|
|
524
|
+
*filters: StatementFilter,
|
|
500
525
|
connection: Optional["BigQueryConnection"] = None,
|
|
501
526
|
job_config: Optional[QueryJobConfig] = None,
|
|
502
527
|
**kwargs: Any,
|
|
@@ -507,7 +532,7 @@ class BigQueryDriver(
|
|
|
507
532
|
int: The number of rows affected by the DML statement.
|
|
508
533
|
"""
|
|
509
534
|
query_job = self._run_query_job(
|
|
510
|
-
sql
|
|
535
|
+
sql, parameters, *filters, connection=connection, job_config=job_config, **kwargs
|
|
511
536
|
)
|
|
512
537
|
# DML statements might not return rows, check job properties
|
|
513
538
|
# num_dml_affected_rows might be None initially, wait might be needed
|
|
@@ -520,7 +545,7 @@ class BigQueryDriver(
|
|
|
520
545
|
sql: str,
|
|
521
546
|
parameters: "Optional[StatementParameterType]" = None,
|
|
522
547
|
/,
|
|
523
|
-
|
|
548
|
+
*filters: StatementFilter,
|
|
524
549
|
connection: "Optional[BigQueryConnection]" = None,
|
|
525
550
|
schema_type: None = None,
|
|
526
551
|
**kwargs: Any,
|
|
@@ -531,7 +556,7 @@ class BigQueryDriver(
|
|
|
531
556
|
sql: str,
|
|
532
557
|
parameters: "Optional[StatementParameterType]" = None,
|
|
533
558
|
/,
|
|
534
|
-
|
|
559
|
+
*filters: StatementFilter,
|
|
535
560
|
connection: "Optional[BigQueryConnection]" = None,
|
|
536
561
|
schema_type: "type[ModelDTOT]",
|
|
537
562
|
**kwargs: Any,
|
|
@@ -541,7 +566,7 @@ class BigQueryDriver(
|
|
|
541
566
|
sql: str,
|
|
542
567
|
parameters: "Optional[StatementParameterType]" = None,
|
|
543
568
|
/,
|
|
544
|
-
|
|
569
|
+
*filters: StatementFilter,
|
|
545
570
|
connection: "Optional[BigQueryConnection]" = None,
|
|
546
571
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
547
572
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
@@ -556,7 +581,6 @@ class BigQueryDriver(
|
|
|
556
581
|
sql: str, # Expecting a script here
|
|
557
582
|
parameters: "Optional[StatementParameterType]" = None, # Parameters might be complex in scripts
|
|
558
583
|
/,
|
|
559
|
-
*,
|
|
560
584
|
connection: "Optional[BigQueryConnection]" = None,
|
|
561
585
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
562
586
|
**kwargs: Any,
|
|
@@ -567,8 +591,8 @@ class BigQueryDriver(
|
|
|
567
591
|
str: The job ID of the executed script.
|
|
568
592
|
"""
|
|
569
593
|
query_job = self._run_query_job(
|
|
570
|
-
sql
|
|
571
|
-
parameters
|
|
594
|
+
sql,
|
|
595
|
+
parameters,
|
|
572
596
|
connection=connection,
|
|
573
597
|
job_config=job_config,
|
|
574
598
|
is_script=True,
|
|
@@ -578,12 +602,12 @@ class BigQueryDriver(
|
|
|
578
602
|
|
|
579
603
|
# --- Mixin Implementations ---
|
|
580
604
|
|
|
581
|
-
def select_arrow( # pyright: ignore
|
|
605
|
+
def select_arrow( # pyright: ignore
|
|
582
606
|
self,
|
|
583
607
|
sql: str,
|
|
584
608
|
parameters: "Optional[StatementParameterType]" = None,
|
|
585
609
|
/,
|
|
586
|
-
|
|
610
|
+
*filters: StatementFilter,
|
|
587
611
|
connection: "Optional[BigQueryConnection]" = None,
|
|
588
612
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
589
613
|
**kwargs: Any,
|
|
@@ -591,41 +615,13 @@ class BigQueryDriver(
|
|
|
591
615
|
conn = self._connection(connection)
|
|
592
616
|
final_job_config = job_config or self._default_query_job_config or QueryJobConfig()
|
|
593
617
|
|
|
594
|
-
#
|
|
595
|
-
|
|
596
|
-
param_style: Optional[str] = None # 'named' (@), 'qmark' (?)
|
|
597
|
-
|
|
598
|
-
if isinstance(parameters, dict):
|
|
599
|
-
params = {**parameters, **kwargs}
|
|
600
|
-
param_style = "named"
|
|
601
|
-
elif isinstance(parameters, (list, tuple)):
|
|
602
|
-
if kwargs:
|
|
603
|
-
msg = "Cannot mix positional parameters with keyword arguments."
|
|
604
|
-
raise SQLSpecError(msg)
|
|
605
|
-
params = list(parameters)
|
|
606
|
-
param_style = "qmark"
|
|
607
|
-
elif kwargs:
|
|
608
|
-
params = kwargs
|
|
609
|
-
param_style = "named"
|
|
610
|
-
elif parameters is not None:
|
|
611
|
-
params = [parameters]
|
|
612
|
-
param_style = "qmark"
|
|
613
|
-
|
|
614
|
-
# Use sqlglot to transpile and bind parameters
|
|
615
|
-
try:
|
|
616
|
-
transpiled_sql = sqlglot.transpile(sql, args=params or {}, read=None, write=self.dialect)[0]
|
|
617
|
-
except Exception as e:
|
|
618
|
-
msg = f"SQL transpilation/binding failed using sqlglot: {e!s}"
|
|
619
|
-
raise SQLSpecError(msg) from e
|
|
618
|
+
# Process SQL and parameters using SQLStatement
|
|
619
|
+
processed_sql, processed_params = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
620
620
|
|
|
621
|
-
#
|
|
622
|
-
if
|
|
623
|
-
if not isinstance(params, dict):
|
|
624
|
-
# This should be logically impossible due to how param_style is set
|
|
625
|
-
msg = "Internal error: named parameter style detected but params is not a dict."
|
|
626
|
-
raise SQLSpecError(msg)
|
|
621
|
+
# Convert parameters to BigQuery format
|
|
622
|
+
if isinstance(processed_params, dict):
|
|
627
623
|
query_parameters = []
|
|
628
|
-
for key, value in
|
|
624
|
+
for key, value in processed_params.items():
|
|
629
625
|
param_type, array_element_type = self._get_bq_param_type(value)
|
|
630
626
|
|
|
631
627
|
if param_type == "ARRAY" and array_element_type:
|
|
@@ -636,15 +632,17 @@ class BigQueryDriver(
|
|
|
636
632
|
msg = f"Unsupported parameter type for BigQuery Arrow named parameter '{key}': {type(value)}"
|
|
637
633
|
raise SQLSpecError(msg)
|
|
638
634
|
final_job_config.query_parameters = query_parameters
|
|
639
|
-
elif
|
|
640
|
-
#
|
|
641
|
-
|
|
635
|
+
elif isinstance(processed_params, (list, tuple)):
|
|
636
|
+
# Convert sequence parameters
|
|
637
|
+
final_job_config.query_parameters = [
|
|
638
|
+
bigquery.ScalarQueryParameter(None, self._get_bq_param_type(value)[0], value)
|
|
639
|
+
for value in processed_params
|
|
640
|
+
]
|
|
642
641
|
|
|
643
642
|
# Execute the query and get Arrow table
|
|
644
643
|
try:
|
|
645
|
-
query_job = conn.query(
|
|
644
|
+
query_job = conn.query(processed_sql, job_config=final_job_config)
|
|
646
645
|
arrow_table = query_job.to_arrow() # Waits for job completion
|
|
647
|
-
|
|
648
646
|
except Exception as e:
|
|
649
647
|
msg = f"BigQuery Arrow query execution failed: {e!s}"
|
|
650
648
|
raise SQLSpecError(msg) from e
|
|
@@ -655,7 +653,7 @@ class BigQueryDriver(
|
|
|
655
653
|
sql: str, # Expects table ID: project.dataset.table
|
|
656
654
|
parameters: "Optional[StatementParameterType]" = None,
|
|
657
655
|
/,
|
|
658
|
-
|
|
656
|
+
*filters: StatementFilter,
|
|
659
657
|
destination_uri: "Optional[str]" = None,
|
|
660
658
|
connection: "Optional[BigQueryConnection]" = None,
|
|
661
659
|
job_config: "Optional[bigquery.ExtractJobConfig]" = None,
|
|
@@ -699,3 +697,14 @@ class BigQueryDriver(
|
|
|
699
697
|
if extract_job.errors:
|
|
700
698
|
msg = f"BigQuery Parquet export failed: {extract_job.errors}"
|
|
701
699
|
raise SQLSpecError(msg)
|
|
700
|
+
|
|
701
|
+
def _connection(self, connection: "Optional[BigQueryConnection]" = None) -> "BigQueryConnection":
|
|
702
|
+
"""Get the connection to use for the operation.
|
|
703
|
+
|
|
704
|
+
Args:
|
|
705
|
+
connection: Optional connection to use.
|
|
706
|
+
|
|
707
|
+
Returns:
|
|
708
|
+
The connection to use.
|
|
709
|
+
"""
|
|
710
|
+
return connection or self.connection
|