sqlspec 0.17.1__py3-none-any.whl → 0.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sqlspec might be problematic. Click here for more details.

Files changed (77) hide show
  1. sqlspec/__init__.py +1 -1
  2. sqlspec/_sql.py +54 -159
  3. sqlspec/adapters/adbc/config.py +24 -30
  4. sqlspec/adapters/adbc/driver.py +42 -61
  5. sqlspec/adapters/aiosqlite/config.py +5 -10
  6. sqlspec/adapters/aiosqlite/driver.py +9 -25
  7. sqlspec/adapters/aiosqlite/pool.py +43 -35
  8. sqlspec/adapters/asyncmy/config.py +10 -7
  9. sqlspec/adapters/asyncmy/driver.py +18 -39
  10. sqlspec/adapters/asyncpg/config.py +4 -0
  11. sqlspec/adapters/asyncpg/driver.py +32 -79
  12. sqlspec/adapters/bigquery/config.py +12 -65
  13. sqlspec/adapters/bigquery/driver.py +39 -133
  14. sqlspec/adapters/duckdb/config.py +11 -15
  15. sqlspec/adapters/duckdb/driver.py +61 -85
  16. sqlspec/adapters/duckdb/pool.py +2 -5
  17. sqlspec/adapters/oracledb/_types.py +8 -1
  18. sqlspec/adapters/oracledb/config.py +55 -38
  19. sqlspec/adapters/oracledb/driver.py +35 -92
  20. sqlspec/adapters/oracledb/migrations.py +257 -0
  21. sqlspec/adapters/psqlpy/config.py +13 -9
  22. sqlspec/adapters/psqlpy/driver.py +28 -103
  23. sqlspec/adapters/psycopg/config.py +9 -5
  24. sqlspec/adapters/psycopg/driver.py +107 -175
  25. sqlspec/adapters/sqlite/config.py +7 -5
  26. sqlspec/adapters/sqlite/driver.py +37 -73
  27. sqlspec/adapters/sqlite/pool.py +3 -12
  28. sqlspec/base.py +19 -22
  29. sqlspec/builder/__init__.py +1 -1
  30. sqlspec/builder/_base.py +34 -20
  31. sqlspec/builder/_ddl.py +407 -183
  32. sqlspec/builder/_insert.py +1 -1
  33. sqlspec/builder/mixins/_insert_operations.py +26 -6
  34. sqlspec/builder/mixins/_merge_operations.py +1 -1
  35. sqlspec/builder/mixins/_select_operations.py +1 -5
  36. sqlspec/cli.py +281 -33
  37. sqlspec/config.py +183 -14
  38. sqlspec/core/__init__.py +89 -14
  39. sqlspec/core/cache.py +57 -104
  40. sqlspec/core/compiler.py +57 -112
  41. sqlspec/core/filters.py +1 -21
  42. sqlspec/core/hashing.py +13 -47
  43. sqlspec/core/parameters.py +272 -261
  44. sqlspec/core/result.py +12 -27
  45. sqlspec/core/splitter.py +17 -21
  46. sqlspec/core/statement.py +150 -159
  47. sqlspec/driver/_async.py +2 -15
  48. sqlspec/driver/_common.py +16 -95
  49. sqlspec/driver/_sync.py +2 -15
  50. sqlspec/driver/mixins/_result_tools.py +8 -29
  51. sqlspec/driver/mixins/_sql_translator.py +6 -8
  52. sqlspec/exceptions.py +1 -2
  53. sqlspec/extensions/litestar/plugin.py +15 -8
  54. sqlspec/loader.py +43 -115
  55. sqlspec/migrations/__init__.py +1 -1
  56. sqlspec/migrations/base.py +34 -45
  57. sqlspec/migrations/commands.py +34 -15
  58. sqlspec/migrations/loaders.py +1 -1
  59. sqlspec/migrations/runner.py +104 -19
  60. sqlspec/migrations/tracker.py +49 -2
  61. sqlspec/protocols.py +3 -6
  62. sqlspec/storage/__init__.py +4 -4
  63. sqlspec/storage/backends/fsspec.py +5 -6
  64. sqlspec/storage/backends/obstore.py +7 -8
  65. sqlspec/storage/registry.py +3 -3
  66. sqlspec/utils/__init__.py +2 -2
  67. sqlspec/utils/logging.py +6 -10
  68. sqlspec/utils/sync_tools.py +27 -4
  69. sqlspec/utils/text.py +6 -1
  70. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/METADATA +1 -1
  71. sqlspec-0.19.0.dist-info/RECORD +138 -0
  72. sqlspec/builder/_ddl_utils.py +0 -103
  73. sqlspec-0.17.1.dist-info/RECORD +0 -138
  74. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/WHEEL +0 -0
  75. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/entry_points.txt +0 -0
  76. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/LICENSE +0 -0
  77. {sqlspec-0.17.1.dist-info → sqlspec-0.19.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/builder/_ddl.py CHANGED
@@ -1,6 +1,5 @@
1
1
  """DDL builders for SQLSpec: DROP, CREATE INDEX, TRUNCATE, etc."""
2
2
 
3
- from dataclasses import dataclass, field
4
3
  from typing import TYPE_CHECKING, Any, Optional, Union
5
4
 
6
5
  from sqlglot import exp
@@ -8,7 +7,6 @@ from sqlglot.dialects.dialect import DialectType
8
7
  from typing_extensions import Self
9
8
 
10
9
  from sqlspec.builder._base import QueryBuilder, SafeQuery
11
- from sqlspec.builder._ddl_utils import build_column_expression, build_constraint_expression
12
10
  from sqlspec.core.result import SQLResult
13
11
 
14
12
  if TYPE_CHECKING:
@@ -37,16 +35,109 @@ __all__ = (
37
35
  )
38
36
 
39
37
 
40
- @dataclass
38
+ def build_column_expression(col: "ColumnDefinition") -> "exp.Expression":
39
+ """Build SQLGlot expression for a column definition."""
40
+ col_def = exp.ColumnDef(this=exp.to_identifier(col.name), kind=exp.DataType.build(col.dtype))
41
+
42
+ constraints: list[exp.ColumnConstraint] = []
43
+
44
+ if col.not_null:
45
+ constraints.append(exp.ColumnConstraint(kind=exp.NotNullColumnConstraint()))
46
+
47
+ if col.primary_key:
48
+ constraints.append(exp.ColumnConstraint(kind=exp.PrimaryKeyColumnConstraint()))
49
+
50
+ if col.unique:
51
+ constraints.append(exp.ColumnConstraint(kind=exp.UniqueColumnConstraint()))
52
+
53
+ if col.default is not None:
54
+ default_expr: Optional[exp.Expression] = None
55
+ if isinstance(col.default, str):
56
+ default_upper = col.default.upper()
57
+ if default_upper == "CURRENT_TIMESTAMP":
58
+ default_expr = exp.CurrentTimestamp()
59
+ elif default_upper == "CURRENT_DATE":
60
+ default_expr = exp.CurrentDate()
61
+ elif default_upper == "CURRENT_TIME":
62
+ default_expr = exp.CurrentTime()
63
+ elif "(" in col.default:
64
+ default_expr = exp.maybe_parse(col.default)
65
+ else:
66
+ default_expr = exp.convert(col.default)
67
+ else:
68
+ default_expr = exp.convert(col.default)
69
+
70
+ constraints.append(exp.ColumnConstraint(kind=exp.DefaultColumnConstraint(this=default_expr)))
71
+
72
+ if col.check:
73
+ constraints.append(exp.ColumnConstraint(kind=exp.Check(this=exp.maybe_parse(col.check))))
74
+
75
+ if col.comment:
76
+ constraints.append(exp.ColumnConstraint(kind=exp.CommentColumnConstraint(this=exp.convert(col.comment))))
77
+
78
+ if col.generated:
79
+ constraints.append(
80
+ exp.ColumnConstraint(kind=exp.GeneratedAsIdentityColumnConstraint(this=exp.maybe_parse(col.generated)))
81
+ )
82
+
83
+ if col.collate:
84
+ constraints.append(exp.ColumnConstraint(kind=exp.CollateColumnConstraint(this=exp.to_identifier(col.collate))))
85
+
86
+ if constraints:
87
+ col_def.set("constraints", constraints)
88
+
89
+ return col_def
90
+
91
+
92
+ def build_constraint_expression(constraint: "ConstraintDefinition") -> "Optional[exp.Expression]":
93
+ """Build SQLGlot expression for a table constraint."""
94
+ if constraint.constraint_type == "PRIMARY KEY":
95
+ pk_constraint = exp.PrimaryKey(expressions=[exp.to_identifier(col) for col in constraint.columns])
96
+
97
+ if constraint.name:
98
+ return exp.Constraint(this=exp.to_identifier(constraint.name), expression=pk_constraint)
99
+ return pk_constraint
100
+
101
+ if constraint.constraint_type == "FOREIGN KEY":
102
+ fk_constraint = exp.ForeignKey(
103
+ expressions=[exp.to_identifier(col) for col in constraint.columns],
104
+ reference=exp.Reference(
105
+ this=exp.to_table(constraint.references_table) if constraint.references_table else None,
106
+ expressions=[exp.to_identifier(col) for col in constraint.references_columns],
107
+ on_delete=constraint.on_delete,
108
+ on_update=constraint.on_update,
109
+ ),
110
+ )
111
+
112
+ if constraint.name:
113
+ return exp.Constraint(this=exp.to_identifier(constraint.name), expression=fk_constraint)
114
+ return fk_constraint
115
+
116
+ if constraint.constraint_type == "UNIQUE":
117
+ unique_constraint = exp.UniqueKeyProperty(expressions=[exp.to_identifier(col) for col in constraint.columns])
118
+
119
+ if constraint.name:
120
+ return exp.Constraint(this=exp.to_identifier(constraint.name), expression=unique_constraint)
121
+ return unique_constraint
122
+
123
+ if constraint.constraint_type == "CHECK":
124
+ check_expr = exp.Check(this=exp.maybe_parse(constraint.condition) if constraint.condition else None)
125
+
126
+ if constraint.name:
127
+ return exp.Constraint(this=exp.to_identifier(constraint.name), expression=check_expr)
128
+ return check_expr
129
+
130
+ return None
131
+
132
+
41
133
  class DDLBuilder(QueryBuilder):
42
134
  """Base class for DDL builders (CREATE, DROP, ALTER, etc)."""
43
135
 
44
- dialect: DialectType = None
45
- _expression: Optional[exp.Expression] = field(default=None, init=False, repr=False, compare=False, hash=False)
136
+ __slots__ = ()
46
137
 
47
- def __post_init__(self) -> None:
48
- # Initialize parent class attributes since dataclass doesn't call super().__init__()
49
- super().__init__(dialect=self.dialect)
138
+ def __init__(self, dialect: DialectType = None) -> None:
139
+ super().__init__(dialect=dialect)
140
+ self._expression: Optional[exp.Expression] = None
50
141
 
51
142
  def _create_base_expression(self) -> exp.Expression:
52
143
  msg = "Subclasses must implement _create_base_expression."
@@ -65,40 +156,91 @@ class DDLBuilder(QueryBuilder):
65
156
  return super().to_statement(config=config)
66
157
 
67
158
 
68
- @dataclass
69
159
  class ColumnDefinition:
70
160
  """Column definition for CREATE TABLE."""
71
161
 
72
- name: str
73
- dtype: str
74
- default: "Optional[Any]" = None
75
- not_null: bool = False
76
- primary_key: bool = False
77
- unique: bool = False
78
- auto_increment: bool = False
79
- comment: "Optional[str]" = None
80
- check: "Optional[str]" = None
81
- generated: "Optional[str]" = None
82
- collate: "Optional[str]" = None
162
+ __slots__ = (
163
+ "auto_increment",
164
+ "check",
165
+ "collate",
166
+ "comment",
167
+ "default",
168
+ "dtype",
169
+ "generated",
170
+ "name",
171
+ "not_null",
172
+ "primary_key",
173
+ "unique",
174
+ )
175
+
176
+ def __init__(
177
+ self,
178
+ name: str,
179
+ dtype: str,
180
+ default: "Optional[Any]" = None,
181
+ not_null: bool = False,
182
+ primary_key: bool = False,
183
+ unique: bool = False,
184
+ auto_increment: bool = False,
185
+ comment: "Optional[str]" = None,
186
+ check: "Optional[str]" = None,
187
+ generated: "Optional[str]" = None,
188
+ collate: "Optional[str]" = None,
189
+ ) -> None:
190
+ self.name = name
191
+ self.dtype = dtype
192
+ self.default = default
193
+ self.not_null = not_null
194
+ self.primary_key = primary_key
195
+ self.unique = unique
196
+ self.auto_increment = auto_increment
197
+ self.comment = comment
198
+ self.check = check
199
+ self.generated = generated
200
+ self.collate = collate
83
201
 
84
202
 
85
- @dataclass
86
203
  class ConstraintDefinition:
87
204
  """Constraint definition for CREATE TABLE."""
88
205
 
89
- constraint_type: str
90
- name: "Optional[str]" = None
91
- columns: "list[str]" = field(default_factory=list)
92
- references_table: "Optional[str]" = None
93
- references_columns: "list[str]" = field(default_factory=list)
94
- condition: "Optional[str]" = None
95
- on_delete: "Optional[str]" = None
96
- on_update: "Optional[str]" = None
97
- deferrable: bool = False
98
- initially_deferred: bool = False
206
+ __slots__ = (
207
+ "columns",
208
+ "condition",
209
+ "constraint_type",
210
+ "deferrable",
211
+ "initially_deferred",
212
+ "name",
213
+ "on_delete",
214
+ "on_update",
215
+ "references_columns",
216
+ "references_table",
217
+ )
218
+
219
+ def __init__(
220
+ self,
221
+ constraint_type: str,
222
+ name: "Optional[str]" = None,
223
+ columns: "Optional[list[str]]" = None,
224
+ references_table: "Optional[str]" = None,
225
+ references_columns: "Optional[list[str]]" = None,
226
+ condition: "Optional[str]" = None,
227
+ on_delete: "Optional[str]" = None,
228
+ on_update: "Optional[str]" = None,
229
+ deferrable: bool = False,
230
+ initially_deferred: bool = False,
231
+ ) -> None:
232
+ self.constraint_type = constraint_type
233
+ self.name = name
234
+ self.columns = columns or []
235
+ self.references_table = references_table
236
+ self.references_columns = references_columns or []
237
+ self.condition = condition
238
+ self.on_delete = on_delete
239
+ self.on_update = on_update
240
+ self.deferrable = deferrable
241
+ self.initially_deferred = initially_deferred
99
242
 
100
243
 
101
- @dataclass
102
244
  class CreateTable(DDLBuilder):
103
245
  """Builder for CREATE TABLE statements with columns and constraints.
104
246
 
@@ -113,20 +255,31 @@ class CreateTable(DDLBuilder):
113
255
  sql = builder.build().sql
114
256
  """
115
257
 
116
- _table_name: str = field(default="", init=False)
117
- _if_not_exists: bool = False
118
- _temporary: bool = False
119
- _columns: "list[ColumnDefinition]" = field(default_factory=list)
120
- _constraints: "list[ConstraintDefinition]" = field(default_factory=list)
121
- _table_options: "dict[str, Any]" = field(default_factory=dict)
122
- _schema: "Optional[str]" = None
123
- _tablespace: "Optional[str]" = None
124
- _like_table: "Optional[str]" = None
125
- _partition_by: "Optional[str]" = None
126
-
127
- def __init__(self, table_name: str) -> None:
128
- super().__init__()
258
+ __slots__ = (
259
+ "_columns",
260
+ "_constraints",
261
+ "_if_not_exists",
262
+ "_like_table",
263
+ "_partition_by",
264
+ "_schema",
265
+ "_table_name",
266
+ "_table_options",
267
+ "_tablespace",
268
+ "_temporary",
269
+ )
270
+
271
+ def __init__(self, table_name: str, dialect: DialectType = None) -> None:
272
+ super().__init__(dialect=dialect)
129
273
  self._table_name = table_name
274
+ self._if_not_exists = False
275
+ self._temporary = False
276
+ self._columns: list[ColumnDefinition] = []
277
+ self._constraints: list[ConstraintDefinition] = []
278
+ self._table_options: dict[str, Any] = {}
279
+ self._schema: Optional[str] = None
280
+ self._tablespace: Optional[str] = None
281
+ self._like_table: Optional[str] = None
282
+ self._partition_by: Optional[str] = None
130
283
 
131
284
  def in_schema(self, schema_name: str) -> "Self":
132
285
  """Set the schema for the table."""
@@ -320,11 +473,6 @@ class CreateTable(DDLBuilder):
320
473
  if not self._columns and not self._like_table:
321
474
  self._raise_sql_builder_error("Table must have at least one column or use LIKE clause")
322
475
 
323
- if self._schema:
324
- table = exp.Table(this=exp.to_identifier(self._table_name), db=exp.to_identifier(self._schema))
325
- else:
326
- table = exp.to_table(self._table_name)
327
-
328
476
  column_defs: list[exp.Expression] = []
329
477
  for col in self._columns:
330
478
  col_expr = build_column_expression(col)
@@ -358,7 +506,12 @@ class CreateTable(DDLBuilder):
358
506
 
359
507
  properties_node = exp.Properties(expressions=props) if props else None
360
508
 
361
- schema_expr = exp.Schema(expressions=column_defs) if column_defs else None
509
+ if self._schema:
510
+ table_identifier = exp.Table(this=exp.to_identifier(self._table_name), db=exp.to_identifier(self._schema))
511
+ else:
512
+ table_identifier = exp.Table(this=exp.to_identifier(self._table_name))
513
+
514
+ schema_expr = exp.Schema(this=table_identifier, expressions=column_defs)
362
515
 
363
516
  like_expr = None
364
517
  if self._like_table:
@@ -366,42 +519,30 @@ class CreateTable(DDLBuilder):
366
519
 
367
520
  return exp.Create(
368
521
  kind="TABLE",
369
- this=table,
522
+ this=schema_expr,
370
523
  exists=self._if_not_exists,
371
524
  temporary=self._temporary,
372
- expression=schema_expr,
373
525
  properties=properties_node,
374
526
  like=like_expr,
375
527
  )
376
528
 
377
- @staticmethod
378
- def _build_column_expression(col: "ColumnDefinition") -> "exp.Expression":
379
- """Build SQLGlot expression for a column definition."""
380
- return build_column_expression(col)
381
-
382
- @staticmethod
383
- def _build_constraint_expression(constraint: "ConstraintDefinition") -> "Optional[exp.Expression]":
384
- """Build SQLGlot expression for a table constraint."""
385
- return build_constraint_expression(constraint)
386
-
387
529
 
388
- @dataclass
389
530
  class DropTable(DDLBuilder):
390
531
  """Builder for DROP TABLE [IF EXISTS] ... [CASCADE|RESTRICT]."""
391
532
 
392
- _table_name: Optional[str] = None
393
- _if_exists: bool = False
394
- _cascade: Optional[bool] = None
533
+ __slots__ = ("_cascade", "_if_exists", "_table_name")
395
534
 
396
- def __init__(self, table_name: str, **kwargs: Any) -> None:
535
+ def __init__(self, table_name: str, dialect: DialectType = None) -> None:
397
536
  """Initialize DROP TABLE with table name.
398
537
 
399
538
  Args:
400
539
  table_name: Name of the table to drop
401
- **kwargs: Additional DDLBuilder arguments
540
+ dialect: SQL dialect to use
402
541
  """
403
- super().__init__(**kwargs)
542
+ super().__init__(dialect=dialect)
404
543
  self._table_name = table_name
544
+ self._if_exists = False
545
+ self._cascade: Optional[bool] = None
405
546
 
406
547
  def table(self, name: str) -> Self:
407
548
  self._table_name = name
@@ -427,24 +568,23 @@ class DropTable(DDLBuilder):
427
568
  )
