sqlspec 0.12.2__py3-none-any.whl → 0.13.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/_sql.py +21 -180
- sqlspec/adapters/adbc/config.py +10 -12
- sqlspec/adapters/adbc/driver.py +120 -118
- sqlspec/adapters/aiosqlite/config.py +16 -3
- sqlspec/adapters/aiosqlite/driver.py +100 -130
- sqlspec/adapters/asyncmy/config.py +17 -4
- sqlspec/adapters/asyncmy/driver.py +123 -135
- sqlspec/adapters/asyncpg/config.py +17 -29
- sqlspec/adapters/asyncpg/driver.py +98 -140
- sqlspec/adapters/bigquery/config.py +4 -5
- sqlspec/adapters/bigquery/driver.py +125 -167
- sqlspec/adapters/duckdb/config.py +3 -6
- sqlspec/adapters/duckdb/driver.py +114 -111
- sqlspec/adapters/oracledb/config.py +32 -5
- sqlspec/adapters/oracledb/driver.py +242 -259
- sqlspec/adapters/psqlpy/config.py +18 -9
- sqlspec/adapters/psqlpy/driver.py +118 -93
- sqlspec/adapters/psycopg/config.py +44 -31
- sqlspec/adapters/psycopg/driver.py +283 -236
- sqlspec/adapters/sqlite/config.py +3 -3
- sqlspec/adapters/sqlite/driver.py +103 -97
- sqlspec/config.py +0 -4
- sqlspec/driver/_async.py +89 -98
- sqlspec/driver/_common.py +52 -17
- sqlspec/driver/_sync.py +81 -105
- sqlspec/driver/connection.py +207 -0
- sqlspec/driver/mixins/_csv_writer.py +91 -0
- sqlspec/driver/mixins/_pipeline.py +38 -49
- sqlspec/driver/mixins/_result_utils.py +27 -9
- sqlspec/driver/mixins/_storage.py +67 -181
- sqlspec/driver/mixins/_type_coercion.py +3 -4
- sqlspec/driver/parameters.py +138 -0
- sqlspec/exceptions.py +10 -2
- sqlspec/extensions/aiosql/adapter.py +0 -10
- sqlspec/extensions/litestar/handlers.py +0 -1
- sqlspec/extensions/litestar/plugin.py +0 -3
- sqlspec/extensions/litestar/providers.py +0 -14
- sqlspec/loader.py +25 -90
- sqlspec/protocols.py +542 -0
- sqlspec/service/__init__.py +3 -2
- sqlspec/service/_util.py +147 -0
- sqlspec/service/base.py +1116 -9
- sqlspec/statement/builder/__init__.py +42 -32
- sqlspec/statement/builder/_ddl_utils.py +0 -10
- sqlspec/statement/builder/_parsing_utils.py +10 -4
- sqlspec/statement/builder/base.py +67 -22
- sqlspec/statement/builder/column.py +283 -0
- sqlspec/statement/builder/ddl.py +91 -67
- sqlspec/statement/builder/delete.py +23 -7
- sqlspec/statement/builder/insert.py +29 -15
- sqlspec/statement/builder/merge.py +4 -4
- sqlspec/statement/builder/mixins/_aggregate_functions.py +113 -14
- sqlspec/statement/builder/mixins/_common_table_expr.py +0 -1
- sqlspec/statement/builder/mixins/_delete_from.py +1 -1
- sqlspec/statement/builder/mixins/_from.py +10 -8
- sqlspec/statement/builder/mixins/_group_by.py +0 -1
- sqlspec/statement/builder/mixins/_insert_from_select.py +0 -1
- sqlspec/statement/builder/mixins/_insert_values.py +0 -2
- sqlspec/statement/builder/mixins/_join.py +20 -13
- sqlspec/statement/builder/mixins/_limit_offset.py +3 -3
- sqlspec/statement/builder/mixins/_merge_clauses.py +3 -4
- sqlspec/statement/builder/mixins/_order_by.py +2 -2
- sqlspec/statement/builder/mixins/_pivot.py +4 -7
- sqlspec/statement/builder/mixins/_select_columns.py +6 -5
- sqlspec/statement/builder/mixins/_unpivot.py +6 -9
- sqlspec/statement/builder/mixins/_update_from.py +2 -1
- sqlspec/statement/builder/mixins/_update_set.py +11 -8
- sqlspec/statement/builder/mixins/_where.py +61 -34
- sqlspec/statement/builder/select.py +32 -17
- sqlspec/statement/builder/update.py +25 -11
- sqlspec/statement/filters.py +39 -14
- sqlspec/statement/parameter_manager.py +220 -0
- sqlspec/statement/parameters.py +210 -79
- sqlspec/statement/pipelines/__init__.py +166 -23
- sqlspec/statement/pipelines/analyzers/_analyzer.py +21 -20
- sqlspec/statement/pipelines/context.py +35 -39
- sqlspec/statement/pipelines/transformers/__init__.py +2 -3
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +19 -187
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +628 -58
- sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +76 -0
- sqlspec/statement/pipelines/validators/_dml_safety.py +33 -18
- sqlspec/statement/pipelines/validators/_parameter_style.py +87 -14
- sqlspec/statement/pipelines/validators/_performance.py +38 -23
- sqlspec/statement/pipelines/validators/_security.py +39 -62
- sqlspec/statement/result.py +37 -129
- sqlspec/statement/splitter.py +0 -12
- sqlspec/statement/sql.py +863 -391
- sqlspec/statement/sql_compiler.py +140 -0
- sqlspec/storage/__init__.py +10 -2
- sqlspec/storage/backends/fsspec.py +53 -8
- sqlspec/storage/backends/obstore.py +15 -19
- sqlspec/storage/capabilities.py +101 -0
- sqlspec/storage/registry.py +56 -83
- sqlspec/typing.py +6 -434
- sqlspec/utils/cached_property.py +25 -0
- sqlspec/utils/correlation.py +0 -2
- sqlspec/utils/logging.py +0 -6
- sqlspec/utils/sync_tools.py +0 -4
- sqlspec/utils/text.py +0 -5
- sqlspec/utils/type_guards.py +892 -0
- {sqlspec-0.12.2.dist-info → sqlspec-0.13.1.dist-info}/METADATA +1 -1
- sqlspec-0.13.1.dist-info/RECORD +150 -0
- sqlspec/statement/builder/protocols.py +0 -20
- sqlspec/statement/pipelines/base.py +0 -315
- sqlspec/statement/pipelines/result_types.py +0 -41
- sqlspec/statement/pipelines/transformers/_remove_comments.py +0 -66
- sqlspec/statement/pipelines/transformers/_remove_hints.py +0 -81
- sqlspec/statement/pipelines/validators/base.py +0 -67
- sqlspec/storage/protocol.py +0 -173
- sqlspec-0.12.2.dist-info/RECORD +0 -145
- {sqlspec-0.12.2.dist-info → sqlspec-0.13.1.dist-info}/WHEEL +0 -0
- {sqlspec-0.12.2.dist-info → sqlspec-0.13.1.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.12.2.dist-info → sqlspec-0.13.1.dist-info}/licenses/NOTICE +0 -0
sqlspec/_sql.py
CHANGED
|
@@ -13,7 +13,7 @@ from sqlglot.dialects.dialect import DialectType
|
|
|
13
13
|
from sqlglot.errors import ParseError as SQLGlotParseError
|
|
14
14
|
|
|
15
15
|
from sqlspec.exceptions import SQLBuilderError
|
|
16
|
-
from sqlspec.statement.builder import
|
|
16
|
+
from sqlspec.statement.builder import Column, Delete, Insert, Merge, Select, Update
|
|
17
17
|
|
|
18
18
|
__all__ = ("SQLFactory",)
|
|
19
19
|
|
|
@@ -100,7 +100,6 @@ class SQLFactory:
|
|
|
100
100
|
if parsed_expr:
|
|
101
101
|
# Attempt to get the class name as a fallback, e.g., "Set", "Command"
|
|
102
102
|
command_type = type(parsed_expr).__name__.upper()
|
|
103
|
-
# Handle specific cases like "COMMAND" which might be too generic
|
|
104
103
|
if command_type == "COMMAND" and parsed_expr.this:
|
|
105
104
|
return str(parsed_expr.this).upper() # e.g. "SET", "ALTER"
|
|
106
105
|
return command_type
|
|
@@ -163,14 +162,12 @@ class SQLFactory:
|
|
|
163
162
|
"WITH": "WITH",
|
|
164
163
|
}
|
|
165
164
|
actual_type_str = expr_type_map.get(actual_type, actual_type)
|
|
166
|
-
# Only allow SELECT or WITH (if WITH wraps SELECT)
|
|
167
165
|
if actual_type_str == "SELECT" or (
|
|
168
166
|
actual_type_str == "WITH" and parsed_expr.this and isinstance(parsed_expr.this, exp.Select)
|
|
169
167
|
):
|
|
170
|
-
builder =
|
|
168
|
+
builder = Select(dialect=dialect or self.dialect)
|
|
171
169
|
builder._expression = parsed_expr
|
|
172
170
|
return builder
|
|
173
|
-
# If not SELECT, raise with helpful message
|
|
174
171
|
msg = (
|
|
175
172
|
f"sql(...) only supports SELECT statements. Detected type: {actual_type_str}. "
|
|
176
173
|
f"Use sql.{actual_type_str.lower()}() instead."
|
|
@@ -180,13 +177,11 @@ class SQLFactory:
|
|
|
180
177
|
# ===================
|
|
181
178
|
# Statement Builders
|
|
182
179
|
# ===================
|
|
183
|
-
def select(self, *columns_or_sql: Union[str, exp.Expression], dialect: DialectType = None) -> "
|
|
180
|
+
def select(self, *columns_or_sql: Union[str, exp.Expression, Column], dialect: DialectType = None) -> "Select":
|
|
184
181
|
builder_dialect = dialect or self.dialect
|
|
185
182
|
if len(columns_or_sql) == 1 and isinstance(columns_or_sql[0], str):
|
|
186
183
|
sql_candidate = columns_or_sql[0].strip()
|
|
187
|
-
# Check if it actually looks like SQL before parsing
|
|
188
184
|
if self._looks_like_sql(sql_candidate):
|
|
189
|
-
# Validate type
|
|
190
185
|
detected = self.detect_sql_type(sql_candidate, dialect=builder_dialect)
|
|
191
186
|
if detected not in {"SELECT", "WITH"}:
|
|
192
187
|
msg = (
|
|
@@ -194,21 +189,20 @@ class SQLFactory:
|
|
|
194
189
|
f"Use sql.{detected.lower()}() if a dedicated builder exists, or ensure the SQL is SELECT/WITH."
|
|
195
190
|
)
|
|
196
191
|
raise SQLBuilderError(msg)
|
|
197
|
-
select_builder =
|
|
192
|
+
select_builder = Select(dialect=builder_dialect)
|
|
198
193
|
if select_builder._expression is None:
|
|
199
194
|
select_builder.__post_init__()
|
|
200
195
|
return self._populate_select_from_sql(select_builder, sql_candidate)
|
|
201
|
-
|
|
202
|
-
select_builder = SelectBuilder(dialect=builder_dialect)
|
|
196
|
+
select_builder = Select(dialect=builder_dialect)
|
|
203
197
|
if select_builder._expression is None:
|
|
204
198
|
select_builder.__post_init__()
|
|
205
199
|
if columns_or_sql:
|
|
206
200
|
select_builder.select(*columns_or_sql)
|
|
207
201
|
return select_builder
|
|
208
202
|
|
|
209
|
-
def insert(self, table_or_sql: Optional[str] = None, dialect: DialectType = None) -> "
|
|
203
|
+
def insert(self, table_or_sql: Optional[str] = None, dialect: DialectType = None) -> "Insert":
|
|
210
204
|
builder_dialect = dialect or self.dialect
|
|
211
|
-
builder =
|
|
205
|
+
builder = Insert(dialect=builder_dialect)
|
|
212
206
|
if builder._expression is None:
|
|
213
207
|
builder.__post_init__()
|
|
214
208
|
if table_or_sql:
|
|
@@ -225,9 +219,9 @@ class SQLFactory:
|
|
|
225
219
|
return builder.into(table_or_sql)
|
|
226
220
|
return builder
|
|
227
221
|
|
|
228
|
-
def update(self, table_or_sql: Optional[str] = None, dialect: DialectType = None) -> "
|
|
222
|
+
def update(self, table_or_sql: Optional[str] = None, dialect: DialectType = None) -> "Update":
|
|
229
223
|
builder_dialect = dialect or self.dialect
|
|
230
|
-
builder =
|
|
224
|
+
builder = Update(dialect=builder_dialect)
|
|
231
225
|
if builder._expression is None:
|
|
232
226
|
builder.__post_init__()
|
|
233
227
|
if table_or_sql:
|
|
@@ -240,9 +234,9 @@ class SQLFactory:
|
|
|
240
234
|
return builder.table(table_or_sql)
|
|
241
235
|
return builder
|
|
242
236
|
|
|
243
|
-
def delete(self, table_or_sql: Optional[str] = None, dialect: DialectType = None) -> "
|
|
237
|
+
def delete(self, table_or_sql: Optional[str] = None, dialect: DialectType = None) -> "Delete":
|
|
244
238
|
builder_dialect = dialect or self.dialect
|
|
245
|
-
builder =
|
|
239
|
+
builder = Delete(dialect=builder_dialect)
|
|
246
240
|
if builder._expression is None:
|
|
247
241
|
builder.__post_init__()
|
|
248
242
|
if table_or_sql and self._looks_like_sql(table_or_sql):
|
|
@@ -253,9 +247,9 @@ class SQLFactory:
|
|
|
253
247
|
return self._populate_delete_from_sql(builder, table_or_sql)
|
|
254
248
|
return builder
|
|
255
249
|
|
|
256
|
-
def merge(self, table_or_sql: Optional[str] = None, dialect: DialectType = None) -> "
|
|
250
|
+
def merge(self, table_or_sql: Optional[str] = None, dialect: DialectType = None) -> "Merge":
|
|
257
251
|
builder_dialect = dialect or self.dialect
|
|
258
|
-
builder =
|
|
252
|
+
builder = Merge(dialect=builder_dialect)
|
|
259
253
|
if builder._expression is None:
|
|
260
254
|
builder.__post_init__()
|
|
261
255
|
if table_or_sql:
|
|
@@ -288,7 +282,6 @@ class SQLFactory:
|
|
|
288
282
|
|
|
289
283
|
candidate_upper = candidate.strip().upper()
|
|
290
284
|
|
|
291
|
-
# Check for SQL keywords at the beginning
|
|
292
285
|
if expected_type:
|
|
293
286
|
return candidate_upper.startswith(expected_type.upper())
|
|
294
287
|
|
|
@@ -302,7 +295,7 @@ class SQLFactory:
|
|
|
302
295
|
|
|
303
296
|
return False
|
|
304
297
|
|
|
305
|
-
def _populate_insert_from_sql(self, builder: "
|
|
298
|
+
def _populate_insert_from_sql(self, builder: "Insert", sql_string: str) -> "Insert":
|
|
306
299
|
"""Parse SQL string and populate INSERT builder using SQLGlot directly."""
|
|
307
300
|
try:
|
|
308
301
|
# Use SQLGlot directly for parsing - no validation here
|
|
@@ -311,12 +304,10 @@ class SQLFactory:
|
|
|
311
304
|
parsed_expr = sqlglot.parse_one(sql_string, read=self.dialect)
|
|
312
305
|
|
|
313
306
|
if isinstance(parsed_expr, exp.Insert):
|
|
314
|
-
# Set the internal expression to the parsed one
|
|
315
307
|
builder._expression = parsed_expr
|
|
316
308
|
return builder
|
|
317
309
|
|
|
318
310
|
if isinstance(parsed_expr, exp.Select):
|
|
319
|
-
# Handle INSERT FROM SELECT case - just return builder for now
|
|
320
311
|
# The actual conversion logic can be handled by the builder itself
|
|
321
312
|
logger.info("Detected SELECT statement for INSERT - may need target table specification")
|
|
322
313
|
return builder
|
|
@@ -328,7 +319,7 @@ class SQLFactory:
|
|
|
328
319
|
logger.warning("Failed to parse INSERT SQL, falling back to traditional mode: %s", e)
|
|
329
320
|
return builder
|
|
330
321
|
|
|
331
|
-
def _populate_select_from_sql(self, builder: "
|
|
322
|
+
def _populate_select_from_sql(self, builder: "Select", sql_string: str) -> "Select":
|
|
332
323
|
"""Parse SQL string and populate SELECT builder using SQLGlot directly."""
|
|
333
324
|
try:
|
|
334
325
|
# Use SQLGlot directly for parsing - no validation here
|
|
@@ -337,7 +328,6 @@ class SQLFactory:
|
|
|
337
328
|
parsed_expr = sqlglot.parse_one(sql_string, read=self.dialect)
|
|
338
329
|
|
|
339
330
|
if isinstance(parsed_expr, exp.Select):
|
|
340
|
-
# Set the internal expression to the parsed one
|
|
341
331
|
builder._expression = parsed_expr
|
|
342
332
|
return builder
|
|
343
333
|
|
|
@@ -347,7 +337,7 @@ class SQLFactory:
|
|
|
347
337
|
logger.warning("Failed to parse SELECT SQL, falling back to traditional mode: %s", e)
|
|
348
338
|
return builder
|
|
349
339
|
|
|
350
|
-
def _populate_update_from_sql(self, builder: "
|
|
340
|
+
def _populate_update_from_sql(self, builder: "Update", sql_string: str) -> "Update":
|
|
351
341
|
"""Parse SQL string and populate UPDATE builder using SQLGlot directly."""
|
|
352
342
|
try:
|
|
353
343
|
# Use SQLGlot directly for parsing - no validation here
|
|
@@ -356,7 +346,6 @@ class SQLFactory:
|
|
|
356
346
|
parsed_expr = sqlglot.parse_one(sql_string, read=self.dialect)
|
|
357
347
|
|
|
358
348
|
if isinstance(parsed_expr, exp.Update):
|
|
359
|
-
# Set the internal expression to the parsed one
|
|
360
349
|
builder._expression = parsed_expr
|
|
361
350
|
return builder
|
|
362
351
|
|
|
@@ -366,7 +355,7 @@ class SQLFactory:
|
|
|
366
355
|
logger.warning("Failed to parse UPDATE SQL, falling back to traditional mode: %s", e)
|
|
367
356
|
return builder
|
|
368
357
|
|
|
369
|
-
def _populate_delete_from_sql(self, builder: "
|
|
358
|
+
def _populate_delete_from_sql(self, builder: "Delete", sql_string: str) -> "Delete":
|
|
370
359
|
"""Parse SQL string and populate DELETE builder using SQLGlot directly."""
|
|
371
360
|
try:
|
|
372
361
|
# Use SQLGlot directly for parsing - no validation here
|
|
@@ -375,7 +364,6 @@ class SQLFactory:
|
|
|
375
364
|
parsed_expr = sqlglot.parse_one(sql_string, read=self.dialect)
|
|
376
365
|
|
|
377
366
|
if isinstance(parsed_expr, exp.Delete):
|
|
378
|
-
# Set the internal expression to the parsed one
|
|
379
367
|
builder._expression = parsed_expr
|
|
380
368
|
return builder
|
|
381
369
|
|
|
@@ -385,7 +373,7 @@ class SQLFactory:
|
|
|
385
373
|
logger.warning("Failed to parse DELETE SQL, falling back to traditional mode: %s", e)
|
|
386
374
|
return builder
|
|
387
375
|
|
|
388
|
-
def _populate_merge_from_sql(self, builder: "
|
|
376
|
+
def _populate_merge_from_sql(self, builder: "Merge", sql_string: str) -> "Merge":
|
|
389
377
|
"""Parse SQL string and populate MERGE builder using SQLGlot directly."""
|
|
390
378
|
try:
|
|
391
379
|
# Use SQLGlot directly for parsing - no validation here
|
|
@@ -394,7 +382,6 @@ class SQLFactory:
|
|
|
394
382
|
parsed_expr = sqlglot.parse_one(sql_string, read=self.dialect)
|
|
395
383
|
|
|
396
384
|
if isinstance(parsed_expr, exp.Merge):
|
|
397
|
-
# Set the internal expression to the parsed one
|
|
398
385
|
builder._expression = parsed_expr
|
|
399
386
|
return builder
|
|
400
387
|
|
|
@@ -408,16 +395,16 @@ class SQLFactory:
|
|
|
408
395
|
# Column References
|
|
409
396
|
# ===================
|
|
410
397
|
|
|
411
|
-
def __getattr__(self, name: str) ->
|
|
398
|
+
def __getattr__(self, name: str) -> Column:
|
|
412
399
|
"""Dynamically create column references.
|
|
413
400
|
|
|
414
401
|
Args:
|
|
415
402
|
name: Column name.
|
|
416
403
|
|
|
417
404
|
Returns:
|
|
418
|
-
Column
|
|
405
|
+
Column object that supports method chaining and operator overloading.
|
|
419
406
|
"""
|
|
420
|
-
return
|
|
407
|
+
return Column(name)
|
|
421
408
|
|
|
422
409
|
# ===================
|
|
423
410
|
# Aggregate Functions
|
|
@@ -579,7 +566,6 @@ class SQLFactory:
|
|
|
579
566
|
for column_set in column_sets:
|
|
580
567
|
if isinstance(column_set, (tuple, list)):
|
|
581
568
|
if len(column_set) == 0:
|
|
582
|
-
# Empty set for grand total
|
|
583
569
|
set_expressions.append(exp.Tuple(expressions=[]))
|
|
584
570
|
else:
|
|
585
571
|
columns = [exp.column(col) for col in column_set]
|
|
@@ -611,7 +597,6 @@ class SQLFactory:
|
|
|
611
597
|
```
|
|
612
598
|
"""
|
|
613
599
|
if isinstance(values, list):
|
|
614
|
-
# Convert list to array literal
|
|
615
600
|
literals = [exp.Literal.string(str(v)) if isinstance(v, str) else exp.Literal.number(v) for v in values]
|
|
616
601
|
return exp.Any(this=exp.Array(expressions=literals))
|
|
617
602
|
if isinstance(values, str):
|
|
@@ -734,11 +719,9 @@ class SQLFactory:
|
|
|
734
719
|
msg = "DECODE requires at least one search/result pair"
|
|
735
720
|
raise ValueError(msg)
|
|
736
721
|
|
|
737
|
-
# Build CASE expression
|
|
738
722
|
conditions = []
|
|
739
723
|
default = None
|
|
740
724
|
|
|
741
|
-
# Process search/result pairs
|
|
742
725
|
for i in range(0, len(args) - 1, 2):
|
|
743
726
|
if i + 1 >= len(args):
|
|
744
727
|
# Odd number of args means last one is default
|
|
@@ -748,7 +731,6 @@ class SQLFactory:
|
|
|
748
731
|
search_val = args[i]
|
|
749
732
|
result_val = args[i + 1]
|
|
750
733
|
|
|
751
|
-
# Create search expression
|
|
752
734
|
if isinstance(search_val, str):
|
|
753
735
|
search_expr = exp.Literal.string(search_val)
|
|
754
736
|
elif isinstance(search_val, (int, float)):
|
|
@@ -758,7 +740,6 @@ class SQLFactory:
|
|
|
758
740
|
else:
|
|
759
741
|
search_expr = exp.Literal.string(str(search_val))
|
|
760
742
|
|
|
761
|
-
# Create result expression
|
|
762
743
|
if isinstance(result_val, str):
|
|
763
744
|
result_expr = exp.Literal.string(result_val)
|
|
764
745
|
elif isinstance(result_val, (int, float)):
|
|
@@ -768,80 +749,11 @@ class SQLFactory:
|
|
|
768
749
|
else:
|
|
769
750
|
result_expr = exp.Literal.string(str(result_val))
|
|
770
751
|
|
|
771
|
-
# Create WHEN condition
|
|
772
752
|
condition = exp.EQ(this=col_expr, expression=search_expr)
|
|
773
753
|
conditions.append(exp.When(this=condition, then=result_expr))
|
|
774
754
|
|
|
775
755
|
return exp.Case(ifs=conditions, default=default)
|
|
776
756
|
|
|
777
|
-
@staticmethod
|
|
778
|
-
def to_date(date_string: Union[str, exp.Expression], format_mask: Optional[str] = None) -> exp.Expression:
|
|
779
|
-
"""Create a TO_DATE expression for converting strings to dates.
|
|
780
|
-
|
|
781
|
-
Args:
|
|
782
|
-
date_string: String or expression containing the date to convert.
|
|
783
|
-
format_mask: Optional format mask (e.g., 'YYYY-MM-DD', 'DD/MM/YYYY').
|
|
784
|
-
|
|
785
|
-
Returns:
|
|
786
|
-
TO_DATE function expression.
|
|
787
|
-
"""
|
|
788
|
-
date_expr = exp.column(date_string) if isinstance(date_string, str) else date_string
|
|
789
|
-
|
|
790
|
-
if format_mask:
|
|
791
|
-
format_expr = exp.Literal.string(format_mask)
|
|
792
|
-
return exp.Anonymous(this="TO_DATE", expressions=[date_expr, format_expr])
|
|
793
|
-
return exp.Anonymous(this="TO_DATE", expressions=[date_expr])
|
|
794
|
-
|
|
795
|
-
@staticmethod
|
|
796
|
-
def to_char(column: Union[str, exp.Expression], format_mask: Optional[str] = None) -> exp.Expression:
|
|
797
|
-
"""Create a TO_CHAR expression for converting values to strings.
|
|
798
|
-
|
|
799
|
-
Args:
|
|
800
|
-
column: Column or expression to convert to string.
|
|
801
|
-
format_mask: Optional format mask for dates/numbers.
|
|
802
|
-
|
|
803
|
-
Returns:
|
|
804
|
-
TO_CHAR function expression.
|
|
805
|
-
"""
|
|
806
|
-
col_expr = exp.column(column) if isinstance(column, str) else column
|
|
807
|
-
|
|
808
|
-
if format_mask:
|
|
809
|
-
format_expr = exp.Literal.string(format_mask)
|
|
810
|
-
return exp.Anonymous(this="TO_CHAR", expressions=[col_expr, format_expr])
|
|
811
|
-
return exp.Anonymous(this="TO_CHAR", expressions=[col_expr])
|
|
812
|
-
|
|
813
|
-
@staticmethod
|
|
814
|
-
def to_string(column: Union[str, exp.Expression]) -> exp.Expression:
|
|
815
|
-
"""Create a TO_STRING expression for converting values to strings.
|
|
816
|
-
|
|
817
|
-
Args:
|
|
818
|
-
column: Column or expression to convert to string.
|
|
819
|
-
|
|
820
|
-
Returns:
|
|
821
|
-
TO_STRING or CAST AS STRING expression.
|
|
822
|
-
"""
|
|
823
|
-
col_expr = exp.column(column) if isinstance(column, str) else column
|
|
824
|
-
# Use CAST for broader compatibility
|
|
825
|
-
return exp.Cast(this=col_expr, to=exp.DataType.build("STRING"))
|
|
826
|
-
|
|
827
|
-
@staticmethod
|
|
828
|
-
def to_number(column: Union[str, exp.Expression], format_mask: Optional[str] = None) -> exp.Expression:
|
|
829
|
-
"""Create a TO_NUMBER expression for converting strings to numbers.
|
|
830
|
-
|
|
831
|
-
Args:
|
|
832
|
-
column: Column or expression to convert to number.
|
|
833
|
-
format_mask: Optional format mask for the conversion.
|
|
834
|
-
|
|
835
|
-
Returns:
|
|
836
|
-
TO_NUMBER function expression.
|
|
837
|
-
"""
|
|
838
|
-
col_expr = exp.column(column) if isinstance(column, str) else column
|
|
839
|
-
|
|
840
|
-
if format_mask:
|
|
841
|
-
format_expr = exp.Literal.string(format_mask)
|
|
842
|
-
return exp.Anonymous(this="TO_NUMBER", expressions=[col_expr, format_expr])
|
|
843
|
-
return exp.Anonymous(this="TO_NUMBER", expressions=[col_expr])
|
|
844
|
-
|
|
845
757
|
@staticmethod
|
|
846
758
|
def cast(column: Union[str, exp.Expression], data_type: str) -> exp.Expression:
|
|
847
759
|
"""Create a CAST expression for type conversion.
|
|
@@ -856,75 +768,6 @@ class SQLFactory:
|
|
|
856
768
|
col_expr = exp.column(column) if isinstance(column, str) else column
|
|
857
769
|
return exp.Cast(this=col_expr, to=exp.DataType.build(data_type))
|
|
858
770
|
|
|
859
|
-
# ===================
|
|
860
|
-
# JSON Functions
|
|
861
|
-
# ===================
|
|
862
|
-
|
|
863
|
-
@staticmethod
|
|
864
|
-
def to_json(column: Union[str, exp.Expression]) -> exp.Expression:
|
|
865
|
-
"""Create a TO_JSON expression for converting values to JSON.
|
|
866
|
-
|
|
867
|
-
Args:
|
|
868
|
-
column: Column or expression to convert to JSON.
|
|
869
|
-
|
|
870
|
-
Returns:
|
|
871
|
-
TO_JSON function expression.
|
|
872
|
-
"""
|
|
873
|
-
col_expr = exp.column(column) if isinstance(column, str) else column
|
|
874
|
-
return exp.Anonymous(this="TO_JSON", expressions=[col_expr])
|
|
875
|
-
|
|
876
|
-
@staticmethod
|
|
877
|
-
def from_json(json_column: Union[str, exp.Expression], schema: Optional[str] = None) -> exp.Expression:
|
|
878
|
-
"""Create a FROM_JSON expression for parsing JSON strings.
|
|
879
|
-
|
|
880
|
-
Args:
|
|
881
|
-
json_column: Column or expression containing JSON string.
|
|
882
|
-
schema: Optional schema specification for the JSON structure.
|
|
883
|
-
|
|
884
|
-
Returns:
|
|
885
|
-
FROM_JSON function expression.
|
|
886
|
-
"""
|
|
887
|
-
json_expr = exp.column(json_column) if isinstance(json_column, str) else json_column
|
|
888
|
-
|
|
889
|
-
if schema:
|
|
890
|
-
schema_expr = exp.Literal.string(schema)
|
|
891
|
-
return exp.Anonymous(this="FROM_JSON", expressions=[json_expr, schema_expr])
|
|
892
|
-
return exp.Anonymous(this="FROM_JSON", expressions=[json_expr])
|
|
893
|
-
|
|
894
|
-
@staticmethod
|
|
895
|
-
def json_extract(json_column: Union[str, exp.Expression], path: str) -> exp.Expression:
|
|
896
|
-
"""Create a JSON_EXTRACT expression for extracting values from JSON.
|
|
897
|
-
|
|
898
|
-
Args:
|
|
899
|
-
json_column: Column or expression containing JSON.
|
|
900
|
-
path: JSON path to extract (e.g., '$.field', '$.array[0]').
|
|
901
|
-
|
|
902
|
-
Returns:
|
|
903
|
-
JSON_EXTRACT function expression.
|
|
904
|
-
"""
|
|
905
|
-
json_expr = exp.column(json_column) if isinstance(json_column, str) else json_column
|
|
906
|
-
path_expr = exp.Literal.string(path)
|
|
907
|
-
return exp.Anonymous(this="JSON_EXTRACT", expressions=[json_expr, path_expr])
|
|
908
|
-
|
|
909
|
-
@staticmethod
|
|
910
|
-
def json_value(json_column: Union[str, exp.Expression], path: str) -> exp.Expression:
|
|
911
|
-
"""Create a JSON_VALUE expression for extracting scalar values from JSON.
|
|
912
|
-
|
|
913
|
-
Args:
|
|
914
|
-
json_column: Column or expression containing JSON.
|
|
915
|
-
path: JSON path to extract scalar value.
|
|
916
|
-
|
|
917
|
-
Returns:
|
|
918
|
-
JSON_VALUE function expression.
|
|
919
|
-
"""
|
|
920
|
-
json_expr = exp.column(json_column) if isinstance(json_column, str) else json_column
|
|
921
|
-
path_expr = exp.Literal.string(path)
|
|
922
|
-
return exp.Anonymous(this="JSON_VALUE", expressions=[json_expr, path_expr])
|
|
923
|
-
|
|
924
|
-
# ===================
|
|
925
|
-
# NULL Functions
|
|
926
|
-
# ===================
|
|
927
|
-
|
|
928
771
|
@staticmethod
|
|
929
772
|
def coalesce(*expressions: Union[str, exp.Expression]) -> exp.Expression:
|
|
930
773
|
"""Create a COALESCE expression.
|
|
@@ -1045,10 +888,8 @@ class SQLFactory:
|
|
|
1045
888
|
Returns:
|
|
1046
889
|
Window function expression.
|
|
1047
890
|
"""
|
|
1048
|
-
# Create the function call
|
|
1049
891
|
func_expr = exp.Anonymous(this=func_name, expressions=func_args)
|
|
1050
892
|
|
|
1051
|
-
# Build OVER clause
|
|
1052
893
|
over_args: dict[str, Any] = {}
|
|
1053
894
|
|
|
1054
895
|
if partition_by:
|
sqlspec/adapters/adbc/config.py
CHANGED
|
@@ -270,7 +270,6 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
270
270
|
|
|
271
271
|
# If explicit driver path is provided, normalize it
|
|
272
272
|
if isinstance(driver_name, str):
|
|
273
|
-
# Handle convenience aliases
|
|
274
273
|
driver_aliases = {
|
|
275
274
|
"sqlite": "adbc_driver_sqlite.dbapi.connect",
|
|
276
275
|
"sqlite3": "adbc_driver_sqlite.dbapi.connect",
|
|
@@ -294,7 +293,6 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
294
293
|
|
|
295
294
|
resolved_driver = driver_aliases.get(driver_name, driver_name)
|
|
296
295
|
|
|
297
|
-
# Ensure it ends with .dbapi.connect
|
|
298
296
|
if not resolved_driver.endswith(".dbapi.connect"):
|
|
299
297
|
resolved_driver = f"{resolved_driver}.dbapi.connect"
|
|
300
298
|
|
|
@@ -458,17 +456,19 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
458
456
|
@contextmanager
|
|
459
457
|
def session_manager() -> "Generator[AdbcDriver, None, None]":
|
|
460
458
|
with self.provide_connection(*args, **kwargs) as connection:
|
|
461
|
-
# Get parameter styles based on the actual driver
|
|
462
459
|
supported_styles, preferred_style = self._get_parameter_styles()
|
|
463
460
|
|
|
464
|
-
# Create statement config with parameter style info if not already set
|
|
465
461
|
statement_config = self.statement_config
|
|
466
|
-
if statement_config
|
|
467
|
-
statement_config
|
|
468
|
-
statement_config,
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
462
|
+
if statement_config is not None:
|
|
463
|
+
if statement_config.dialect is None:
|
|
464
|
+
statement_config = replace(statement_config, dialect=self._get_dialect())
|
|
465
|
+
|
|
466
|
+
if statement_config.allowed_parameter_styles is None:
|
|
467
|
+
statement_config = replace(
|
|
468
|
+
statement_config,
|
|
469
|
+
allowed_parameter_styles=supported_styles,
|
|
470
|
+
target_parameter_style=preferred_style,
|
|
471
|
+
)
|
|
472
472
|
|
|
473
473
|
driver = self.driver_type(connection=connection, config=statement_config)
|
|
474
474
|
yield driver
|
|
@@ -492,7 +492,6 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
492
492
|
# Merge extras parameters
|
|
493
493
|
config.update(self.extras)
|
|
494
494
|
|
|
495
|
-
# Process URI based on driver type
|
|
496
495
|
if "driver_name" in config:
|
|
497
496
|
driver_name = config["driver_name"]
|
|
498
497
|
|
|
@@ -526,7 +525,6 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
526
525
|
if isinstance(db_kwargs, dict):
|
|
527
526
|
config.update(db_kwargs)
|
|
528
527
|
|
|
529
|
-
# Remove driver_name from config as it's not a connection parameter
|
|
530
528
|
config.pop("driver_name", None)
|
|
531
529
|
|
|
532
530
|
return config
|