sqlspec 0.11.0__py3-none-any.whl → 0.11.1__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/adapters/adbc/driver.py +37 -60
- sqlspec/adapters/aiosqlite/driver.py +73 -104
- sqlspec/adapters/asyncmy/driver.py +28 -44
- sqlspec/adapters/asyncpg/driver.py +34 -44
- sqlspec/adapters/bigquery/driver.py +79 -168
- sqlspec/adapters/duckdb/driver.py +20 -49
- sqlspec/adapters/oracledb/driver.py +66 -86
- sqlspec/adapters/psqlpy/driver.py +39 -56
- sqlspec/adapters/psycopg/driver.py +71 -112
- sqlspec/adapters/sqlite/driver.py +15 -35
- sqlspec/base.py +0 -41
- sqlspec/filters.py +2 -1
- sqlspec/mixins.py +9 -10
- {sqlspec-0.11.0.dist-info → sqlspec-0.11.1.dist-info}/METADATA +4 -1
- {sqlspec-0.11.0.dist-info → sqlspec-0.11.1.dist-info}/RECORD +18 -18
- {sqlspec-0.11.0.dist-info → sqlspec-0.11.1.dist-info}/WHEEL +0 -0
- {sqlspec-0.11.0.dist-info → sqlspec-0.11.1.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.11.0.dist-info → sqlspec-0.11.1.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import datetime
|
|
3
3
|
import logging
|
|
4
|
-
from collections.abc import Iterator, Sequence
|
|
4
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
5
5
|
from decimal import Decimal
|
|
6
6
|
from typing import (
|
|
7
7
|
TYPE_CHECKING,
|
|
@@ -69,8 +69,6 @@ class BigQueryDriver(
|
|
|
69
69
|
if isinstance(value, float):
|
|
70
70
|
return "FLOAT64", None
|
|
71
71
|
if isinstance(value, Decimal):
|
|
72
|
-
# Precision/scale might matter, but BQ client handles conversion.
|
|
73
|
-
# Defaulting to BIGNUMERIC, NUMERIC might be desired in some cases though (User change)
|
|
74
72
|
return "BIGNUMERIC", None
|
|
75
73
|
if isinstance(value, str):
|
|
76
74
|
return "STRING", None
|
|
@@ -78,23 +76,17 @@ class BigQueryDriver(
|
|
|
78
76
|
return "BYTES", None
|
|
79
77
|
if isinstance(value, datetime.date):
|
|
80
78
|
return "DATE", None
|
|
81
|
-
# DATETIME is for timezone-naive values
|
|
82
79
|
if isinstance(value, datetime.datetime) and value.tzinfo is None:
|
|
83
80
|
return "DATETIME", None
|
|
84
|
-
# TIMESTAMP is for timezone-aware values
|
|
85
81
|
if isinstance(value, datetime.datetime) and value.tzinfo is not None:
|
|
86
82
|
return "TIMESTAMP", None
|
|
87
83
|
if isinstance(value, datetime.time):
|
|
88
84
|
return "TIME", None
|
|
89
85
|
|
|
90
|
-
# Handle Arrays - Determine element type
|
|
91
86
|
if isinstance(value, (list, tuple)):
|
|
92
87
|
if not value:
|
|
93
|
-
# Cannot determine type of empty array, BQ requires type.
|
|
94
|
-
# Raise or default? Defaulting is risky. Let's raise.
|
|
95
88
|
msg = "Cannot determine BigQuery ARRAY type for empty sequence."
|
|
96
89
|
raise SQLSpecError(msg)
|
|
97
|
-
# Infer type from first element
|
|
98
90
|
first_element = value[0]
|
|
99
91
|
element_type, _ = BigQueryDriver._get_bq_param_type(first_element)
|
|
100
92
|
if element_type is None:
|
|
@@ -102,55 +94,59 @@ class BigQueryDriver(
|
|
|
102
94
|
raise SQLSpecError(msg)
|
|
103
95
|
return "ARRAY", element_type
|
|
104
96
|
|
|
105
|
-
|
|
106
|
-
# if isinstance(value, dict):
|
|
107
|
-
# # This requires recursive type mapping for sub-fields.
|
|
108
|
-
# # For simplicity, users might need to construct StructQueryParameter manually.
|
|
109
|
-
# # return "STRUCT", None # Placeholder if implementing # noqa: ERA001
|
|
110
|
-
# raise SQLSpecError("Automatic STRUCT mapping not implemented. Please use bigquery.StructQueryParameter.") # noqa: ERA001
|
|
111
|
-
|
|
112
|
-
return None, None # Unsupported type
|
|
97
|
+
return None, None
|
|
113
98
|
|
|
114
99
|
def _process_sql_params(
|
|
115
100
|
self,
|
|
116
101
|
sql: str,
|
|
117
102
|
parameters: "Optional[StatementParameterType]" = None,
|
|
118
|
-
|
|
119
|
-
*filters: StatementFilter,
|
|
103
|
+
*filters: "StatementFilter",
|
|
120
104
|
**kwargs: Any,
|
|
121
105
|
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
|
|
122
106
|
"""Process SQL and parameters using SQLStatement with dialect support.
|
|
123
107
|
|
|
108
|
+
This method also handles the separation of StatementFilter instances that might be
|
|
109
|
+
passed in the 'parameters' argument.
|
|
110
|
+
|
|
124
111
|
Args:
|
|
125
112
|
sql: The SQL statement to process.
|
|
126
|
-
parameters: The parameters to bind to the statement.
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
parameters: The parameters to bind to the statement. This can be a
|
|
114
|
+
Mapping (dict), Sequence (list/tuple), a single StatementFilter, or None.
|
|
115
|
+
*filters: Additional statement filters to apply.
|
|
116
|
+
**kwargs: Additional keyword arguments (treated as named parameters for the SQL statement).
|
|
129
117
|
|
|
130
118
|
Raises:
|
|
131
119
|
ParameterStyleMismatchError: If pre-formatted BigQuery parameters are mixed with keyword arguments.
|
|
132
120
|
|
|
133
121
|
Returns:
|
|
134
|
-
A tuple of (
|
|
122
|
+
A tuple of (processed_sql, processed_parameters) ready for execution.
|
|
135
123
|
"""
|
|
136
|
-
|
|
124
|
+
passed_parameters: Optional[Union[Mapping[str, Any], Sequence[Any]]] = None
|
|
125
|
+
combined_filters_list: list[StatementFilter] = list(filters)
|
|
126
|
+
|
|
127
|
+
if parameters is not None:
|
|
128
|
+
if isinstance(parameters, StatementFilter):
|
|
129
|
+
combined_filters_list.insert(0, parameters)
|
|
130
|
+
else:
|
|
131
|
+
passed_parameters = parameters
|
|
132
|
+
|
|
137
133
|
if (
|
|
138
|
-
isinstance(
|
|
139
|
-
and
|
|
140
|
-
and all(
|
|
134
|
+
isinstance(passed_parameters, (list, tuple))
|
|
135
|
+
and passed_parameters
|
|
136
|
+
and all(
|
|
137
|
+
isinstance(p, (bigquery.ScalarQueryParameter, bigquery.ArrayQueryParameter)) for p in passed_parameters
|
|
138
|
+
)
|
|
141
139
|
):
|
|
142
140
|
if kwargs:
|
|
143
141
|
msg = "Cannot mix pre-formatted BigQuery parameters with keyword arguments."
|
|
144
142
|
raise ParameterStyleMismatchError(msg)
|
|
145
|
-
return sql,
|
|
143
|
+
return sql, passed_parameters
|
|
146
144
|
|
|
147
|
-
statement = SQLStatement(sql,
|
|
145
|
+
statement = SQLStatement(sql, passed_parameters, kwargs=kwargs, dialect=self.dialect)
|
|
148
146
|
|
|
149
|
-
|
|
150
|
-
for filter_obj in filters:
|
|
147
|
+
for filter_obj in combined_filters_list:
|
|
151
148
|
statement = statement.apply_filter(filter_obj)
|
|
152
149
|
|
|
153
|
-
# Process the statement for execution
|
|
154
150
|
processed_sql, processed_params, _ = statement.process()
|
|
155
151
|
|
|
156
152
|
return processed_sql, processed_params
|
|
@@ -159,8 +155,7 @@ class BigQueryDriver(
|
|
|
159
155
|
self,
|
|
160
156
|
sql: str,
|
|
161
157
|
parameters: "Optional[StatementParameterType]" = None,
|
|
162
|
-
|
|
163
|
-
*filters: StatementFilter,
|
|
158
|
+
*filters: "StatementFilter",
|
|
164
159
|
connection: "Optional[BigQueryConnection]" = None,
|
|
165
160
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
166
161
|
is_script: bool = False,
|
|
@@ -168,19 +163,15 @@ class BigQueryDriver(
|
|
|
168
163
|
) -> "QueryJob":
|
|
169
164
|
conn = self._connection(connection)
|
|
170
165
|
|
|
171
|
-
# Determine the final job config, creating a new one if necessary
|
|
172
|
-
# to avoid modifying a shared default config.
|
|
173
166
|
if job_config:
|
|
174
|
-
final_job_config = job_config
|
|
167
|
+
final_job_config = job_config
|
|
175
168
|
elif self._default_query_job_config:
|
|
176
|
-
final_job_config = QueryJobConfig()
|
|
169
|
+
final_job_config = QueryJobConfig.from_api_repr(self._default_query_job_config.to_api_repr()) # type: ignore[assignment]
|
|
177
170
|
else:
|
|
178
|
-
final_job_config = QueryJobConfig()
|
|
171
|
+
final_job_config = QueryJobConfig()
|
|
179
172
|
|
|
180
|
-
# Process SQL and parameters
|
|
181
173
|
final_sql, processed_params = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
182
174
|
|
|
183
|
-
# Handle pre-formatted parameters
|
|
184
175
|
if (
|
|
185
176
|
isinstance(processed_params, (list, tuple))
|
|
186
177
|
and processed_params
|
|
@@ -189,31 +180,24 @@ class BigQueryDriver(
|
|
|
189
180
|
)
|
|
190
181
|
):
|
|
191
182
|
final_job_config.query_parameters = list(processed_params)
|
|
192
|
-
# Convert regular parameters to BigQuery parameters
|
|
193
183
|
elif isinstance(processed_params, dict):
|
|
194
|
-
# Convert dict params to BQ ScalarQueryParameter
|
|
195
184
|
final_job_config.query_parameters = [
|
|
196
185
|
bigquery.ScalarQueryParameter(name, self._get_bq_param_type(value)[0], value)
|
|
197
186
|
for name, value in processed_params.items()
|
|
198
187
|
]
|
|
199
188
|
elif isinstance(processed_params, (list, tuple)):
|
|
200
|
-
# Convert list params to BQ ScalarQueryParameter
|
|
201
189
|
final_job_config.query_parameters = [
|
|
202
190
|
bigquery.ScalarQueryParameter(None, self._get_bq_param_type(value)[0], value)
|
|
203
191
|
for value in processed_params
|
|
204
192
|
]
|
|
205
193
|
|
|
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
|
|
208
194
|
final_query_kwargs = {}
|
|
209
|
-
if parameters is not None and kwargs:
|
|
195
|
+
if parameters is not None and kwargs:
|
|
210
196
|
final_query_kwargs = kwargs
|
|
211
|
-
# Else: If params came via kwargs, they are already handled, so don't pass them again
|
|
212
197
|
|
|
213
|
-
# Execute query
|
|
214
198
|
return conn.query(
|
|
215
199
|
final_sql,
|
|
216
|
-
job_config=final_job_config,
|
|
200
|
+
job_config=final_job_config, # pyright: ignore
|
|
217
201
|
**final_query_kwargs,
|
|
218
202
|
)
|
|
219
203
|
|
|
@@ -238,15 +222,12 @@ class BigQueryDriver(
|
|
|
238
222
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
239
223
|
) -> Sequence[Union[ModelDTOT, dict[str, Any]]]:
|
|
240
224
|
processed_results = []
|
|
241
|
-
# Create a quick lookup map for schema fields from the passed schema
|
|
242
225
|
schema_map = {field.name: field for field in schema}
|
|
243
226
|
|
|
244
227
|
for row in rows:
|
|
245
|
-
# row here is now a Row object from the iterator
|
|
246
228
|
row_dict = {}
|
|
247
|
-
for key, value in row.items():
|
|
229
|
+
for key, value in row.items():
|
|
248
230
|
field = schema_map.get(key)
|
|
249
|
-
# Workaround remains the same
|
|
250
231
|
if field and field.field_type == "TIMESTAMP" and isinstance(value, str) and "." in value:
|
|
251
232
|
try:
|
|
252
233
|
parsed_value = datetime.datetime.fromtimestamp(float(value), tz=datetime.timezone.utc)
|
|
@@ -263,8 +244,7 @@ class BigQueryDriver(
|
|
|
263
244
|
self,
|
|
264
245
|
sql: str,
|
|
265
246
|
parameters: "Optional[StatementParameterType]" = None,
|
|
266
|
-
|
|
267
|
-
*filters: StatementFilter,
|
|
247
|
+
*filters: "StatementFilter",
|
|
268
248
|
connection: "Optional[BigQueryConnection]" = None,
|
|
269
249
|
schema_type: None = None,
|
|
270
250
|
**kwargs: Any,
|
|
@@ -274,8 +254,7 @@ class BigQueryDriver(
|
|
|
274
254
|
self,
|
|
275
255
|
sql: str,
|
|
276
256
|
parameters: "Optional[StatementParameterType]" = None,
|
|
277
|
-
|
|
278
|
-
*filters: StatementFilter,
|
|
257
|
+
*filters: "StatementFilter",
|
|
279
258
|
connection: "Optional[BigQueryConnection]" = None,
|
|
280
259
|
schema_type: "type[ModelDTOT]",
|
|
281
260
|
**kwargs: Any,
|
|
@@ -284,27 +263,12 @@ class BigQueryDriver(
|
|
|
284
263
|
self,
|
|
285
264
|
sql: str,
|
|
286
265
|
parameters: "Optional[StatementParameterType]" = None,
|
|
287
|
-
|
|
288
|
-
*filters: StatementFilter,
|
|
266
|
+
*filters: "StatementFilter",
|
|
289
267
|
connection: "Optional[BigQueryConnection]" = None,
|
|
290
268
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
291
269
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
292
270
|
**kwargs: Any,
|
|
293
271
|
) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
|
|
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
272
|
query_job = self._run_query_job(
|
|
309
273
|
sql, parameters, *filters, connection=connection, job_config=job_config, **kwargs
|
|
310
274
|
)
|
|
@@ -315,8 +279,7 @@ class BigQueryDriver(
|
|
|
315
279
|
self,
|
|
316
280
|
sql: str,
|
|
317
281
|
parameters: "Optional[StatementParameterType]" = None,
|
|
318
|
-
|
|
319
|
-
*filters: StatementFilter,
|
|
282
|
+
*filters: "StatementFilter",
|
|
320
283
|
connection: "Optional[BigQueryConnection]" = None,
|
|
321
284
|
schema_type: None = None,
|
|
322
285
|
**kwargs: Any,
|
|
@@ -326,8 +289,7 @@ class BigQueryDriver(
|
|
|
326
289
|
self,
|
|
327
290
|
sql: str,
|
|
328
291
|
parameters: "Optional[StatementParameterType]" = None,
|
|
329
|
-
|
|
330
|
-
*filters: StatementFilter,
|
|
292
|
+
*filters: "StatementFilter",
|
|
331
293
|
connection: "Optional[BigQueryConnection]" = None,
|
|
332
294
|
schema_type: "type[ModelDTOT]",
|
|
333
295
|
**kwargs: Any,
|
|
@@ -336,8 +298,7 @@ class BigQueryDriver(
|
|
|
336
298
|
self,
|
|
337
299
|
sql: str,
|
|
338
300
|
parameters: "Optional[StatementParameterType]" = None,
|
|
339
|
-
|
|
340
|
-
*filters: StatementFilter,
|
|
301
|
+
*filters: "StatementFilter",
|
|
341
302
|
connection: "Optional[BigQueryConnection]" = None,
|
|
342
303
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
343
304
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
@@ -348,14 +309,8 @@ class BigQueryDriver(
|
|
|
348
309
|
)
|
|
349
310
|
rows_iterator = query_job.result()
|
|
350
311
|
try:
|
|
351
|
-
# Pass the iterator containing only the first row to _rows_to_results
|
|
352
|
-
# This ensures the timestamp workaround is applied consistently.
|
|
353
|
-
# We need to pass the original iterator for schema access, but only consume one row.
|
|
354
312
|
first_row = next(rows_iterator)
|
|
355
|
-
# Create a simple iterator yielding only the first row for processing
|
|
356
313
|
single_row_iter = iter([first_row])
|
|
357
|
-
# We need RowIterator type for schema, create mock/proxy if needed, or pass schema
|
|
358
|
-
# Let's try passing schema directly to _rows_to_results (requires modifying it)
|
|
359
314
|
results = self._rows_to_results(single_row_iter, rows_iterator.schema, schema_type)
|
|
360
315
|
return results[0]
|
|
361
316
|
except StopIteration:
|
|
@@ -367,8 +322,7 @@ class BigQueryDriver(
|
|
|
367
322
|
self,
|
|
368
323
|
sql: str,
|
|
369
324
|
parameters: "Optional[StatementParameterType]" = None,
|
|
370
|
-
|
|
371
|
-
*filters: StatementFilter,
|
|
325
|
+
*filters: "StatementFilter",
|
|
372
326
|
connection: "Optional[BigQueryConnection]" = None,
|
|
373
327
|
schema_type: None = None,
|
|
374
328
|
**kwargs: Any,
|
|
@@ -378,8 +332,7 @@ class BigQueryDriver(
|
|
|
378
332
|
self,
|
|
379
333
|
sql: str,
|
|
380
334
|
parameters: "Optional[StatementParameterType]" = None,
|
|
381
|
-
|
|
382
|
-
*filters: StatementFilter,
|
|
335
|
+
*filters: "StatementFilter",
|
|
383
336
|
connection: "Optional[BigQueryConnection]" = None,
|
|
384
337
|
schema_type: "type[ModelDTOT]",
|
|
385
338
|
**kwargs: Any,
|
|
@@ -388,8 +341,7 @@ class BigQueryDriver(
|
|
|
388
341
|
self,
|
|
389
342
|
sql: str,
|
|
390
343
|
parameters: "Optional[StatementParameterType]" = None,
|
|
391
|
-
|
|
392
|
-
*filters: StatementFilter,
|
|
344
|
+
*filters: "StatementFilter",
|
|
393
345
|
connection: "Optional[BigQueryConnection]" = None,
|
|
394
346
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
395
347
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
@@ -401,9 +353,7 @@ class BigQueryDriver(
|
|
|
401
353
|
rows_iterator = query_job.result()
|
|
402
354
|
try:
|
|
403
355
|
first_row = next(rows_iterator)
|
|
404
|
-
# Create a simple iterator yielding only the first row for processing
|
|
405
356
|
single_row_iter = iter([first_row])
|
|
406
|
-
# Pass schema directly
|
|
407
357
|
results = self._rows_to_results(single_row_iter, rows_iterator.schema, schema_type)
|
|
408
358
|
return results[0]
|
|
409
359
|
except StopIteration:
|
|
@@ -414,8 +364,7 @@ class BigQueryDriver(
|
|
|
414
364
|
self,
|
|
415
365
|
sql: str,
|
|
416
366
|
parameters: "Optional[StatementParameterType]" = None,
|
|
417
|
-
|
|
418
|
-
*filters: StatementFilter,
|
|
367
|
+
*filters: "StatementFilter",
|
|
419
368
|
connection: "Optional[BigQueryConnection]" = None,
|
|
420
369
|
schema_type: "Optional[type[T]]" = None,
|
|
421
370
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
@@ -426,8 +375,7 @@ class BigQueryDriver(
|
|
|
426
375
|
self,
|
|
427
376
|
sql: str,
|
|
428
377
|
parameters: "Optional[StatementParameterType]" = None,
|
|
429
|
-
|
|
430
|
-
*filters: StatementFilter,
|
|
378
|
+
*filters: "StatementFilter",
|
|
431
379
|
connection: "Optional[BigQueryConnection]" = None,
|
|
432
380
|
schema_type: "type[T]",
|
|
433
381
|
**kwargs: Any,
|
|
@@ -436,8 +384,7 @@ class BigQueryDriver(
|
|
|
436
384
|
self,
|
|
437
385
|
sql: str,
|
|
438
386
|
parameters: "Optional[StatementParameterType]" = None,
|
|
439
|
-
|
|
440
|
-
*filters: StatementFilter,
|
|
387
|
+
*filters: "StatementFilter",
|
|
441
388
|
connection: "Optional[BigQueryConnection]" = None,
|
|
442
389
|
schema_type: "Optional[type[T]]" = None,
|
|
443
390
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
@@ -450,8 +397,7 @@ class BigQueryDriver(
|
|
|
450
397
|
try:
|
|
451
398
|
first_row = next(iter(rows))
|
|
452
399
|
value = first_row[0]
|
|
453
|
-
|
|
454
|
-
field = rows.schema[0] # Get schema for the first column
|
|
400
|
+
field = rows.schema[0]
|
|
455
401
|
if field and field.field_type == "TIMESTAMP" and isinstance(value, str) and "." in value:
|
|
456
402
|
with contextlib.suppress(ValueError):
|
|
457
403
|
value = datetime.datetime.fromtimestamp(float(value), tz=datetime.timezone.utc)
|
|
@@ -466,8 +412,7 @@ class BigQueryDriver(
|
|
|
466
412
|
self,
|
|
467
413
|
sql: str,
|
|
468
414
|
parameters: "Optional[StatementParameterType]" = None,
|
|
469
|
-
|
|
470
|
-
*filters: StatementFilter,
|
|
415
|
+
*filters: "StatementFilter",
|
|
471
416
|
connection: "Optional[BigQueryConnection]" = None,
|
|
472
417
|
schema_type: None = None,
|
|
473
418
|
**kwargs: Any,
|
|
@@ -477,8 +422,7 @@ class BigQueryDriver(
|
|
|
477
422
|
self,
|
|
478
423
|
sql: str,
|
|
479
424
|
parameters: "Optional[StatementParameterType]" = None,
|
|
480
|
-
|
|
481
|
-
*filters: StatementFilter,
|
|
425
|
+
*filters: "StatementFilter",
|
|
482
426
|
connection: "Optional[BigQueryConnection]" = None,
|
|
483
427
|
schema_type: "type[T]",
|
|
484
428
|
**kwargs: Any,
|
|
@@ -487,8 +431,7 @@ class BigQueryDriver(
|
|
|
487
431
|
self,
|
|
488
432
|
sql: str,
|
|
489
433
|
parameters: "Optional[StatementParameterType]" = None,
|
|
490
|
-
|
|
491
|
-
*filters: StatementFilter,
|
|
434
|
+
*filters: "StatementFilter",
|
|
492
435
|
connection: "Optional[BigQueryConnection]" = None,
|
|
493
436
|
schema_type: "Optional[type[T]]" = None,
|
|
494
437
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
@@ -506,8 +449,7 @@ class BigQueryDriver(
|
|
|
506
449
|
try:
|
|
507
450
|
first_row = next(iter(rows))
|
|
508
451
|
value = first_row[0]
|
|
509
|
-
|
|
510
|
-
field = rows.schema[0] # Get schema for the first column
|
|
452
|
+
field = rows.schema[0]
|
|
511
453
|
if field and field.field_type == "TIMESTAMP" and isinstance(value, str) and "." in value:
|
|
512
454
|
with contextlib.suppress(ValueError):
|
|
513
455
|
value = datetime.datetime.fromtimestamp(float(value), tz=datetime.timezone.utc)
|
|
@@ -520,32 +462,23 @@ class BigQueryDriver(
|
|
|
520
462
|
self,
|
|
521
463
|
sql: str,
|
|
522
464
|
parameters: Optional[StatementParameterType] = None,
|
|
523
|
-
|
|
524
|
-
*filters: StatementFilter,
|
|
465
|
+
*filters: "StatementFilter",
|
|
525
466
|
connection: Optional["BigQueryConnection"] = None,
|
|
526
467
|
job_config: Optional[QueryJobConfig] = None,
|
|
527
468
|
**kwargs: Any,
|
|
528
469
|
) -> int:
|
|
529
|
-
"""Executes INSERT, UPDATE, DELETE and returns affected row count.
|
|
530
|
-
|
|
531
|
-
Returns:
|
|
532
|
-
int: The number of rows affected by the DML statement.
|
|
533
|
-
"""
|
|
534
470
|
query_job = self._run_query_job(
|
|
535
471
|
sql, parameters, *filters, connection=connection, job_config=job_config, **kwargs
|
|
536
472
|
)
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
query_job.result() # Ensure completion
|
|
540
|
-
return query_job.num_dml_affected_rows or 0 # Return 0 if None
|
|
473
|
+
query_job.result()
|
|
474
|
+
return query_job.num_dml_affected_rows or 0
|
|
541
475
|
|
|
542
476
|
@overload
|
|
543
477
|
def insert_update_delete_returning(
|
|
544
478
|
self,
|
|
545
479
|
sql: str,
|
|
546
480
|
parameters: "Optional[StatementParameterType]" = None,
|
|
547
|
-
|
|
548
|
-
*filters: StatementFilter,
|
|
481
|
+
*filters: "StatementFilter",
|
|
549
482
|
connection: "Optional[BigQueryConnection]" = None,
|
|
550
483
|
schema_type: None = None,
|
|
551
484
|
**kwargs: Any,
|
|
@@ -555,8 +488,7 @@ class BigQueryDriver(
|
|
|
555
488
|
self,
|
|
556
489
|
sql: str,
|
|
557
490
|
parameters: "Optional[StatementParameterType]" = None,
|
|
558
|
-
|
|
559
|
-
*filters: StatementFilter,
|
|
491
|
+
*filters: "StatementFilter",
|
|
560
492
|
connection: "Optional[BigQueryConnection]" = None,
|
|
561
493
|
schema_type: "type[ModelDTOT]",
|
|
562
494
|
**kwargs: Any,
|
|
@@ -565,31 +497,23 @@ class BigQueryDriver(
|
|
|
565
497
|
self,
|
|
566
498
|
sql: str,
|
|
567
499
|
parameters: "Optional[StatementParameterType]" = None,
|
|
568
|
-
|
|
569
|
-
*filters: StatementFilter,
|
|
500
|
+
*filters: "StatementFilter",
|
|
570
501
|
connection: "Optional[BigQueryConnection]" = None,
|
|
571
502
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
572
503
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
573
504
|
**kwargs: Any,
|
|
574
505
|
) -> Union[ModelDTOT, dict[str, Any]]:
|
|
575
|
-
"""BigQuery DML RETURNING equivalent is complex, often requires temp tables or scripting."""
|
|
576
506
|
msg = "BigQuery does not support `RETURNING` clauses directly in the same way as some other SQL databases. Consider multi-statement queries or alternative approaches."
|
|
577
507
|
raise NotImplementedError(msg)
|
|
578
508
|
|
|
579
509
|
def execute_script(
|
|
580
510
|
self,
|
|
581
|
-
sql: str,
|
|
582
|
-
parameters: "Optional[StatementParameterType]" = None,
|
|
583
|
-
/,
|
|
511
|
+
sql: str,
|
|
512
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
584
513
|
connection: "Optional[BigQueryConnection]" = None,
|
|
585
514
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
586
515
|
**kwargs: Any,
|
|
587
516
|
) -> str:
|
|
588
|
-
"""Executes a BigQuery script and returns the job ID.
|
|
589
|
-
|
|
590
|
-
Returns:
|
|
591
|
-
str: The job ID of the executed script.
|
|
592
|
-
"""
|
|
593
517
|
query_job = self._run_query_job(
|
|
594
518
|
sql,
|
|
595
519
|
parameters,
|
|
@@ -600,14 +524,11 @@ class BigQueryDriver(
|
|
|
600
524
|
)
|
|
601
525
|
return str(query_job.job_id)
|
|
602
526
|
|
|
603
|
-
# --- Mixin Implementations ---
|
|
604
|
-
|
|
605
527
|
def select_arrow( # pyright: ignore
|
|
606
528
|
self,
|
|
607
529
|
sql: str,
|
|
608
530
|
parameters: "Optional[StatementParameterType]" = None,
|
|
609
|
-
|
|
610
|
-
*filters: StatementFilter,
|
|
531
|
+
*filters: "StatementFilter",
|
|
611
532
|
connection: "Optional[BigQueryConnection]" = None,
|
|
612
533
|
job_config: "Optional[QueryJobConfig]" = None,
|
|
613
534
|
**kwargs: Any,
|
|
@@ -615,10 +536,8 @@ class BigQueryDriver(
|
|
|
615
536
|
conn = self._connection(connection)
|
|
616
537
|
final_job_config = job_config or self._default_query_job_config or QueryJobConfig()
|
|
617
538
|
|
|
618
|
-
# Process SQL and parameters using SQLStatement
|
|
619
539
|
processed_sql, processed_params = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
620
540
|
|
|
621
|
-
# Convert parameters to BigQuery format
|
|
622
541
|
if isinstance(processed_params, dict):
|
|
623
542
|
query_parameters = []
|
|
624
543
|
for key, value in processed_params.items():
|
|
@@ -633,16 +552,14 @@ class BigQueryDriver(
|
|
|
633
552
|
raise SQLSpecError(msg)
|
|
634
553
|
final_job_config.query_parameters = query_parameters
|
|
635
554
|
elif isinstance(processed_params, (list, tuple)):
|
|
636
|
-
# Convert sequence parameters
|
|
637
555
|
final_job_config.query_parameters = [
|
|
638
556
|
bigquery.ScalarQueryParameter(None, self._get_bq_param_type(value)[0], value)
|
|
639
557
|
for value in processed_params
|
|
640
558
|
]
|
|
641
559
|
|
|
642
|
-
# Execute the query and get Arrow table
|
|
643
560
|
try:
|
|
644
561
|
query_job = conn.query(processed_sql, job_config=final_job_config)
|
|
645
|
-
arrow_table = query_job.to_arrow()
|
|
562
|
+
arrow_table = query_job.to_arrow()
|
|
646
563
|
except Exception as e:
|
|
647
564
|
msg = f"BigQuery Arrow query execution failed: {e!s}"
|
|
648
565
|
raise SQLSpecError(msg) from e
|
|
@@ -650,31 +567,34 @@ class BigQueryDriver(
|
|
|
650
567
|
|
|
651
568
|
def select_to_parquet(
|
|
652
569
|
self,
|
|
653
|
-
sql: str,
|
|
570
|
+
sql: str,
|
|
654
571
|
parameters: "Optional[StatementParameterType]" = None,
|
|
655
|
-
|
|
656
|
-
*filters: StatementFilter,
|
|
572
|
+
*filters: "StatementFilter",
|
|
657
573
|
destination_uri: "Optional[str]" = None,
|
|
658
574
|
connection: "Optional[BigQueryConnection]" = None,
|
|
659
575
|
job_config: "Optional[bigquery.ExtractJobConfig]" = None,
|
|
660
576
|
**kwargs: Any,
|
|
661
577
|
) -> None:
|
|
662
|
-
"""Exports a BigQuery table to Parquet files in Google Cloud Storage.
|
|
663
|
-
|
|
664
|
-
Raises:
|
|
665
|
-
NotImplementedError: If the SQL is not a fully qualified table ID or if parameters are provided.
|
|
666
|
-
NotFoundError: If the source table is not found.
|
|
667
|
-
SQLSpecError: If the Parquet export fails.
|
|
668
|
-
"""
|
|
669
578
|
if destination_uri is None:
|
|
670
579
|
msg = "destination_uri is required"
|
|
671
580
|
raise SQLSpecError(msg)
|
|
672
581
|
conn = self._connection(connection)
|
|
673
|
-
|
|
674
|
-
|
|
582
|
+
|
|
583
|
+
if parameters is not None:
|
|
584
|
+
msg = (
|
|
585
|
+
"select_to_parquet expects a fully qualified table ID (e.g., 'project.dataset.table') "
|
|
586
|
+
"as the `sql` argument and does not support `parameters`."
|
|
587
|
+
)
|
|
675
588
|
raise NotImplementedError(msg)
|
|
676
589
|
|
|
677
|
-
|
|
590
|
+
try:
|
|
591
|
+
source_table_ref = bigquery.TableReference.from_string(sql, default_project=conn.project)
|
|
592
|
+
except ValueError as e:
|
|
593
|
+
msg = (
|
|
594
|
+
"select_to_parquet expects a fully qualified table ID (e.g., 'project.dataset.table') "
|
|
595
|
+
f"as the `sql` argument. Parsing failed for input '{sql}': {e!s}"
|
|
596
|
+
)
|
|
597
|
+
raise NotImplementedError(msg) from e
|
|
678
598
|
|
|
679
599
|
final_extract_config = job_config or bigquery.ExtractJobConfig() # type: ignore[no-untyped-call]
|
|
680
600
|
final_extract_config.destination_format = bigquery.DestinationFormat.PARQUET
|
|
@@ -684,9 +604,8 @@ class BigQueryDriver(
|
|
|
684
604
|
source_table_ref,
|
|
685
605
|
destination_uri,
|
|
686
606
|
job_config=final_extract_config,
|
|
687
|
-
# Location is correctly inferred by the client library
|
|
688
607
|
)
|
|
689
|
-
extract_job.result()
|
|
608
|
+
extract_job.result()
|
|
690
609
|
|
|
691
610
|
except NotFound:
|
|
692
611
|
msg = f"Source table not found for Parquet export: {source_table_ref}"
|
|
@@ -699,12 +618,4 @@ class BigQueryDriver(
|
|
|
699
618
|
raise SQLSpecError(msg)
|
|
700
619
|
|
|
701
620
|
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
621
|
return connection or self.connection
|