428
569
 
429
570
 
430
- @dataclass
431
571
  class DropIndex(DDLBuilder):
432
572
  """Builder for DROP INDEX [IF EXISTS] ... [ON table] [CASCADE|RESTRICT]."""
433
573
 
434
- _index_name: Optional[str] = None
435
- _table_name: Optional[str] = None
436
- _if_exists: bool = False
437
- _cascade: Optional[bool] = None
574
+ __slots__ = ("_cascade", "_if_exists", "_index_name", "_table_name")
438
575
 
439
- def __init__(self, index_name: str, **kwargs: Any) -> None:
576
+ def __init__(self, index_name: str, dialect: DialectType = None) -> None:
440
577
  """Initialize DROP INDEX with index name.
441
578
 
442
579
  Args:
443
580
  index_name: Name of the index to drop
444
- **kwargs: Additional DDLBuilder arguments
581
+ dialect: SQL dialect to use
445
582
  """
446
- super().__init__(**kwargs)
583
+ super().__init__(dialect=dialect)
447
584
  self._index_name = index_name
585
+ self._table_name: Optional[str] = None
586
+ self._if_exists = False
587
+ self._cascade: Optional[bool] = None
448
588
 
449
589
  def name(self, index_name: str) -> Self:
450
590
  self._index_name = index_name
