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.

Files changed (185) hide show
  1. sqlspec/__init__.py +71 -8
  2. sqlspec/__main__.py +12 -0
  3. sqlspec/__metadata__.py +1 -3
  4. sqlspec/_serialization.py +1 -2
  5. sqlspec/_sql.py +930 -136
  6. sqlspec/_typing.py +278 -142
  7. sqlspec/adapters/adbc/__init__.py +4 -3
  8. sqlspec/adapters/adbc/_types.py +12 -0
  9. sqlspec/adapters/adbc/config.py +116 -285
  10. sqlspec/adapters/adbc/driver.py +462 -340
  11. sqlspec/adapters/aiosqlite/__init__.py +18 -3
  12. sqlspec/adapters/aiosqlite/_types.py +13 -0
  13. sqlspec/adapters/aiosqlite/config.py +202 -150
  14. sqlspec/adapters/aiosqlite/driver.py +226 -247
  15. sqlspec/adapters/asyncmy/__init__.py +18 -3
  16. sqlspec/adapters/asyncmy/_types.py +12 -0
  17. sqlspec/adapters/asyncmy/config.py +80 -199
  18. sqlspec/adapters/asyncmy/driver.py +257 -215
  19. sqlspec/adapters/asyncpg/__init__.py +19 -4
  20. sqlspec/adapters/asyncpg/_types.py +17 -0
  21. sqlspec/adapters/asyncpg/config.py +81 -214
  22. sqlspec/adapters/asyncpg/driver.py +284 -359
  23. sqlspec/adapters/bigquery/__init__.py +17 -3
  24. sqlspec/adapters/bigquery/_types.py +12 -0
  25. sqlspec/adapters/bigquery/config.py +191 -299
  26. sqlspec/adapters/bigquery/driver.py +474 -634
  27. sqlspec/adapters/duckdb/__init__.py +14 -3
  28. sqlspec/adapters/duckdb/_types.py +12 -0
  29. sqlspec/adapters/duckdb/config.py +414 -397
  30. sqlspec/adapters/duckdb/driver.py +342 -393
  31. sqlspec/adapters/oracledb/__init__.py +19 -5
  32. sqlspec/adapters/oracledb/_types.py +14 -0
  33. sqlspec/adapters/oracledb/config.py +123 -458
  34. sqlspec/adapters/oracledb/driver.py +505 -531
  35. sqlspec/adapters/psqlpy/__init__.py +13 -3
  36. sqlspec/adapters/psqlpy/_types.py +11 -0
  37. sqlspec/adapters/psqlpy/config.py +93 -307
  38. sqlspec/adapters/psqlpy/driver.py +504 -213
  39. sqlspec/adapters/psycopg/__init__.py +19 -5
  40. sqlspec/adapters/psycopg/_types.py +17 -0
  41. sqlspec/adapters/psycopg/config.py +143 -472
  42. sqlspec/adapters/psycopg/driver.py +704 -825
  43. sqlspec/adapters/sqlite/__init__.py +14 -3
  44. sqlspec/adapters/sqlite/_types.py +11 -0
  45. sqlspec/adapters/sqlite/config.py +208 -142
  46. sqlspec/adapters/sqlite/driver.py +263 -278
  47. sqlspec/base.py +105 -9
  48. sqlspec/{statement/builder → builder}/__init__.py +12 -14
  49. sqlspec/{statement/builder/base.py → builder/_base.py} +184 -86
  50. sqlspec/{statement/builder/column.py → builder/_column.py} +97 -60
  51. sqlspec/{statement/builder/ddl.py → builder/_ddl.py} +61 -131
  52. sqlspec/{statement/builder → builder}/_ddl_utils.py +4 -10
  53. sqlspec/{statement/builder/delete.py → builder/_delete.py} +10 -30
  54. sqlspec/builder/_insert.py +421 -0
  55. sqlspec/builder/_merge.py +71 -0
  56. sqlspec/{statement/builder → builder}/_parsing_utils.py +49 -26
  57. sqlspec/builder/_select.py +170 -0
  58. sqlspec/{statement/builder/update.py → builder/_update.py} +16 -20
  59. sqlspec/builder/mixins/__init__.py +55 -0
  60. sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
  61. sqlspec/{statement/builder/mixins/_delete_from.py → builder/mixins/_delete_operations.py} +8 -1
  62. sqlspec/builder/mixins/_insert_operations.py +244 -0
  63. sqlspec/{statement/builder/mixins/_join.py → builder/mixins/_join_operations.py} +45 -13
  64. sqlspec/{statement/builder/mixins/_merge_clauses.py → builder/mixins/_merge_operations.py} +188 -30
  65. sqlspec/builder/mixins/_order_limit_operations.py +135 -0
  66. sqlspec/builder/mixins/_pivot_operations.py +153 -0
  67. sqlspec/builder/mixins/_select_operations.py +604 -0
  68. sqlspec/builder/mixins/_update_operations.py +202 -0
  69. sqlspec/builder/mixins/_where_clause.py +644 -0
  70. sqlspec/cli.py +247 -0
  71. sqlspec/config.py +183 -138
  72. sqlspec/core/__init__.py +63 -0
  73. sqlspec/core/cache.py +871 -0
  74. sqlspec/core/compiler.py +417 -0
  75. sqlspec/core/filters.py +830 -0
  76. sqlspec/core/hashing.py +310 -0
  77. sqlspec/core/parameters.py +1237 -0
  78. sqlspec/core/result.py +677 -0
  79. sqlspec/{statement → core}/splitter.py +321 -191
  80. sqlspec/core/statement.py +676 -0
  81. sqlspec/driver/__init__.py +7 -10
  82. sqlspec/driver/_async.py +422 -163
  83. sqlspec/driver/_common.py +545 -287
  84. sqlspec/driver/_sync.py +426 -160
  85. sqlspec/driver/mixins/__init__.py +2 -13
  86. sqlspec/driver/mixins/_result_tools.py +193 -0
  87. sqlspec/driver/mixins/_sql_translator.py +65 -14
  88. sqlspec/exceptions.py +5 -252
  89. sqlspec/extensions/aiosql/adapter.py +93 -96
  90. sqlspec/extensions/litestar/__init__.py +2 -1
  91. sqlspec/extensions/litestar/cli.py +48 -0
  92. sqlspec/extensions/litestar/config.py +0 -1
  93. sqlspec/extensions/litestar/handlers.py +15 -26
  94. sqlspec/extensions/litestar/plugin.py +21 -16
  95. sqlspec/extensions/litestar/providers.py +17 -52
  96. sqlspec/loader.py +423 -104
  97. sqlspec/migrations/__init__.py +35 -0
  98. sqlspec/migrations/base.py +414 -0
  99. sqlspec/migrations/commands.py +443 -0
  100. sqlspec/migrations/loaders.py +402 -0
  101. sqlspec/migrations/runner.py +213 -0
  102. sqlspec/migrations/tracker.py +140 -0
  103. sqlspec/migrations/utils.py +129 -0
  104. sqlspec/protocols.py +51 -186
  105. sqlspec/storage/__init__.py +1 -1
  106. sqlspec/storage/backends/base.py +37 -40
  107. sqlspec/storage/backends/fsspec.py +136 -112
  108. sqlspec/storage/backends/obstore.py +138 -160
  109. sqlspec/storage/capabilities.py +5 -4
  110. sqlspec/storage/registry.py +57 -106
  111. sqlspec/typing.py +136 -115
  112. sqlspec/utils/__init__.py +2 -2
  113. sqlspec/utils/correlation.py +0 -3
  114. sqlspec/utils/deprecation.py +6 -6
  115. sqlspec/utils/fixtures.py +6 -6
  116. sqlspec/utils/logging.py +0 -2
  117. sqlspec/utils/module_loader.py +7 -12
  118. sqlspec/utils/singleton.py +0 -1
  119. sqlspec/utils/sync_tools.py +17 -38
  120. sqlspec/utils/text.py +12 -51
  121. sqlspec/utils/type_guards.py +482 -235
  122. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/METADATA +7 -2
  123. sqlspec-0.16.2.dist-info/RECORD +134 -0
  124. sqlspec-0.16.2.dist-info/entry_points.txt +2 -0
  125. sqlspec/driver/connection.py +0 -207
  126. sqlspec/driver/mixins/_csv_writer.py +0 -91
  127. sqlspec/driver/mixins/_pipeline.py +0 -512
  128. sqlspec/driver/mixins/_result_utils.py +0 -140
  129. sqlspec/driver/mixins/_storage.py +0 -926
  130. sqlspec/driver/mixins/_type_coercion.py +0 -130
  131. sqlspec/driver/parameters.py +0 -138
  132. sqlspec/service/__init__.py +0 -4
  133. sqlspec/service/_util.py +0 -147
  134. sqlspec/service/base.py +0 -1131
  135. sqlspec/service/pagination.py +0 -26
  136. sqlspec/statement/__init__.py +0 -21
  137. sqlspec/statement/builder/insert.py +0 -288
  138. sqlspec/statement/builder/merge.py +0 -95
  139. sqlspec/statement/builder/mixins/__init__.py +0 -65
  140. sqlspec/statement/builder/mixins/_aggregate_functions.py +0 -250
  141. sqlspec/statement/builder/mixins/_case_builder.py +0 -91
  142. sqlspec/statement/builder/mixins/_common_table_expr.py +0 -90
  143. sqlspec/statement/builder/mixins/_from.py +0 -63
  144. sqlspec/statement/builder/mixins/_group_by.py +0 -118
  145. sqlspec/statement/builder/mixins/_having.py +0 -35
  146. sqlspec/statement/builder/mixins/_insert_from_select.py +0 -47
  147. sqlspec/statement/builder/mixins/_insert_into.py +0 -36
  148. sqlspec/statement/builder/mixins/_insert_values.py +0 -67
  149. sqlspec/statement/builder/mixins/_limit_offset.py +0 -53
  150. sqlspec/statement/builder/mixins/_order_by.py +0 -46
  151. sqlspec/statement/builder/mixins/_pivot.py +0 -79
  152. sqlspec/statement/builder/mixins/_returning.py +0 -37
  153. sqlspec/statement/builder/mixins/_select_columns.py +0 -61
  154. sqlspec/statement/builder/mixins/_set_ops.py +0 -122
  155. sqlspec/statement/builder/mixins/_unpivot.py +0 -77
  156. sqlspec/statement/builder/mixins/_update_from.py +0 -55
  157. sqlspec/statement/builder/mixins/_update_set.py +0 -94
  158. sqlspec/statement/builder/mixins/_update_table.py +0 -29
  159. sqlspec/statement/builder/mixins/_where.py +0 -401
  160. sqlspec/statement/builder/mixins/_window_functions.py +0 -86
  161. sqlspec/statement/builder/select.py +0 -221
  162. sqlspec/statement/filters.py +0 -596
  163. sqlspec/statement/parameter_manager.py +0 -220
  164. sqlspec/statement/parameters.py +0 -867
  165. sqlspec/statement/pipelines/__init__.py +0 -210
  166. sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
  167. sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
  168. sqlspec/statement/pipelines/context.py +0 -115
  169. sqlspec/statement/pipelines/transformers/__init__.py +0 -7
  170. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
  171. sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
  172. sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
  173. sqlspec/statement/pipelines/validators/__init__.py +0 -23
  174. sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
  175. sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
  176. sqlspec/statement/pipelines/validators/_performance.py +0 -718
  177. sqlspec/statement/pipelines/validators/_security.py +0 -967
  178. sqlspec/statement/result.py +0 -435
  179. sqlspec/statement/sql.py +0 -1704
  180. sqlspec/statement/sql_compiler.py +0 -140
  181. sqlspec/utils/cached_property.py +0 -25
  182. sqlspec-0.13.1.dist-info/RECORD +0 -150
  183. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/WHEEL +0 -0
  184. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/LICENSE +0 -0
  185. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/NOTICE +0 -0
