sqlspec 0.13.1__py3-none-any.whl → 0.16.2__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 +71 -8
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +1 -3
- sqlspec/_serialization.py +1 -2
- sqlspec/_sql.py +930 -136
- sqlspec/_typing.py +278 -142
- sqlspec/adapters/adbc/__init__.py +4 -3
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +116 -285
- sqlspec/adapters/adbc/driver.py +462 -340
- sqlspec/adapters/aiosqlite/__init__.py +18 -3
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +202 -150
- sqlspec/adapters/aiosqlite/driver.py +226 -247
- sqlspec/adapters/asyncmy/__init__.py +18 -3
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +80 -199
- sqlspec/adapters/asyncmy/driver.py +257 -215
- sqlspec/adapters/asyncpg/__init__.py +19 -4
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +81 -214
- sqlspec/adapters/asyncpg/driver.py +284 -359
- sqlspec/adapters/bigquery/__init__.py +17 -3
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +191 -299
- sqlspec/adapters/bigquery/driver.py +474 -634
- sqlspec/adapters/duckdb/__init__.py +14 -3
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +414 -397
- sqlspec/adapters/duckdb/driver.py +342 -393
- sqlspec/adapters/oracledb/__init__.py +19 -5
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +123 -458
- sqlspec/adapters/oracledb/driver.py +505 -531
- sqlspec/adapters/psqlpy/__init__.py +13 -3
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +93 -307
- sqlspec/adapters/psqlpy/driver.py +504 -213
- sqlspec/adapters/psycopg/__init__.py +19 -5
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +143 -472
- sqlspec/adapters/psycopg/driver.py +704 -825
- sqlspec/adapters/sqlite/__init__.py +14 -3
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +208 -142
- sqlspec/adapters/sqlite/driver.py +263 -278
- sqlspec/base.py +105 -9
- sqlspec/{statement/builder → builder}/__init__.py +12 -14
- sqlspec/{statement/builder/base.py → builder/_base.py} +184 -86
- sqlspec/{statement/builder/column.py → builder/_column.py} +97 -60
- sqlspec/{statement/builder/ddl.py → builder/_ddl.py} +61 -131
- sqlspec/{statement/builder → builder}/_ddl_utils.py +4 -10
- sqlspec/{statement/builder/delete.py → builder/_delete.py} +10 -30
- sqlspec/builder/_insert.py +421 -0
- sqlspec/builder/_merge.py +71 -0
- sqlspec/{statement/builder → builder}/_parsing_utils.py +49 -26
- sqlspec/builder/_select.py +170 -0
- sqlspec/{statement/builder/update.py → builder/_update.py} +16 -20
- sqlspec/builder/mixins/__init__.py +55 -0
- sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
- sqlspec/{statement/builder/mixins/_delete_from.py → builder/mixins/_delete_operations.py} +8 -1
- sqlspec/builder/mixins/_insert_operations.py +244 -0
- sqlspec/{statement/builder/mixins/_join.py → builder/mixins/_join_operations.py} +45 -13
- sqlspec/{statement/builder/mixins/_merge_clauses.py → builder/mixins/_merge_operations.py} +188 -30
- sqlspec/builder/mixins/_order_limit_operations.py +135 -0
- sqlspec/builder/mixins/_pivot_operations.py +153 -0
- sqlspec/builder/mixins/_select_operations.py +604 -0
- sqlspec/builder/mixins/_update_operations.py +202 -0
- sqlspec/builder/mixins/_where_clause.py +644 -0
- sqlspec/cli.py +247 -0
- sqlspec/config.py +183 -138
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.py +871 -0
- sqlspec/core/compiler.py +417 -0
- sqlspec/core/filters.py +830 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.py +1237 -0
- sqlspec/core/result.py +677 -0
- sqlspec/{statement → core}/splitter.py +321 -191
- sqlspec/core/statement.py +676 -0
- sqlspec/driver/__init__.py +7 -10
- sqlspec/driver/_async.py +422 -163
- sqlspec/driver/_common.py +545 -287
- sqlspec/driver/_sync.py +426 -160
- sqlspec/driver/mixins/__init__.py +2 -13
- sqlspec/driver/mixins/_result_tools.py +193 -0
- sqlspec/driver/mixins/_sql_translator.py +65 -14
- sqlspec/exceptions.py +5 -252
- sqlspec/extensions/aiosql/adapter.py +93 -96
- sqlspec/extensions/litestar/__init__.py +2 -1
- sqlspec/extensions/litestar/cli.py +48 -0
- sqlspec/extensions/litestar/config.py +0 -1
- sqlspec/extensions/litestar/handlers.py +15 -26
- sqlspec/extensions/litestar/plugin.py +21 -16
- sqlspec/extensions/litestar/providers.py +17 -52
- sqlspec/loader.py +423 -104
- sqlspec/migrations/__init__.py +35 -0
- sqlspec/migrations/base.py +414 -0
- sqlspec/migrations/commands.py +443 -0
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +213 -0
- sqlspec/migrations/tracker.py +140 -0
- sqlspec/migrations/utils.py +129 -0
- sqlspec/protocols.py +51 -186
- 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 -2
- 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 +482 -235
- {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/METADATA +7 -2
- sqlspec-0.16.2.dist-info/RECORD +134 -0
- sqlspec-0.16.2.dist-info/entry_points.txt +2 -0
- sqlspec/driver/connection.py +0 -207
- sqlspec/driver/mixins/_csv_writer.py +0 -91
- sqlspec/driver/mixins/_pipeline.py +0 -512
- sqlspec/driver/mixins/_result_utils.py +0 -140
- sqlspec/driver/mixins/_storage.py +0 -926
- sqlspec/driver/mixins/_type_coercion.py +0 -130
- sqlspec/driver/parameters.py +0 -138
- sqlspec/service/__init__.py +0 -4
- sqlspec/service/_util.py +0 -147
- sqlspec/service/base.py +0 -1131
- sqlspec/service/pagination.py +0 -26
- sqlspec/statement/__init__.py +0 -21
- sqlspec/statement/builder/insert.py +0 -288
- sqlspec/statement/builder/merge.py +0 -95
- sqlspec/statement/builder/mixins/__init__.py +0 -65
- sqlspec/statement/builder/mixins/_aggregate_functions.py +0 -250
- sqlspec/statement/builder/mixins/_case_builder.py +0 -91
- sqlspec/statement/builder/mixins/_common_table_expr.py +0 -90
- sqlspec/statement/builder/mixins/_from.py +0 -63
- sqlspec/statement/builder/mixins/_group_by.py +0 -118
- sqlspec/statement/builder/mixins/_having.py +0 -35
- sqlspec/statement/builder/mixins/_insert_from_select.py +0 -47
- sqlspec/statement/builder/mixins/_insert_into.py +0 -36
- sqlspec/statement/builder/mixins/_insert_values.py +0 -67
- sqlspec/statement/builder/mixins/_limit_offset.py +0 -53
- sqlspec/statement/builder/mixins/_order_by.py +0 -46
- sqlspec/statement/builder/mixins/_pivot.py +0 -79
- sqlspec/statement/builder/mixins/_returning.py +0 -37
- sqlspec/statement/builder/mixins/_select_columns.py +0 -61
- sqlspec/statement/builder/mixins/_set_ops.py +0 -122
- sqlspec/statement/builder/mixins/_unpivot.py +0 -77
- sqlspec/statement/builder/mixins/_update_from.py +0 -55
- sqlspec/statement/builder/mixins/_update_set.py +0 -94
- sqlspec/statement/builder/mixins/_update_table.py +0 -29
- sqlspec/statement/builder/mixins/_where.py +0 -401
- sqlspec/statement/builder/mixins/_window_functions.py +0 -86
- sqlspec/statement/builder/select.py +0 -221
- sqlspec/statement/filters.py +0 -596
- sqlspec/statement/parameter_manager.py +0 -220
- sqlspec/statement/parameters.py +0 -867
- 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 -115
- 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 -718
- sqlspec/statement/pipelines/validators/_security.py +0 -967
- sqlspec/statement/result.py +0 -435
- sqlspec/statement/sql.py +0 -1704
- sqlspec/statement/sql_compiler.py +0 -140
- sqlspec/utils/cached_property.py +0 -25
- sqlspec-0.13.1.dist-info/RECORD +0 -150
- {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/WHEEL +0 -0
- {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
"""DDL builders for SQLSpec: DROP, CREATE INDEX, TRUNCATE, etc."""
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
@@ -7,13 +7,13 @@ from sqlglot import exp
|
|
|
7
7
|
from sqlglot.dialects.dialect import DialectType
|
|
8
8
|
from typing_extensions import Self
|
|
9
9
|
|
|
10
|
-
from sqlspec.
|
|
11
|
-
from sqlspec.
|
|
12
|
-
from sqlspec.
|
|
10
|
+
from sqlspec.builder._base import QueryBuilder, SafeQuery
|
|
11
|
+
from sqlspec.builder._ddl_utils import build_column_expression, build_constraint_expression
|
|
12
|
+
from sqlspec.core.result import SQLResult
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
|
-
from sqlspec.
|
|
16
|
-
from sqlspec.statement
|
|
15
|
+
from sqlspec.builder._column import ColumnExpression
|
|
16
|
+
from sqlspec.core.statement import SQL, StatementConfig
|
|
17
17
|
|
|
18
18
|
__all__ = (
|
|
19
19
|
"AlterOperation",
|
|
@@ -33,28 +33,27 @@ __all__ = (
|
|
|
33
33
|
"DropTable",
|
|
34
34
|
"DropView",
|
|
35
35
|
"RenameTable",
|
|
36
|
-
"
|
|
36
|
+
"Truncate",
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
@dataclass
|
|
41
|
-
class DDLBuilder(QueryBuilder
|
|
41
|
+
class DDLBuilder(QueryBuilder):
|
|
42
42
|
"""Base class for DDL builders (CREATE, DROP, ALTER, etc)."""
|
|
43
43
|
|
|
44
44
|
dialect: DialectType = None
|
|
45
45
|
_expression: Optional[exp.Expression] = field(default=None, init=False, repr=False, compare=False, hash=False)
|
|
46
46
|
|
|
47
47
|
def __post_init__(self) -> None:
|
|
48
|
-
#
|
|
49
|
-
|
|
48
|
+
# Initialize parent class attributes since dataclass doesn't call super().__init__()
|
|
49
|
+
super().__init__(dialect=self.dialect)
|
|
50
50
|
|
|
51
51
|
def _create_base_expression(self) -> exp.Expression:
|
|
52
52
|
msg = "Subclasses must implement _create_base_expression."
|
|
53
53
|
raise NotImplementedError(msg)
|
|
54
54
|
|
|
55
55
|
@property
|
|
56
|
-
def _expected_result_type(self) -> "type[SQLResult
|
|
57
|
-
# DDL typically returns no rows; use object for now.
|
|
56
|
+
def _expected_result_type(self) -> "type[SQLResult]":
|
|
58
57
|
return SQLResult
|
|
59
58
|
|
|
60
59
|
def build(self) -> "SafeQuery":
|
|
@@ -62,11 +61,10 @@ class DDLBuilder(QueryBuilder[Any]):
|
|
|
62
61
|
self._expression = self._create_base_expression()
|
|
63
62
|
return super().build()
|
|
64
63
|
|
|
65
|
-
def to_statement(self, config: "Optional[
|
|
64
|
+
def to_statement(self, config: "Optional[StatementConfig]" = None) -> "SQL":
|
|
66
65
|
return super().to_statement(config=config)
|
|
67
66
|
|
|
68
67
|
|
|
69
|
-
# --- Data Structures for CREATE TABLE ---
|
|
70
68
|
@dataclass
|
|
71
69
|
class ColumnDefinition:
|
|
72
70
|
"""Column definition for CREATE TABLE."""
|
|
@@ -80,7 +78,7 @@ class ColumnDefinition:
|
|
|
80
78
|
auto_increment: bool = False
|
|
81
79
|
comment: "Optional[str]" = None
|
|
82
80
|
check: "Optional[str]" = None
|
|
83
|
-
generated: "Optional[str]" = None
|
|
81
|
+
generated: "Optional[str]" = None
|
|
84
82
|
collate: "Optional[str]" = None
|
|
85
83
|
|
|
86
84
|
|
|
@@ -88,7 +86,7 @@ class ColumnDefinition:
|
|
|
88
86
|
class ConstraintDefinition:
|
|
89
87
|
"""Constraint definition for CREATE TABLE."""
|
|
90
88
|
|
|
91
|
-
constraint_type: str
|
|
89
|
+
constraint_type: str
|
|
92
90
|
name: "Optional[str]" = None
|
|
93
91
|
columns: "list[str]" = field(default_factory=list)
|
|
94
92
|
references_table: "Optional[str]" = None
|
|
@@ -100,7 +98,6 @@ class ConstraintDefinition:
|
|
|
100
98
|
initially_deferred: bool = False
|
|
101
99
|
|
|
102
100
|
|
|
103
|
-
# --- CREATE TABLE ---
|
|
104
101
|
@dataclass
|
|
105
102
|
class CreateTable(DDLBuilder):
|
|
106
103
|
"""Builder for CREATE TABLE statements with columns and constraints.
|
|
@@ -201,7 +198,6 @@ class CreateTable(DDLBuilder):
|
|
|
201
198
|
|
|
202
199
|
self._columns.append(column_def)
|
|
203
200
|
|
|
204
|
-
# If primary key is specified on column, also add a constraint
|
|
205
201
|
if primary_key and not any(c.constraint_type == "PRIMARY KEY" for c in self._constraints):
|
|
206
202
|
self.primary_key_constraint([name])
|
|
207
203
|
|
|
@@ -209,10 +205,8 @@ class CreateTable(DDLBuilder):
|
|
|
209
205
|
|
|
210
206
|
def primary_key_constraint(self, columns: "Union[str, list[str]]", name: "Optional[str]" = None) -> "Self":
|
|
211
207
|
"""Add a primary key constraint."""
|
|
212
|
-
# Normalize column list
|
|
213
208
|
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
214
209
|
|
|
215
|
-
# Validation
|
|
216
210
|
if not col_list:
|
|
217
211
|
self._raise_sql_builder_error("Primary key must include at least one column")
|
|
218
212
|
|
|
@@ -239,12 +233,10 @@ class CreateTable(DDLBuilder):
|
|
|
239
233
|
initially_deferred: bool = False,
|
|
240
234
|
) -> "Self":
|
|
241
235
|
"""Add a foreign key constraint."""
|
|
242
|
-
# Normalize inputs
|
|
243
236
|
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
244
237
|
|
|
245
238
|
ref_col_list = [references_columns] if isinstance(references_columns, str) else list(references_columns)
|
|
246
239
|
|
|
247
|
-
# Validation
|
|
248
240
|
if len(col_list) != len(ref_col_list):
|
|
249
241
|
self._raise_sql_builder_error("Foreign key columns and referenced columns must have same length")
|
|
250
242
|
|
|
@@ -271,7 +263,6 @@ class CreateTable(DDLBuilder):
|
|
|
271
263
|
|
|
272
264
|
def unique_constraint(self, columns: "Union[str, list[str]]", name: "Optional[str]" = None) -> "Self":
|
|
273
265
|
"""Add a unique constraint."""
|
|
274
|
-
# Normalize column list
|
|
275
266
|
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
276
267
|
|
|
277
268
|
if not col_list:
|
|
@@ -289,11 +280,9 @@ class CreateTable(DDLBuilder):
|
|
|
289
280
|
|
|
290
281
|
condition_str: str
|
|
291
282
|
if hasattr(condition, "sqlglot_expression"):
|
|
292
|
-
# This is a ColumnExpression - render as raw SQL for DDL (no parameters)
|
|
293
283
|
sqlglot_expr = getattr(condition, "sqlglot_expression", None)
|
|
294
284
|
condition_str = sqlglot_expr.sql(dialect=self.dialect) if sqlglot_expr else str(condition)
|
|
295
285
|
else:
|
|
296
|
-
# String condition - use as-is
|
|
297
286
|
condition_str = str(condition)
|
|
298
287
|
|
|
299
288
|
constraint = ConstraintDefinition(constraint_type="CHECK", name=name, condition=condition_str)
|
|
@@ -342,7 +331,6 @@ class CreateTable(DDLBuilder):
|
|
|
342
331
|
column_defs.append(col_expr)
|
|
343
332
|
|
|
344
333
|
for constraint in self._constraints:
|
|
345
|
-
# Skip PRIMARY KEY constraints that are already defined on columns
|
|
346
334
|
if constraint.constraint_type == "PRIMARY KEY" and len(constraint.columns) == 1:
|
|
347
335
|
col_name = constraint.columns[0]
|
|
348
336
|
if any(c.name == col_name and c.primary_key for c in self._columns):
|
|
@@ -362,16 +350,11 @@ class CreateTable(DDLBuilder):
|
|
|
362
350
|
if self._tablespace:
|
|
363
351
|
props.append(exp.Property(this=exp.to_identifier("TABLESPACE"), value=exp.to_identifier(self._tablespace)))
|
|
364
352
|
if self._partition_by:
|
|
365
|
-
props.append(
|
|
366
|
-
exp.Property(this=exp.to_identifier("PARTITION BY"), value=exp.Literal.string(self._partition_by))
|
|
367
|
-
)
|
|
353
|
+
props.append(exp.Property(this=exp.to_identifier("PARTITION BY"), value=exp.convert(self._partition_by)))
|
|
368
354
|
|
|
369
355
|
for key, value in self._table_options.items():
|
|
370
|
-
if key != "engine":
|
|
371
|
-
|
|
372
|
-
props.append(exp.Property(this=exp.to_identifier(key.upper()), value=exp.Literal.string(value)))
|
|
373
|
-
else:
|
|
374
|
-
props.append(exp.Property(this=exp.to_identifier(key.upper()), value=exp.Literal.number(value)))
|
|
356
|
+
if key != "engine":
|
|
357
|
+
props.append(exp.Property(this=exp.to_identifier(key.upper()), value=exp.convert(value)))
|
|
375
358
|
|
|
376
359
|
properties_node = exp.Properties(expressions=props) if props else None
|
|
377
360
|
|
|
@@ -402,14 +385,13 @@ class CreateTable(DDLBuilder):
|
|
|
402
385
|
return build_constraint_expression(constraint)
|
|
403
386
|
|
|
404
387
|
|
|
405
|
-
# --- DROP TABLE ---
|
|
406
388
|
@dataclass
|
|
407
389
|
class DropTable(DDLBuilder):
|
|
408
390
|
"""Builder for DROP TABLE [IF EXISTS] ... [CASCADE|RESTRICT]."""
|
|
409
391
|
|
|
410
392
|
_table_name: Optional[str] = None
|
|
411
393
|
_if_exists: bool = False
|
|
412
|
-
_cascade: Optional[bool] = None
|
|
394
|
+
_cascade: Optional[bool] = None
|
|
413
395
|
|
|
414
396
|
def __init__(self, table_name: str, **kwargs: Any) -> None:
|
|
415
397
|
"""Initialize DROP TABLE with table name.
|
|
@@ -445,7 +427,6 @@ class DropTable(DDLBuilder):
|
|
|
445
427
|
)
|
|
446
428
|
|
|
447
429
|
|
|
448
|
-
# --- DROP INDEX ---
|
|
449
430
|
@dataclass
|
|
450
431
|
class DropIndex(DDLBuilder):
|
|
451
432
|
"""Builder for DROP INDEX [IF EXISTS] ... [ON table] [CASCADE|RESTRICT]."""
|
|
@@ -497,7 +478,6 @@ class DropIndex(DDLBuilder):
|
|
|
497
478
|
)
|
|
498
479
|
|
|
499
480
|
|
|
500
|
-
# --- DROP VIEW ---
|
|
501
481
|
@dataclass
|
|
502
482
|
class DropView(DDLBuilder):
|
|
503
483
|
"""Builder for DROP VIEW [IF EXISTS] ... [CASCADE|RESTRICT]."""
|
|
@@ -530,7 +510,6 @@ class DropView(DDLBuilder):
|
|
|
530
510
|
)
|
|
531
511
|
|
|
532
512
|
|
|
533
|
-
# --- DROP SCHEMA ---
|
|
534
513
|
@dataclass
|
|
535
514
|
class DropSchema(DDLBuilder):
|
|
536
515
|
"""Builder for DROP SCHEMA [IF EXISTS] ... [CASCADE|RESTRICT]."""
|
|
@@ -563,7 +542,6 @@ class DropSchema(DDLBuilder):
|
|
|
563
542
|
)
|
|
564
543
|
|
|
565
544
|
|
|
566
|
-
# --- CREATE INDEX ---
|
|
567
545
|
@dataclass
|
|
568
546
|
class CreateIndex(DDLBuilder):
|
|
569
547
|
"""Builder for CREATE [UNIQUE] INDEX [IF NOT EXISTS] ... ON ... (...).
|
|
@@ -588,7 +566,6 @@ class CreateIndex(DDLBuilder):
|
|
|
588
566
|
"""
|
|
589
567
|
super().__init__(**kwargs)
|
|
590
568
|
self._index_name = index_name
|
|
591
|
-
# Initialize dataclass fields that may not be set by super().__init__
|
|
592
569
|
if not hasattr(self, "_columns"):
|
|
593
570
|
self._columns = []
|
|
594
571
|
|
|
@@ -636,7 +613,6 @@ class CreateIndex(DDLBuilder):
|
|
|
636
613
|
where_expr = None
|
|
637
614
|
if self._where:
|
|
638
615
|
where_expr = exp.condition(self._where) if isinstance(self._where, str) else self._where
|
|
639
|
-
# Use exp.Create for CREATE INDEX
|
|
640
616
|
return exp.Create(
|
|
641
617
|
kind="INDEX",
|
|
642
618
|
this=exp.to_identifier(self._index_name),
|
|
@@ -649,14 +625,13 @@ class CreateIndex(DDLBuilder):
|
|
|
649
625
|
)
|
|
650
626
|
|
|
651
627
|
|
|
652
|
-
# --- TRUNCATE TABLE ---
|
|
653
628
|
@dataclass
|
|
654
|
-
class
|
|
629
|
+
class Truncate(DDLBuilder):
|
|
655
630
|
"""Builder for TRUNCATE TABLE ... [CASCADE|RESTRICT] [RESTART IDENTITY|CONTINUE IDENTITY]."""
|
|
656
631
|
|
|
657
632
|
_table_name: Optional[str] = None
|
|
658
633
|
_cascade: Optional[bool] = None
|
|
659
|
-
_identity: Optional[str] = None
|
|
634
|
+
_identity: Optional[str] = None
|
|
660
635
|
|
|
661
636
|
def table(self, name: str) -> Self:
|
|
662
637
|
self._table_name = name
|
|
@@ -685,7 +660,6 @@ class TruncateTable(DDLBuilder):
|
|
|
685
660
|
return exp.TruncateTable(this=exp.to_table(self._table_name), cascade=self._cascade, identity=identity_expr)
|
|
686
661
|
|
|
687
662
|
|
|
688
|
-
# --- ALTER TABLE ---
|
|
689
663
|
@dataclass
|
|
690
664
|
class AlterOperation:
|
|
691
665
|
"""Represents a single ALTER TABLE operation."""
|
|
@@ -702,7 +676,6 @@ class AlterOperation:
|
|
|
702
676
|
using_expression: "Optional[str]" = None
|
|
703
677
|
|
|
704
678
|
|
|
705
|
-
# --- CREATE SCHEMA ---
|
|
706
679
|
@dataclass
|
|
707
680
|
class CreateSchema(DDLBuilder):
|
|
708
681
|
"""Builder for CREATE SCHEMA [IF NOT EXISTS] schema_name [AUTHORIZATION user_name]."""
|
|
@@ -766,7 +739,7 @@ class CreateTableAsSelect(DDLBuilder):
|
|
|
766
739
|
_table_name: Optional[str] = None
|
|
767
740
|
_if_not_exists: bool = False
|
|
768
741
|
_columns: list[str] = field(default_factory=list)
|
|
769
|
-
_select_query: Optional[object] = None
|
|
742
|
+
_select_query: Optional[object] = None
|
|
770
743
|
|
|
771
744
|
def name(self, table_name: str) -> Self:
|
|
772
745
|
self._table_name = table_name
|
|
@@ -791,39 +764,32 @@ class CreateTableAsSelect(DDLBuilder):
|
|
|
791
764
|
self._raise_sql_builder_error("SELECT query must be set for CREATE TABLE AS SELECT.")
|
|
792
765
|
|
|
793
766
|
select_expr = None
|
|
794
|
-
|
|
795
|
-
from sqlspec.
|
|
796
|
-
from sqlspec.statement
|
|
767
|
+
select_parameters = None
|
|
768
|
+
from sqlspec.builder._select import Select
|
|
769
|
+
from sqlspec.core.statement import SQL
|
|
797
770
|
|
|
798
771
|
if isinstance(self._select_query, SQL):
|
|
799
772
|
select_expr = self._select_query.expression
|
|
800
|
-
|
|
773
|
+
select_parameters = getattr(self._select_query, "parameters", None)
|
|
801
774
|
elif isinstance(self._select_query, Select):
|
|
802
775
|
select_expr = getattr(self._select_query, "_expression", None)
|
|
803
|
-
|
|
776
|
+
select_parameters = getattr(self._select_query, "_parameters", None)
|
|
804
777
|
|
|
805
778
|
with_ctes = getattr(self._select_query, "_with_ctes", {})
|
|
806
779
|
if with_ctes and select_expr and isinstance(select_expr, exp.Select):
|
|
807
780
|
for alias, cte in with_ctes.items():
|
|
808
781
|
if hasattr(select_expr, "with_"):
|
|
809
|
-
select_expr = select_expr.with_(
|
|
810
|
-
cte.this, # The CTE's SELECT expression
|
|
811
|
-
as_=alias,
|
|
812
|
-
copy=False,
|
|
813
|
-
)
|
|
782
|
+
select_expr = select_expr.with_(cte.this, as_=alias, copy=False)
|
|
814
783
|
elif isinstance(self._select_query, str):
|
|
815
784
|
select_expr = exp.maybe_parse(self._select_query)
|
|
816
|
-
|
|
785
|
+
select_parameters = None
|
|
817
786
|
else:
|
|
818
787
|
self._raise_sql_builder_error("Unsupported type for SELECT query in CTAS.")
|
|
819
788
|
if select_expr is None:
|
|
820
789
|
self._raise_sql_builder_error("SELECT query must be a valid SELECT expression.")
|
|
821
790
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
for p_name, p_value in select_params.items():
|
|
825
|
-
# Always preserve the original parameter name
|
|
826
|
-
# The SELECT query already has unique parameter names
|
|
791
|
+
if select_parameters:
|
|
792
|
+
for p_name, p_value in select_parameters.items():
|
|
827
793
|
self._parameters[p_name] = p_value
|
|
828
794
|
|
|
829
795
|
schema_expr = None
|
|
@@ -849,8 +815,8 @@ class CreateMaterializedView(DDLBuilder):
|
|
|
849
815
|
_view_name: Optional[str] = None
|
|
850
816
|
_if_not_exists: bool = False
|
|
851
817
|
_columns: list[str] = field(default_factory=list)
|
|
852
|
-
_select_query: Optional[object] = None
|
|
853
|
-
_with_data: Optional[bool] = None
|
|
818
|
+
_select_query: Optional[object] = None
|
|
819
|
+
_with_data: Optional[bool] = None
|
|
854
820
|
_refresh_mode: Optional[str] = None
|
|
855
821
|
_storage_parameters: dict[str, Any] = field(default_factory=dict)
|
|
856
822
|
_tablespace: Optional[str] = None
|
|
@@ -908,29 +874,26 @@ class CreateMaterializedView(DDLBuilder):
|
|
|
908
874
|
self._raise_sql_builder_error("SELECT query must be set for CREATE MATERIALIZED VIEW.")
|
|
909
875
|
|
|
910
876
|
select_expr = None
|
|
911
|
-
|
|
912
|
-
from sqlspec.
|
|
913
|
-
from sqlspec.statement
|
|
877
|
+
select_parameters = None
|
|
878
|
+
from sqlspec.builder._select import Select
|
|
879
|
+
from sqlspec.core.statement import SQL
|
|
914
880
|
|
|
915
881
|
if isinstance(self._select_query, SQL):
|
|
916
882
|
select_expr = self._select_query.expression
|
|
917
|
-
|
|
883
|
+
select_parameters = getattr(self._select_query, "parameters", None)
|
|
918
884
|
elif isinstance(self._select_query, Select):
|
|
919
885
|
select_expr = getattr(self._select_query, "_expression", None)
|
|
920
|
-
|
|
886
|
+
select_parameters = getattr(self._select_query, "_parameters", None)
|
|
921
887
|
elif isinstance(self._select_query, str):
|
|
922
888
|
select_expr = exp.maybe_parse(self._select_query)
|
|
923
|
-
|
|
889
|
+
select_parameters = None
|
|
924
890
|
else:
|
|
925
891
|
self._raise_sql_builder_error("Unsupported type for SELECT query in materialized view.")
|
|
926
892
|
if select_expr is None or not isinstance(select_expr, exp.Select):
|
|
927
893
|
self._raise_sql_builder_error("SELECT query must be a valid SELECT expression.")
|
|
928
894
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
for p_name, p_value in select_params.items():
|
|
932
|
-
# Always preserve the original parameter name
|
|
933
|
-
# The SELECT query already has unique parameter names
|
|
895
|
+
if select_parameters:
|
|
896
|
+
for p_name, p_value in select_parameters.items():
|
|
934
897
|
self._parameters[p_name] = p_value
|
|
935
898
|
|
|
936
899
|
schema_expr = None
|
|
@@ -939,9 +902,7 @@ class CreateMaterializedView(DDLBuilder):
|
|
|
939
902
|
|
|
940
903
|
props: list[exp.Property] = []
|
|
941
904
|
if self._refresh_mode:
|
|
942
|
-
props.append(
|
|
943
|
-
exp.Property(this=exp.to_identifier("REFRESH_MODE"), value=exp.Literal.string(self._refresh_mode))
|
|
944
|
-
)
|
|
905
|
+
props.append(exp.Property(this=exp.to_identifier("REFRESH_MODE"), value=exp.convert(self._refresh_mode)))
|
|
945
906
|
if self._tablespace:
|
|
946
907
|
props.append(exp.Property(this=exp.to_identifier("TABLESPACE"), value=exp.to_identifier(self._tablespace)))
|
|
947
908
|
if self._using_index:
|
|
@@ -949,12 +910,10 @@ class CreateMaterializedView(DDLBuilder):
|
|
|
949
910
|
exp.Property(this=exp.to_identifier("USING_INDEX"), value=exp.to_identifier(self._using_index))
|
|
950
911
|
)
|
|
951
912
|
for k, v in self._storage_parameters.items():
|
|
952
|
-
props.append(exp.Property(this=exp.to_identifier(k), value=exp.
|
|
913
|
+
props.append(exp.Property(this=exp.to_identifier(k), value=exp.convert(str(v))))
|
|
953
914
|
if self._with_data is not None:
|
|
954
915
|
props.append(exp.Property(this=exp.to_identifier("WITH_DATA" if self._with_data else "NO_DATA")))
|
|
955
|
-
props.extend(
|
|
956
|
-
exp.Property(this=exp.to_identifier("HINT"), value=exp.Literal.string(hint)) for hint in self._hints
|
|
957
|
-
)
|
|
916
|
+
props.extend(exp.Property(this=exp.to_identifier("HINT"), value=exp.convert(hint)) for hint in self._hints)
|
|
958
917
|
properties_node = exp.Properties(expressions=props) if props else None
|
|
959
918
|
|
|
960
919
|
return exp.Create(
|
|
@@ -977,7 +936,7 @@ class CreateView(DDLBuilder):
|
|
|
977
936
|
_view_name: Optional[str] = None
|
|
978
937
|
_if_not_exists: bool = False
|
|
979
938
|
_columns: list[str] = field(default_factory=list)
|
|
980
|
-
_select_query: Optional[object] = None
|
|
939
|
+
_select_query: Optional[object] = None
|
|
981
940
|
_hints: list[str] = field(default_factory=list)
|
|
982
941
|
|
|
983
942
|
def name(self, view_name: str) -> Self:
|
|
@@ -1007,29 +966,26 @@ class CreateView(DDLBuilder):
|
|
|
1007
966
|
self._raise_sql_builder_error("SELECT query must be set for CREATE VIEW.")
|
|
1008
967
|
|
|
1009
968
|
select_expr = None
|
|
1010
|
-
|
|
1011
|
-
from sqlspec.
|
|
1012
|
-
from sqlspec.statement
|
|
969
|
+
select_parameters = None
|
|
970
|
+
from sqlspec.builder._select import Select
|
|
971
|
+
from sqlspec.core.statement import SQL
|
|
1013
972
|
|
|
1014
973
|
if isinstance(self._select_query, SQL):
|
|
1015
974
|
select_expr = self._select_query.expression
|
|
1016
|
-
|
|
975
|
+
select_parameters = getattr(self._select_query, "parameters", None)
|
|
1017
976
|
elif isinstance(self._select_query, Select):
|
|
1018
977
|
select_expr = getattr(self._select_query, "_expression", None)
|
|
1019
|
-
|
|
978
|
+
select_parameters = getattr(self._select_query, "_parameters", None)
|
|
1020
979
|
elif isinstance(self._select_query, str):
|
|
1021
980
|
select_expr = exp.maybe_parse(self._select_query)
|
|
1022
|
-
|
|
981
|
+
select_parameters = None
|
|
1023
982
|
else:
|
|
1024
983
|
self._raise_sql_builder_error("Unsupported type for SELECT query in view.")
|
|
1025
984
|
if select_expr is None or not isinstance(select_expr, exp.Select):
|
|
1026
985
|
self._raise_sql_builder_error("SELECT query must be a valid SELECT expression.")
|
|
1027
986
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
for p_name, p_value in select_params.items():
|
|
1031
|
-
# Always preserve the original parameter name
|
|
1032
|
-
# The SELECT query already has unique parameter names
|
|
987
|
+
if select_parameters:
|
|
988
|
+
for p_name, p_value in select_parameters.items():
|
|
1033
989
|
self._parameters[p_name] = p_value
|
|
1034
990
|
|
|
1035
991
|
schema_expr = None
|
|
@@ -1037,7 +993,7 @@ class CreateView(DDLBuilder):
|
|
|
1037
993
|
schema_expr = exp.Schema(expressions=[exp.column(c) for c in self._columns])
|
|
1038
994
|
|
|
1039
995
|
props: list[exp.Property] = [
|
|
1040
|
-
exp.Property(this=exp.to_identifier("HINT"), value=exp.
|
|
996
|
+
exp.Property(this=exp.to_identifier("HINT"), value=exp.convert(h)) for h in self._hints
|
|
1041
997
|
]
|
|
1042
998
|
properties_node = exp.Properties(expressions=props) if props else None
|
|
1043
999
|
|
|
@@ -1177,17 +1133,14 @@ class AlterTable(DDLBuilder):
|
|
|
1177
1133
|
if constraint_type.upper() not in valid_types:
|
|
1178
1134
|
self._raise_sql_builder_error(f"Invalid constraint type: {constraint_type}")
|
|
1179
1135
|
|
|
1180
|
-
# Normalize columns
|
|
1181
1136
|
col_list = None
|
|
1182
1137
|
if columns is not None:
|
|
1183
1138
|
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
1184
1139
|
|
|
1185
|
-
# Normalize reference columns
|
|
1186
1140
|
ref_col_list = None
|
|
1187
1141
|
if references_columns is not None:
|
|
1188
1142
|
ref_col_list = [references_columns] if isinstance(references_columns, str) else list(references_columns)
|
|
1189
1143
|
|
|
1190
|
-
# Handle ColumnExpression for CHECK constraints
|
|
1191
1144
|
condition_str: Optional[str] = None
|
|
1192
1145
|
if condition is not None:
|
|
1193
1146
|
if hasattr(condition, "sqlglot_expression"):
|
|
@@ -1238,24 +1191,6 @@ class AlterTable(DDLBuilder):
|
|
|
1238
1191
|
self._operations.append(operation)
|
|
1239
1192
|
return self
|
|
1240
1193
|
|
|
1241
|
-
def set_default(self, column: str, default: "Any") -> "Self":
|
|
1242
|
-
"""Set default value for a column."""
|
|
1243
|
-
operation = AlterOperation(
|
|
1244
|
-
operation_type="ALTER COLUMN SET DEFAULT",
|
|
1245
|
-
column_name=column,
|
|
1246
|
-
column_definition=ColumnDefinition(name=column, dtype="", default=default),
|
|
1247
|
-
)
|
|
1248
|
-
|
|
1249
|
-
self._operations.append(operation)
|
|
1250
|
-
return self
|
|
1251
|
-
|
|
1252
|
-
def drop_default(self, column: str) -> "Self":
|
|
1253
|
-
"""Remove default value from a column."""
|
|
1254
|
-
operation = AlterOperation(operation_type="ALTER COLUMN DROP DEFAULT", column_name=column)
|
|
1255
|
-
|
|
1256
|
-
self._operations.append(operation)
|
|
1257
|
-
return self
|
|
1258
|
-
|
|
1259
1194
|
def _create_base_expression(self) -> "exp.Expression":
|
|
1260
1195
|
"""Create the SQLGlot expression for ALTER TABLE."""
|
|
1261
1196
|
if not self._operations:
|
|
@@ -1277,9 +1212,6 @@ class AlterTable(DDLBuilder):
|
|
|
1277
1212
|
if op_type == "ADD COLUMN":
|
|
1278
1213
|
if not op.column_definition:
|
|
1279
1214
|
self._raise_sql_builder_error("Column definition required for ADD COLUMN")
|
|
1280
|
-
# SQLGlot expects a ColumnDef directly for ADD COLUMN actions
|
|
1281
|
-
# Note: SQLGlot doesn't support AFTER/FIRST positioning in standard ALTER TABLE ADD COLUMN
|
|
1282
|
-
# These would need to be handled at the dialect level
|
|
1283
1215
|
return build_column_expression(op.column_definition)
|
|
1284
1216
|
|
|
1285
1217
|
if op_type == "DROP COLUMN":
|
|
@@ -1327,22 +1259,22 @@ class AlterTable(DDLBuilder):
|
|
|
1327
1259
|
if default_val.upper() in {"CURRENT_TIMESTAMP", "CURRENT_DATE", "CURRENT_TIME"} or "(" in default_val:
|
|
1328
1260
|
default_expr = exp.maybe_parse(default_val)
|
|
1329
1261
|
else:
|
|
1330
|
-
default_expr = exp.
|
|
1262
|
+
default_expr = exp.convert(default_val)
|
|
1331
1263
|
elif isinstance(default_val, (int, float)):
|
|
1332
|
-
default_expr = exp.
|
|
1264
|
+
default_expr = exp.convert(default_val)
|
|
1333
1265
|
elif default_val is True:
|
|
1334
1266
|
default_expr = exp.true()
|
|
1335
1267
|
elif default_val is False:
|
|
1336
1268
|
default_expr = exp.false()
|
|
1337
1269
|
else:
|
|
1338
|
-
default_expr = exp.
|
|
1270
|
+
default_expr = exp.convert(str(default_val))
|
|
1339
1271
|
return exp.AlterColumn(this=exp.to_identifier(op.column_name), default=default_expr)
|
|
1340
1272
|
|
|
1341
1273
|
if op_type == "ALTER COLUMN DROP DEFAULT":
|
|
1342
1274
|
return exp.AlterColumn(this=exp.to_identifier(op.column_name), kind="DROP DEFAULT")
|
|
1343
1275
|
|
|
1344
1276
|
self._raise_sql_builder_error(f"Unknown operation type: {op.operation_type}")
|
|
1345
|
-
raise AssertionError
|
|
1277
|
+
raise AssertionError
|
|
1346
1278
|
|
|
1347
1279
|
|
|
1348
1280
|
@dataclass
|
|
@@ -1352,7 +1284,7 @@ class CommentOn(DDLBuilder):
|
|
|
1352
1284
|
Supports COMMENT ON TABLE and COMMENT ON COLUMN.
|
|
1353
1285
|
"""
|
|
1354
1286
|
|
|
1355
|
-
_target_type: Optional[str] = None
|
|
1287
|
+
_target_type: Optional[str] = None
|
|
1356
1288
|
_table: Optional[str] = None
|
|
1357
1289
|
_column: Optional[str] = None
|
|
1358
1290
|
_comment: Optional[str] = None
|
|
@@ -1375,17 +1307,15 @@ class CommentOn(DDLBuilder):
|
|
|
1375
1307
|
|
|
1376
1308
|
def _create_base_expression(self) -> exp.Expression:
|
|
1377
1309
|
if self._target_type == "TABLE" and self._table and self._comment is not None:
|
|
1378
|
-
return exp.Comment(
|
|
1379
|
-
this=exp.to_table(self._table), kind="TABLE", expression=exp.Literal.string(self._comment)
|
|
1380
|
-
)
|
|
1310
|
+
return exp.Comment(this=exp.to_table(self._table), kind="TABLE", expression=exp.convert(self._comment))
|
|
1381
1311
|
if self._target_type == "COLUMN" and self._table and self._column and self._comment is not None:
|
|
1382
1312
|
return exp.Comment(
|
|
1383
1313
|
this=exp.Column(table=self._table, this=self._column),
|
|
1384
1314
|
kind="COLUMN",
|
|
1385
|
-
expression=exp.
|
|
1315
|
+
expression=exp.convert(self._comment),
|
|
1386
1316
|
)
|
|
1387
1317
|
self._raise_sql_builder_error("Must specify target and comment for COMMENT ON statement.")
|
|
1388
|
-
raise AssertionError
|
|
1318
|
+
raise AssertionError
|
|
1389
1319
|
|
|
1390
1320
|
|
|
1391
1321
|
@dataclass
|
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Optional
|
|
|
5
5
|
from sqlglot import exp
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
-
from sqlspec.
|
|
8
|
+
from sqlspec.builder._ddl import ColumnDefinition, ConstraintDefinition
|
|
9
9
|
|
|
10
10
|
__all__ = ("build_column_expression", "build_constraint_expression")
|
|
11
11
|
|
|
@@ -31,15 +31,9 @@ def build_column_expression(col: "ColumnDefinition") -> "exp.Expression":
|
|
|
31
31
|
if col.default.upper() in {"CURRENT_TIMESTAMP", "CURRENT_DATE", "CURRENT_TIME"} or "(" in col.default:
|
|
32
32
|
default_expr = exp.maybe_parse(col.default)
|
|
33
33
|
else:
|
|
34
|
-
default_expr = exp.
|
|
35
|
-
elif isinstance(col.default, (int, float)):
|
|
36
|
-
default_expr = exp.Literal.number(col.default)
|
|
37
|
-
elif col.default is True:
|
|
38
|
-
default_expr = exp.true()
|
|
39
|
-
elif col.default is False:
|
|
40
|
-
default_expr = exp.false()
|
|
34
|
+
default_expr = exp.convert(col.default)
|
|
41
35
|
else:
|
|
42
|
-
default_expr = exp.
|
|
36
|
+
default_expr = exp.convert(col.default)
|
|
43
37
|
|
|
44
38
|
constraints.append(exp.ColumnConstraint(kind=default_expr))
|
|
45
39
|
|
|
@@ -48,7 +42,7 @@ def build_column_expression(col: "ColumnDefinition") -> "exp.Expression":
|
|
|
48
42
|
constraints.append(exp.ColumnConstraint(kind=check_expr))
|
|
49
43
|
|
|
50
44
|
if col.comment:
|
|
51
|
-
constraints.append(exp.ColumnConstraint(kind=exp.CommentColumnConstraint(this=exp.
|
|
45
|
+
constraints.append(exp.ColumnConstraint(kind=exp.CommentColumnConstraint(this=exp.convert(col.comment))))
|
|
52
46
|
|
|
53
47
|
if col.generated:
|
|
54
48
|
generated_expr = exp.GeneratedAsIdentityColumnConstraint(this=exp.maybe_parse(col.generated))
|