@@ -478,13 +618,22 @@ class DropIndex(DDLBuilder):
478
618
  )
479
619
 
480
620
 
481
- @dataclass
482
621
  class DropView(DDLBuilder):
483
622
  """Builder for DROP VIEW [IF EXISTS] ... [CASCADE|RESTRICT]."""
484
623
 
485
- _view_name: Optional[str] = None
486
- _if_exists: bool = False
487
- _cascade: Optional[bool] = None
624
+ __slots__ = ("_cascade", "_if_exists", "_view_name")
625
+
626
+ def __init__(self, view_name: str, dialect: DialectType = None) -> None:
627
+ """Initialize DROP VIEW with view name.
628
+
629
+ Args:
630
+ view_name: Name of the view to drop
631
+ dialect: SQL dialect to use
632
+ """
633
+ super().__init__(dialect=dialect)
634
+ self._view_name = view_name
635
+ self._if_exists = False
636
+ self._cascade: Optional[bool] = None
488
637
 
489
638
  def name(self, view_name: str) -> Self:
490
639
  self._view_name = view_name
@@ -510,13 +659,22 @@ class DropView(DDLBuilder):
510
659
  )
511
660
 
512
661
 
513
- @dataclass
514
662
  class DropSchema(DDLBuilder):
515
663
  """Builder for DROP SCHEMA [IF EXISTS] ... [CASCADE|RESTRICT]."""