@@ -1,4 +1,4 @@
1
- # DDL builders for SQLSpec: DROP, CREATE INDEX, TRUNCATE, etc.
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.statement.builder._ddl_utils import build_column_expression, build_constraint_expression
11
- from sqlspec.statement.builder.base import QueryBuilder, SafeQuery
12
- from sqlspec.statement.result import SQLResult
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.statement.builder.column import ColumnExpression
16
- from sqlspec.statement.sql import SQL, SQLConfig
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
- "TruncateTable",
36
+ "Truncate",
37
37
  )
38
38
 
39
39
 
40
40
  @dataclass
41
- class DDLBuilder(QueryBuilder[Any]):
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
- # Override to prevent QueryBuilder from calling _create_base_expression prematurely
49
- pass
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[Any]]":
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[SQLConfig]" = None) -> "SQL":
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 # For computed columns
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 # 'PRIMARY KEY', 'FOREIGN KEY', 'UNIQUE', 'CHECK'
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": # Skip already handled options
371
- if isinstance(value, str):
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 # True: CASCADE, False: RESTRICT, None: not set
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 TruncateTable(DDLBuilder):
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 # "RESTART" or "CONTINUE"
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 # SQL, SelectBuilder, or str
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
- select_params = None
795
- from sqlspec.statement.builder.select import Select
796
- from sqlspec.statement.sql import SQL
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
- select_params = getattr(self._select_query, "parameters", None)
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
- select_params = getattr(self._select_query, "_parameters", None)
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
- select_params = None
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
- # Merge parameters from SELECT if present
823
- if select_params:
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 # SQL, SelectBuilder, or str
853
- _with_data: Optional[bool] = None # True: WITH DATA, False: NO DATA, None: not set
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
- select_params = None
912
- from sqlspec.statement.builder.select import Select
913
- from sqlspec.statement.sql import SQL
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
- select_params = getattr(self._select_query, "parameters", None)
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
- select_params = getattr(self._select_query, "_parameters", None)
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
- select_params = None
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
- # Merge parameters from SELECT if present
930
- if select_params:
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.Literal.string(str(v))))
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 # SQL, SelectBuilder, or str
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
- select_params = None
1011
- from sqlspec.statement.builder.select import Select
1012
- from sqlspec.statement.sql import SQL
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
- select_params = getattr(self._select_query, "parameters", None)
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
- select_params = getattr(self._select_query, "_parameters", None)
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
- select_params = None
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
- # Merge parameters from SELECT if present
1029
- if select_params:
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.Literal.string(h)) for h in self._hints
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.Literal.string(default_val)
1262
+ default_expr = exp.convert(default_val)
1331
1263
  elif isinstance(default_val, (int, float)):
1332
- default_expr = exp.Literal.number(default_val)
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.Literal.string(str(default_val))
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 # This line is unreachable but satisfies the linter
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 # 'TABLE' or 'COLUMN'
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.Literal.string(self._comment),
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 # This line is unreachable but satisfies the linter
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.statement.builder.ddl import ColumnDefinition, ConstraintDefinition
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.Literal.string(col.default)
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.Literal.string(str(col.default))
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.Literal.string(col.comment))))
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))