516
664
 
517
- _schema_name: Optional[str] = None
518
- _if_exists: bool = False
519
- _cascade: Optional[bool] = None
665
+ __slots__ = ("_cascade", "_if_exists", "_schema_name")
666
+
667
+ def __init__(self, schema_name: str, dialect: DialectType = None) -> None:
668
+ """Initialize DROP SCHEMA with schema name.
669
+
670
+ Args:
671
+ schema_name: Name of the schema to drop
672
+ dialect: SQL dialect to use
673
+ """
674
+ super().__init__(dialect=dialect)
675
+ self._schema_name = schema_name
676
+ self._if_exists = False
677
+ self._cascade: Optional[bool] = None
520
678
 
521
679
  def name(self, schema_name: str) -> Self:
522
680
  self._schema_name = schema_name
@@ -542,32 +700,26 @@ class DropSchema(DDLBuilder):
542
700
  )
543
701
 
544
702
 
545
- @dataclass
546
703
  class CreateIndex(DDLBuilder):
547
- """Builder for CREATE [UNIQUE] INDEX [IF NOT EXISTS] ... ON ... (...).
704
+ """Builder for CREATE [UNIQUE] INDEX [IF NOT EXISTS] ... ON ... (...)."""
548
705
 
549
- Supports columns, expressions, ordering, using, and where.
550
- """
551
-
552
- _index_name: Optional[str] = None
553
- _table_name: Optional[str] = None
554
- _columns: list[Union[str, exp.Ordered, exp.Expression]] = field(default_factory=list)
555
- _unique: bool = False
556
- _if_not_exists: bool = False
557
- _using: Optional[str] = None
558
- _where: Optional[Union[str, exp.Expression]] = None
706
+ __slots__ = ("_columns", "_if_not_exists", "_index_name", "_table_name", "_unique", "_using", "_where")
559
707
 
560
- def __init__(self, index_name: str, **kwargs: Any) -> None:
708
+ def __init__(self, index_name: str, dialect: DialectType = None) -> None:
561
709
  """Initialize CREATE INDEX with index name.
562
710
 
563
711
  Args:
564
712
  index_name: Name of the index to create
565
- **kwargs: Additional DDLBuilder arguments
713
+ dialect: SQL dialect to use
566
714
  """
567
- super().__init__(**kwargs)
715
+ super().__init__(dialect=dialect)
568
716
  self._index_name = index_name
569
- if not hasattr(self, "_columns"):
570
- self._columns = []
717
+ self._table_name: Optional[str] = None
718
+ self._columns: list[Union[str, exp.Ordered, exp.Expression]] = []
719
+ self._unique = False
720
+ self._if_not_exists = False
721
+ self._using: Optional[str] = None
722
+ self._where: Optional[Union[str, exp.Expression]] = None
571
723
 
572
724
  def name(self, index_name: str) -> Self:
573
725
  self._index_name = index_name
@@ -625,13 +777,22 @@ class CreateIndex(DDLBuilder):
625
777
  )
626
778
 
627
779
 
628
- @dataclass
629
780
  class Truncate(DDLBuilder):
630
781
  """Builder for TRUNCATE TABLE ... [CASCADE|RESTRICT] [RESTART IDENTITY|CONTINUE IDENTITY]."""
631
782
 
632
- _table_name: Optional[str] = None
633
- _cascade: Optional[bool] = None
634
- _identity: Optional[str] = None
783
+ __slots__ = ("_cascade", "_identity", "_table_name")
784
+
785
+ def __init__(self, table_name: str, dialect: DialectType = None) -> None:
786
+ """Initialize TRUNCATE with table name.
787
+
788
+ Args:
789
+ table_name: Name of the table to truncate
790
+ dialect: SQL dialect to use
791
+ """
792
+ super().__init__(dialect=dialect)
793
+ self._table_name = table_name
794
+ self._cascade: Optional[bool] = None
795
+ self._identity: Optional[str] = None
635
796
 
636
797
  def table(self, name: str) -> Self:
637
798
  self._table_name = name
@@ -660,29 +821,63 @@ class Truncate(DDLBuilder):
660
821
  return exp.TruncateTable(this=exp.to_table(self._table_name), cascade=self._cascade, identity=identity_expr)
661
822
 
662
823
 
663
- @dataclass
664
824
  class AlterOperation:
665
825
  """Represents a single ALTER TABLE operation."""
666
826
 
667
- operation_type: str
668
- column_name: "Optional[str]" = None
669
- column_definition: "Optional[ColumnDefinition]" = None
670
- constraint_name: "Optional[str]" = None
671
- constraint_definition: "Optional[ConstraintDefinition]" = None
672
- new_type: "Optional[str]" = None
673
- new_name: "Optional[str]" = None
674
- after_column: "Optional[str]" = None
675
- first: bool = False
676
- using_expression: "Optional[str]" = None
827
+ __slots__ = (
828
+ "after_column",
829
+ "column_definition",
830
+ "column_name",
831
+ "constraint_definition",
832
+ "constraint_name",
833
+ "first",
834
+ "new_name",
835
+ "new_type",
836
+ "operation_type",
837
+ "using_expression",
838
+ )
839
+
840
+ def __init__(
841
+ self,
842
+ operation_type: str,
843
+ column_name: "Optional[str]" = None,
844
+ column_definition: "Optional[ColumnDefinition]" = None,
845
+ constraint_name: "Optional[str]" = None,
846
+ constraint_definition: "Optional[ConstraintDefinition]" = None,
847
+ new_type: "Optional[str]" = None,
848
+ new_name: "Optional[str]" = None,
849
+ after_column: "Optional[str]" = None,
850
+ first: bool = False,
851
+ using_expression: "Optional[str]" = None,
852
+ ) -> None:
853
+ self.operation_type = operation_type
854
+ self.column_name = column_name
855
+ self.column_definition = column_definition
856
+ self.constraint_name = constraint_name
857
+ self.constraint_definition = constraint_definition
858
+ self.new_type = new_type
859
+ self.new_name = new_name
860
+ self.after_column = after_column
861
+ self.first = first
862
+ self.using_expression = using_expression
677
863
 
678
864
 
679
- @dataclass
680
865
  class CreateSchema(DDLBuilder):
681
866
  """Builder for CREATE SCHEMA [IF NOT EXISTS] schema_name [AUTHORIZATION user_name]."""
682
867
 
683
- _schema_name: Optional[str] = None
684
- _if_not_exists: bool = False
685
- _authorization: Optional[str] = None
868
+ __slots__ = ("_authorization", "_if_not_exists", "_schema_name")
869
+
870
+ def __init__(self, schema_name: str, dialect: DialectType = None) -> None:
871
+ """Initialize CREATE SCHEMA with schema name.
872
+
873
+ Args:
874
+ schema_name: Name of the schema to create
875
+ dialect: SQL dialect to use
876
+ """
877
+ super().__init__(dialect=dialect)
878
+ self._schema_name = schema_name
879
+ self._if_not_exists = False
880
+ self._authorization: Optional[str] = None
686
881
 
687
882
  def name(self, schema_name: str) -> Self:
688
883
  self._schema_name = schema_name
@@ -713,12 +908,9 @@ class CreateSchema(DDLBuilder):
713
908
  )
714
909
 
715
910
 
716
- @dataclass
717
911
  class CreateTableAsSelect(DDLBuilder):
718
912
  """Builder for CREATE TABLE [IF NOT EXISTS] ... AS SELECT ... (CTAS).
719
913
 
720
- Supports optional column list and parameterized SELECT sources.
721
-
722
914
  Example:
723
915
  builder = (
724
916
  CreateTableAsSelectBuilder()
@@ -736,10 +928,14 @@ class CreateTableAsSelect(DDLBuilder):
736
928
  - as_select(select_query): Set the SELECT source (SQL, SelectBuilder, or str).
737
929
  """
738
930
 
739
- _table_name: Optional[str] = None
740
- _if_not_exists: bool = False
741
- _columns: list[str] = field(default_factory=list)
742
- _select_query: Optional[object] = None
931
+ __slots__ = ("_columns", "_if_not_exists", "_select_query", "_table_name")
932
+
933
+ def __init__(self, dialect: DialectType = None) -> None:
934
+ super().__init__(dialect=dialect)
935
+ self._table_name: Optional[str] = None
936
+ self._if_not_exists = False
937
+ self._columns: list[str] = []
938
+ self._select_query: Optional[object] = None
743
939
 
744
940
  def name(self, table_name: str) -> Self:
745
941
  self._table_name = table_name
@@ -753,7 +949,7 @@ class CreateTableAsSelect(DDLBuilder):
753
949
  self._columns = list(cols)
754
950
  return self
755
951
 
756
- def as_select(self, select_query: object) -> Self:
952
+ def as_select(self, select_query: "Union[str, exp.Expression]") -> Self:
757
953
  self._select_query = select_query
758
954
  return self
759
955
 
@@ -805,23 +1001,40 @@ class CreateTableAsSelect(DDLBuilder):
805
1001
  )
806
1002
 
807
1003
 
808
- @dataclass
809
1004
  class CreateMaterializedView(DDLBuilder):
810
- """Builder for CREATE MATERIALIZED VIEW [IF NOT EXISTS] ... AS SELECT ...
1005
+ """Builder for CREATE MATERIALIZED VIEW [IF NOT EXISTS] ... AS SELECT ..."""
1006
+
1007
+ __slots__ = (
1008
+ "_columns",
1009
+ "_hints",
1010
+ "_if_not_exists",
1011
+ "_refresh_mode",
1012
+ "_select_query",
1013
+ "_storage_parameters",
1014
+ "_tablespace",
1015
+ "_using_index",
1016
+ "_view_name",
1017
+ "_with_data",
1018
+ )
1019
+
1020
+ def __init__(self, view_name: str, dialect: DialectType = None) -> None:
1021
+ """Initialize CREATE MATERIALIZED VIEW with view name.
811
1022
 
812
- Supports optional column list, parameterized SELECT sources, and dialect-specific options.
813
- """
814
-
815
- _view_name: Optional[str] = None
816
- _if_not_exists: bool = False
817
- _columns: list[str] = field(default_factory=list)
818
- _select_query: Optional[object] = None
819
- _with_data: Optional[bool] = None
820
- _refresh_mode: Optional[str] = None
821
- _storage_parameters: dict[str, Any] = field(default_factory=dict)
822
- _tablespace: Optional[str] = None
823
- _using_index: Optional[str] = None
824
- _hints: list[str] = field(default_factory=list)
1023
+ Args:
1024
+ view_name: Name of the materialized view to create
1025
+ dialect: SQL dialect to use
1026
+ """
1027
+ super().__init__(dialect=dialect)
1028
+ self._view_name = view_name
1029
+ self._if_not_exists = False
1030
+ self._columns: list[str] = []
1031
+ self._select_query: Optional[Union[str, exp.Expression]] = None
1032
+ self._with_data: Optional[bool] = None
1033
+ self._refresh_mode: Optional[str] = None
1034
+ self._storage_parameters: dict[str, Any] = {}
1035
+ self._tablespace: Optional[str] = None
1036
+ self._using_index: Optional[str] = None
1037
+ self._hints: list[str] = []
825
1038
 
826
1039
  def name(self, view_name: str) -> Self:
827
1040
  self._view_name = view_name
@@ -835,7 +1048,7 @@ class CreateMaterializedView(DDLBuilder):
835
1048
  self._columns = list(cols)
836
1049
  return self
837
1050
 
838
- def as_select(self, select_query: object) -> Self:
1051
+ def as_select(self, select_query: "Union[str, exp.Expression]") -> Self:
839
1052
  self._select_query = select_query
840
1053
  return self
841
1054
 
@@ -926,18 +1139,24 @@ class CreateMaterializedView(DDLBuilder):
926
1139
  )
927
1140
 
928
1141
 
929
- @dataclass
930
1142
  class CreateView(DDLBuilder):
931
- """Builder for CREATE VIEW [IF NOT EXISTS] ... AS SELECT ...
1143
+ """Builder for CREATE VIEW [IF NOT EXISTS] ... AS SELECT ..."""
932
1144
 
933
- Supports optional column list, parameterized SELECT sources, and hints.
934
- """
1145
+ __slots__ = ("_columns", "_hints", "_if_not_exists", "_select_query", "_view_name")
935
1146
 
936
- _view_name: Optional[str] = None
937
- _if_not_exists: bool = False
938
- _columns: list[str] = field(default_factory=list)
939
- _select_query: Optional[object] = None
940
- _hints: list[str] = field(default_factory=list)
1147
+ def __init__(self, view_name: str, dialect: DialectType = None) -> None:
1148
+ """Initialize CREATE VIEW with view name.
1149
+
1150
+ Args:
1151
+ view_name: Name of the view to create
1152
+ dialect: SQL dialect to use
1153
+ """
1154
+ super().__init__(dialect=dialect)
1155
+ self._view_name = view_name
1156
+ self._if_not_exists = False
1157
+ self._columns: list[str] = []
1158
+ self._select_query: Optional[Union[str, exp.Expression]] = None
1159
+ self._hints: list[str] = []
941
1160
 
942
1161
  def name(self, view_name: str) -> Self:
943
1162
  self._view_name = view_name
@@ -951,7 +1170,7 @@ class CreateView(DDLBuilder):
951
1170
  self._columns = list(cols)
952
1171
  return self
953
1172
 
954
- def as_select(self, select_query: object) -> Self:
1173
+ def as_select(self, select_query: "Union[str, exp.Expression]") -> Self:
955
1174
  self._select_query = select_query
956
1175
  return self
957
1176
 
@@ -1007,31 +1226,25 @@ class CreateView(DDLBuilder):
1007
1226
  )
1008
1227
 
1009
1228
 
1010
- @dataclass
1011
1229
  class AlterTable(DDLBuilder):
1012
- """Builder for ALTER TABLE with granular operations.
1013
-
1014
- Supports column operations (add, drop, alter type, rename) and constraint operations.
1230
+ """Builder for ALTER TABLE operations.
1015
1231
 
1016
1232
  Example:
1017
1233
  builder = (
1018
- AlterTableBuilder("users")
1234
+ AlterTable("users")
1019
1235
  .add_column("email", "VARCHAR(255)", not_null=True)
1020
1236
  .drop_column("old_field")
1021
1237
  .add_constraint("check_age", "CHECK (age >= 18)")
1022
1238
  )
1023
1239
  """
1024
1240
 
1025
- _table_name: str = field(default="", init=False)
1026
- _operations: "list[AlterOperation]" = field(default_factory=list)
1027
- _schema: "Optional[str]" = None
1028
- _if_exists: bool = False
1241
+ __slots__ = ("_if_exists", "_operations", "_schema", "_table_name")
1029
1242
 
1030
- def __init__(self, table_name: str) -> None:
1031
- super().__init__()
1243
+ def __init__(self, table_name: str, dialect: DialectType = None) -> None:
1244
+ super().__init__(dialect=dialect)
1032
1245
  self._table_name = table_name
1033
- self._operations = []
1034
- self._schema = None
1246
+ self._operations: list[AlterOperation] = []
1247
+ self._schema: Optional[str] = None
1035
1248
  self._if_exists = False
1036
1249
 
1037
1250
  def if_exists(self) -> "Self":
@@ -1277,17 +1490,22 @@ class AlterTable(DDLBuilder):
1277
1490
  raise AssertionError
1278
1491
 
1279
1492
 
1280
- @dataclass
1281
1493
  class CommentOn(DDLBuilder):
1282
- """Builder for COMMENT ON ... IS ... statements.
1494
+ """Builder for COMMENT ON ... IS ... statements."""
1283
1495
 
1284
- Supports COMMENT ON TABLE and COMMENT ON COLUMN.
1285
- """
1496
+ __slots__ = ("_column", "_comment", "_table", "_target_type")
1286
1497
 
1287
- _target_type: Optional[str] = None
1288
- _table: Optional[str] = None
1289
- _column: Optional[str] = None
1290
- _comment: Optional[str] = None
1498
+ def __init__(self, dialect: DialectType = None) -> None:
1499
+ """Initialize COMMENT ON builder.
1500
+
1501
+ Args:
1502
+ dialect: SQL dialect to use
1503
+ """
1504
+ super().__init__(dialect=dialect)
1505
+ self._target_type: Optional[str] = None
1506
+ self._table: Optional[str] = None
1507
+ self._column: Optional[str] = None
1508
+ self._comment: Optional[str] = None
1291
1509
 
1292
1510
  def on_table(self, table: str) -> Self:
1293
1511
  self._target_type = "TABLE"
@@ -1318,15 +1536,21 @@ class CommentOn(DDLBuilder):
1318
1536
  raise AssertionError
1319
1537
 
1320
1538
 
1321
- @dataclass
1322
1539
  class RenameTable(DDLBuilder):
1323
- """Builder for ALTER TABLE ... RENAME TO ... statements.
1540
+ """Builder for ALTER TABLE ... RENAME TO ... statements."""
1324
1541
 
1325
- Supports renaming a table.
1326
- """
1542
+ __slots__ = ("_new_name", "_old_name")
1543
+
1544
+ def __init__(self, old_name: str, dialect: DialectType = None) -> None:
1545
+ """Initialize RENAME TABLE with old name.
1327
1546
 
1328
- _old_name: Optional[str] = None
1329
- _new_name: Optional[str] = None
1547
+ Args:
1548
+ old_name: Current name of the table
1549
+ dialect: SQL dialect to use
1550
+ """
1551
+ super().__init__(dialect=dialect)
1552
+ self._old_name = old_name
1553
+ self._new_name: Optional[str] = None
1330
1554
 
1331
1555
  def table(self, old_name: str) -> Self:
1332
1556
  self._old_name = old_name