sqlspec 0.17.1__py3-none-any.whl → 0.18.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 (75) 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 +1 -8
  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/config.py +32 -13
  37. sqlspec/core/__init__.py +89 -14
  38. sqlspec/core/cache.py +57 -104
  39. sqlspec/core/compiler.py +57 -112
  40. sqlspec/core/filters.py +1 -21
  41. sqlspec/core/hashing.py +13 -47
  42. sqlspec/core/parameters.py +272 -261
  43. sqlspec/core/result.py +12 -27
  44. sqlspec/core/splitter.py +17 -21
  45. sqlspec/core/statement.py +150 -159
  46. sqlspec/driver/_async.py +2 -15
  47. sqlspec/driver/_common.py +16 -95
  48. sqlspec/driver/_sync.py +2 -15
  49. sqlspec/driver/mixins/_result_tools.py +8 -29
  50. sqlspec/driver/mixins/_sql_translator.py +6 -8
  51. sqlspec/exceptions.py +1 -2
  52. sqlspec/loader.py +43 -115
  53. sqlspec/migrations/__init__.py +1 -1
  54. sqlspec/migrations/base.py +34 -45
  55. sqlspec/migrations/commands.py +34 -15
  56. sqlspec/migrations/loaders.py +1 -1
  57. sqlspec/migrations/runner.py +104 -19
  58. sqlspec/migrations/tracker.py +49 -2
  59. sqlspec/protocols.py +3 -6
  60. sqlspec/storage/__init__.py +4 -4
  61. sqlspec/storage/backends/fsspec.py +5 -6
  62. sqlspec/storage/backends/obstore.py +7 -8
  63. sqlspec/storage/registry.py +3 -3
  64. sqlspec/utils/__init__.py +2 -2
  65. sqlspec/utils/logging.py +6 -10
  66. sqlspec/utils/sync_tools.py +27 -4
  67. sqlspec/utils/text.py +6 -1
  68. {sqlspec-0.17.1.dist-info → sqlspec-0.18.0.dist-info}/METADATA +1 -1
  69. sqlspec-0.18.0.dist-info/RECORD +138 -0
  70. sqlspec/builder/_ddl_utils.py +0 -103
  71. sqlspec-0.17.1.dist-info/RECORD +0 -138
  72. {sqlspec-0.17.1.dist-info → sqlspec-0.18.0.dist-info}/WHEEL +0 -0
  73. {sqlspec-0.17.1.dist-info → sqlspec-0.18.0.dist-info}/entry_points.txt +0 -0
  74. {sqlspec-0.17.1.dist-info → sqlspec-0.18.0.dist-info}/licenses/LICENSE +0 -0
  75. {sqlspec-0.17.1.dist-info → sqlspec-0.18.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- """SQLSpec: Safe and elegant SQL query building for Python."""
1
+ """SQLSpec: Type-safe SQL query mapper for Python."""
2
2
 
3
3
  from sqlspec import adapters, base, builder, core, driver, exceptions, extensions, loader, migrations, typing, utils
4
4
  from sqlspec.__metadata__ import __version__
sqlspec/_sql.py CHANGED
@@ -1,6 +1,6 @@
1
- """Unified SQL factory for creating SQL builders and column expressions with a clean API.
1
+ """SQL factory for creating SQL builders and column expressions.
2
2
 
3
- Provides both statement builders (select, insert, update, etc.) and column expressions.
3
+ Provides statement builders (select, insert, update, etc.) and column expressions.
4
4
  """
5
5
 
6
6
  import logging
@@ -42,11 +42,11 @@ from sqlspec.builder._expression_wrappers import (
42
42
  )
43
43
  from sqlspec.builder.mixins._join_operations import JoinBuilder
44
44
  from sqlspec.builder.mixins._select_operations import Case, SubqueryBuilder, WindowFunctionBuilder
45
+ from sqlspec.core.statement import SQL
45
46
  from sqlspec.exceptions import SQLBuilderError
46
47
 
47
48
  if TYPE_CHECKING:
48
49
  from sqlspec.builder._expression_wrappers import ExpressionWrapper
49
- from sqlspec.core.statement import SQL
50
50
 
51
51
 
52
52
  __all__ = (
@@ -109,7 +109,7 @@ SQL_STARTERS = {
109
109
 
110
110
 
111
111
  class SQLFactory:
112
- """Unified factory for creating SQL builders and column expressions with a fluent API."""
112
+ """Factory for creating SQL builders and column expressions."""
113
113
 
114
114
  @classmethod
115
115
  def detect_sql_type(cls, sql: str, dialect: DialectType = None) -> str:
@@ -136,9 +136,6 @@ class SQLFactory:
136
136
  """
137
137
  self.dialect = dialect
138
138
 
139
- # ===================
140
- # Callable Interface
141
- # ===================
142
139
  def __call__(self, statement: str, dialect: DialectType = None) -> "Any":
143
140
  """Create a SelectBuilder from a SQL string, only allowing SELECT/CTE queries.
144
141
 
@@ -184,9 +181,6 @@ class SQLFactory:
184
181
  )
185
182
  raise SQLBuilderError(msg)
186
183
 
187
- # ===================
188
- # Statement Builders
189
- # ===================
190
184
  def select(
191
185
  self, *columns_or_sql: Union[str, exp.Expression, Column, "SQL", "Case"], dialect: DialectType = None
192
186
  ) -> "Select":
@@ -271,10 +265,6 @@ class SQLFactory:
271
265
  return builder.into(table_or_sql)
272
266
  return builder
273
267
 
274
- # ===================
275
- # DDL Statement Builders
276
- # ===================
277
-
278
268
  def create_table(self, table_name: str, dialect: DialectType = None) -> "CreateTable":
279
269
  """Create a CREATE TABLE builder.
280
270
 
@@ -285,9 +275,7 @@ class SQLFactory:
285
275
  Returns:
286
276
  CreateTable builder instance
287
277
  """
288
- builder = CreateTable(table_name)
289
- builder.dialect = dialect or self.dialect
290
- return builder
278
+ return CreateTable(table_name, dialect=dialect or self.dialect)
291
279
 
292
280
  def create_table_as_select(self, dialect: DialectType = None) -> "CreateTableAsSelect":
293
281
  """Create a CREATE TABLE AS SELECT builder.
@@ -298,35 +286,31 @@ class SQLFactory:
298
286
  Returns:
299
287
  CreateTableAsSelect builder instance
300
288
  """
301
- builder = CreateTableAsSelect()
302
- builder.dialect = dialect or self.dialect
303
- return builder
289
+ return CreateTableAsSelect(dialect=dialect or self.dialect)
304
290
 
305
- def create_view(self, dialect: DialectType = None) -> "CreateView":
291
+ def create_view(self, view_name: str, dialect: DialectType = None) -> "CreateView":
306
292
  """Create a CREATE VIEW builder.
307
293
 
308
294
  Args:
295
+ view_name: Name of the view to create
309
296
  dialect: Optional SQL dialect
310
297
 
311
298
  Returns:
312
299
  CreateView builder instance
313
300
  """
314
- builder = CreateView()
315
- builder.dialect = dialect or self.dialect
316
- return builder
301
+ return CreateView(view_name, dialect=dialect or self.dialect)
317
302
 
318
- def create_materialized_view(self, dialect: DialectType = None) -> "CreateMaterializedView":
303
+ def create_materialized_view(self, view_name: str, dialect: DialectType = None) -> "CreateMaterializedView":
319
304
  """Create a CREATE MATERIALIZED VIEW builder.
320
305
 
321
306
  Args:
307
+ view_name: Name of the materialized view to create
322
308
  dialect: Optional SQL dialect
323
309
 
324
310
  Returns:
325
311
  CreateMaterializedView builder instance
326
312
  """
327
- builder = CreateMaterializedView()
328
- builder.dialect = dialect or self.dialect
329
- return builder
313
+ return CreateMaterializedView(view_name, dialect=dialect or self.dialect)
330
314
 
331
315
  def create_index(self, index_name: str, dialect: DialectType = None) -> "CreateIndex":
332
316
  """Create a CREATE INDEX builder.
@@ -340,18 +324,17 @@ class SQLFactory:
340
324
  """
341
325
  return CreateIndex(index_name, dialect=dialect or self.dialect)
342
326
 
343
- def create_schema(self, dialect: DialectType = None) -> "CreateSchema":
327
+ def create_schema(self, schema_name: str, dialect: DialectType = None) -> "CreateSchema":
344
328
  """Create a CREATE SCHEMA builder.
345
329
 
346
330
  Args:
331
+ schema_name: Name of the schema to create
347
332
  dialect: Optional SQL dialect
348
333
 
349
334
  Returns:
350
335
  CreateSchema builder instance
351
336
  """
352
- builder = CreateSchema()
353
- builder.dialect = dialect or self.dialect
354
- return builder
337
+ return CreateSchema(schema_name, dialect=dialect or self.dialect)
355
338
 
356
339
  def drop_table(self, table_name: str, dialect: DialectType = None) -> "DropTable":
357
340
  """Create a DROP TABLE builder.
@@ -365,16 +348,17 @@ class SQLFactory:
365
348
  """
366
349
  return DropTable(table_name, dialect=dialect or self.dialect)
367
350
 
368
- def drop_view(self, dialect: DialectType = None) -> "DropView":
351
+ def drop_view(self, view_name: str, dialect: DialectType = None) -> "DropView":
369
352
  """Create a DROP VIEW builder.
370
353
 
371
354
  Args:
355
+ view_name: Name of the view to drop
372
356
  dialect: Optional SQL dialect
373
357
 
374
358
  Returns:
375
359
  DropView builder instance
376
360
  """
377
- return DropView(dialect=dialect or self.dialect)
361
+ return DropView(view_name, dialect=dialect or self.dialect)
378
362
 
379
363
  def drop_index(self, index_name: str, dialect: DialectType = None) -> "DropIndex":
380
364
  """Create a DROP INDEX builder.
@@ -388,16 +372,17 @@ class SQLFactory:
388
372
  """
389
373
  return DropIndex(index_name, dialect=dialect or self.dialect)
390
374
 
391
- def drop_schema(self, dialect: DialectType = None) -> "DropSchema":
375
+ def drop_schema(self, schema_name: str, dialect: DialectType = None) -> "DropSchema":
392
376
  """Create a DROP SCHEMA builder.
393
377
 
394
378
  Args:
379
+ schema_name: Name of the schema to drop
395
380
  dialect: Optional SQL dialect
396
381
 
397
382
  Returns:
398
383
  DropSchema builder instance
399
384
  """
400
- return DropSchema(dialect=dialect or self.dialect)
385
+ return DropSchema(schema_name, dialect=dialect or self.dialect)
401
386
 
402
387
  def alter_table(self, table_name: str, dialect: DialectType = None) -> "AlterTable":
403
388
  """Create an ALTER TABLE builder.
@@ -409,22 +394,19 @@ class SQLFactory:
409
394
  Returns:
410
395
  AlterTable builder instance
411
396
  """
412
- builder = AlterTable(table_name)
413
- builder.dialect = dialect or self.dialect
414
- return builder
397
+ return AlterTable(table_name, dialect=dialect or self.dialect)
415
398
 
416
- def rename_table(self, dialect: DialectType = None) -> "RenameTable":
399
+ def rename_table(self, old_name: str, dialect: DialectType = None) -> "RenameTable":
417
400
  """Create a RENAME TABLE builder.
418
401
 
419
402
  Args:
403
+ old_name: Current name of the table
420
404
  dialect: Optional SQL dialect
421
405
 
422
406
  Returns:
423
407
  RenameTable builder instance
424
408
  """
425
- builder = RenameTable()
426
- builder.dialect = dialect or self.dialect
427
- return builder
409
+ return RenameTable(old_name, dialect=dialect or self.dialect)
428
410
 
429
411
  def comment_on(self, dialect: DialectType = None) -> "CommentOn":
430
412
  """Create a COMMENT ON builder.
@@ -435,17 +417,11 @@ class SQLFactory:
435
417
  Returns:
436
418
  CommentOn builder instance
437
419
  """
438
- builder = CommentOn()
439
- builder.dialect = dialect or self.dialect
440
- return builder
441
-
442
- # ===================
443
- # SQL Analysis Helpers
444
- # ===================
420
+ return CommentOn(dialect=dialect or self.dialect)
445
421
 
446
422
  @staticmethod
447
423
  def _looks_like_sql(candidate: str, expected_type: Optional[str] = None) -> bool:
448
- """Efficiently determine if a string looks like SQL.
424
+ """Determine if a string looks like SQL.
449
425
 
450
426
  Args:
451
427
  candidate: String to check
@@ -462,12 +438,7 @@ class SQLFactory:
462
438
  if expected_type:
463
439
  return candidate_upper.startswith(expected_type.upper())
464
440
 
465
- # More sophisticated check for SQL vs column names
466
- # Column names that start with SQL keywords are common (user_id, insert_date, etc.)
467
441
  if any(candidate_upper.startswith(starter) for starter in SQL_STARTERS):
468
- # Additional checks to distinguish real SQL from column names:
469
- # 1. Real SQL typically has spaces (SELECT ... FROM, INSERT INTO, etc.)
470
- # 2. Check for common SQL syntax patterns
471
442
  return " " in candidate
472
443
 
473
444
  return False
@@ -475,19 +446,16 @@ class SQLFactory:
475
446
  def _populate_insert_from_sql(self, builder: "Insert", sql_string: str) -> "Insert":
476
447
  """Parse SQL string and populate INSERT builder using SQLGlot directly."""
477
448
  try:
478
- # Use SQLGlot directly for parsing - no validation here
479
- parsed_expr = exp.maybe_parse(sql_string, dialect=self.dialect) # type: ignore[var-annotated]
449
+ parsed_expr: exp.Expression = exp.maybe_parse(sql_string, dialect=self.dialect)
480
450
 
481
451
  if isinstance(parsed_expr, exp.Insert):
482
452
  builder._expression = parsed_expr
483
453
  return builder
484
454
 
485
455
  if isinstance(parsed_expr, exp.Select):
486
- # The actual conversion logic can be handled by the builder itself
487
456
  logger.info("Detected SELECT statement for INSERT - may need target table specification")
488
457
  return builder
489
458
 
490
- # For other statement types, just return the builder as-is
491
459
  logger.warning("Cannot create INSERT from %s statement", type(parsed_expr).__name__)
492
460
 
493
461
  except Exception as e:
@@ -497,8 +465,7 @@ class SQLFactory:
497
465
  def _populate_select_from_sql(self, builder: "Select", sql_string: str) -> "Select":
498
466
  """Parse SQL string and populate SELECT builder using SQLGlot directly."""
499
467
  try:
500
- # Use SQLGlot directly for parsing - no validation here
501
- parsed_expr = exp.maybe_parse(sql_string, dialect=self.dialect) # type: ignore[var-annotated]
468
+ parsed_expr: exp.Expression = exp.maybe_parse(sql_string, dialect=self.dialect)
502
469
 
503
470
  if isinstance(parsed_expr, exp.Select):
504
471
  builder._expression = parsed_expr
@@ -513,8 +480,7 @@ class SQLFactory:
513
480
  def _populate_update_from_sql(self, builder: "Update", sql_string: str) -> "Update":
514
481
  """Parse SQL string and populate UPDATE builder using SQLGlot directly."""
515
482
  try:
516
- # Use SQLGlot directly for parsing - no validation here
517
- parsed_expr = exp.maybe_parse(sql_string, dialect=self.dialect) # type: ignore[var-annotated]
483
+ parsed_expr: exp.Expression = exp.maybe_parse(sql_string, dialect=self.dialect)
518
484
 
519
485
  if isinstance(parsed_expr, exp.Update):
520
486
  builder._expression = parsed_expr
@@ -529,8 +495,7 @@ class SQLFactory:
529
495
  def _populate_delete_from_sql(self, builder: "Delete", sql_string: str) -> "Delete":
530
496
  """Parse SQL string and populate DELETE builder using SQLGlot directly."""
531
497
  try:
532
- # Use SQLGlot directly for parsing - no validation here
533
- parsed_expr = exp.maybe_parse(sql_string, dialect=self.dialect) # type: ignore[var-annotated]
498
+ parsed_expr: exp.Expression = exp.maybe_parse(sql_string, dialect=self.dialect)
534
499
 
535
500
  if isinstance(parsed_expr, exp.Delete):
536
501
  builder._expression = parsed_expr
@@ -545,8 +510,7 @@ class SQLFactory:
545
510
  def _populate_merge_from_sql(self, builder: "Merge", sql_string: str) -> "Merge":
546
511
  """Parse SQL string and populate MERGE builder using SQLGlot directly."""
547
512
  try:
548
- # Use SQLGlot directly for parsing - no validation here
549
- parsed_expr = exp.maybe_parse(sql_string, dialect=self.dialect) # type: ignore[var-annotated]
513
+ parsed_expr: exp.Expression = exp.maybe_parse(sql_string, dialect=self.dialect)
550
514
 
551
515
  if isinstance(parsed_expr, exp.Merge):
552
516
  builder._expression = parsed_expr
@@ -558,10 +522,6 @@ class SQLFactory:
558
522
  logger.warning("Failed to parse MERGE SQL, falling back to traditional mode: %s", e)
559
523
  return builder
560
524
 
561
- # ===================
562
- # Column References
563
- # ===================
564
-
565
525
  def column(self, name: str, table: Optional[str] = None) -> Column:
566
526
  """Create a column reference.
567
527
 
@@ -576,10 +536,10 @@ class SQLFactory:
576
536
 
577
537
  @property
578
538
  def case_(self) -> "Case":
579
- """Create a CASE expression builder with improved syntax.
539
+ """Create a CASE expression builder.
580
540
 
581
541
  Returns:
582
- Case builder instance for fluent CASE expression building.
542
+ Case builder instance for CASE expression building.
583
543
 
584
544
  Example:
585
545
  ```python
@@ -678,23 +638,15 @@ class SQLFactory:
678
638
  Column object for the given name.
679
639
 
680
640
  Note:
681
- Special SQL constructs like case_, row_number_, etc. are now
682
- handled as properties for better type safety.
641
+ Special SQL constructs like case_, row_number_, etc. are
642
+ handled as properties for type safety.
683
643
  """
684
644
  return Column(name)
685
645
 
686
- # ===================
687
- # Raw SQL Expressions
688
- # ===================
689
-
690
646
  @staticmethod
691
647
  def raw(sql_fragment: str, **parameters: Any) -> "Union[exp.Expression, SQL]":
692
648
  """Create a raw SQL expression from a string fragment with optional parameters.
693
649
 
694
- This method makes it explicit that you are passing raw SQL that should
695
- be parsed and included directly in the query. Useful for complex expressions,
696
- database-specific functions, or when you need precise control over the SQL.
697
-
698
650
  Args:
699
651
  sql_fragment: Raw SQL string to parse into an expression.
700
652
  **parameters: Named parameters for parameter binding.
@@ -708,22 +660,21 @@ class SQLFactory:
708
660
 
709
661
  Example:
710
662
  ```python
711
- # Raw expression without parameters (current behavior)
712
663
  expr = sql.raw("COALESCE(name, 'Unknown')")
713
664
 
714
- # Raw SQL with named parameters (new functionality)
665
+
715
666
  stmt = sql.raw(
716
667
  "LOWER(name) LIKE LOWER(:pattern)", pattern=f"%{query}%"
717
668
  )
718
669
 
719
- # Raw complex expression with parameters
670
+
720
671
  expr = sql.raw(
721
672
  "price BETWEEN :min_price AND :max_price",
722
673
  min_price=100,
723
674
  max_price=500,
724
675
  )
725
676
 
726
- # Raw window function
677
+
727
678
  query = sql.select(
728
679
  "name",
729
680
  sql.raw(
@@ -733,11 +684,9 @@ class SQLFactory:
733
684
  ```
734
685
  """
735
686
  if not parameters:
736
- # Original behavior - return pure expression
737
687
  try:
738
- parsed: Optional[exp.Expression] = exp.maybe_parse(sql_fragment)
739
- if parsed is not None:
740
- return parsed
688
+ parsed: exp.Expression = exp.maybe_parse(sql_fragment)
689
+ return parsed
741
690
  if sql_fragment.strip().replace("_", "").replace(".", "").isalnum():
742
691
  return exp.to_identifier(sql_fragment)
743
692
  return exp.Literal.string(sql_fragment)
@@ -745,15 +694,8 @@ class SQLFactory:
745
694
  msg = f"Failed to parse raw SQL fragment '{sql_fragment}': {e}"
746
695
  raise SQLBuilderError(msg) from e
747
696
 
748
- # New behavior - return SQL statement with parameters
749
- from sqlspec.core.statement import SQL
750
-
751
697
  return SQL(sql_fragment, parameters)
752
698
 
753
- # ===================
754
- # Aggregate Functions
755
- # ===================
756
-
757
699
  @staticmethod
758
700
  def count(
759
701
  column: Union[str, exp.Expression, "ExpressionWrapper", "Case", "Column"] = "*", distinct: bool = False
@@ -840,10 +782,6 @@ class SQLFactory:
840
782
  col_expr = SQLFactory._extract_expression(column)
841
783
  return AggregateExpression(exp.Min(this=col_expr))
842
784
 
843
- # ===================
844
- # Advanced SQL Operations
845
- # ===================
846
-
847
785
  @staticmethod
848
786
  def rollup(*columns: Union[str, exp.Expression]) -> FunctionExpression:
849
787
  """Create a ROLLUP expression for GROUP BY clauses.
@@ -856,7 +794,6 @@ class SQLFactory:
856
794
 
857
795
  Example:
858
796
  ```python
859
- # GROUP BY ROLLUP(product, region)
860
797
  query = (
861
798
  sql.select("product", "region", sql.sum("sales"))
862
799
  .from_("sales_data")
@@ -879,7 +816,6 @@ class SQLFactory:
879
816
 
880
817
  Example:
881
818
  ```python
882
- # GROUP BY CUBE(product, region)
883
819
  query = (
884
820
  sql.select("product", "region", sql.sum("sales"))
885
821
  .from_("sales_data")
@@ -902,7 +838,6 @@ class SQLFactory:
902
838
 
903
839
  Example:
904
840
  ```python
905
- # GROUP BY GROUPING SETS ((product), (region), ())
906
841
  query = (
907
842
  sql.select("product", "region", sql.sum("sales"))
908
843
  .from_("sales_data")
@@ -937,7 +872,6 @@ class SQLFactory:
937
872
 
938
873
  Example:
939
874
  ```python
940
- # WHERE id = ANY(subquery)
941
875
  subquery = sql.select("user_id").from_("active_users")
942
876
  query = (
943
877
  sql.select("*")
@@ -950,11 +884,8 @@ class SQLFactory:
950
884
  literals = [SQLFactory.to_literal(v) for v in values]
951
885
  return FunctionExpression(exp.Any(this=exp.Array(expressions=literals)))
952
886
  if isinstance(values, str):
953
- # Parse as SQL
954
- parsed = exp.maybe_parse(values) # type: ignore[var-annotated]
955
- if parsed:
956
- return FunctionExpression(exp.Any(this=parsed))
957
- return FunctionExpression(exp.Any(this=exp.Literal.string(values)))
887
+ parsed: exp.Expression = exp.maybe_parse(values)
888
+ return FunctionExpression(exp.Any(this=parsed))
958
889
  return FunctionExpression(exp.Any(this=values))
959
890
 
960
891
  @staticmethod
@@ -969,7 +900,6 @@ class SQLFactory:
969
900
 
970
901
  Example:
971
902
  ```python
972
- # WHERE id <> ANY(subquery)
973
903
  subquery = sql.select("user_id").from_("blocked_users")
974
904
  query = (
975
905
  sql.select("*")
@@ -980,10 +910,6 @@ class SQLFactory:
980
910
  """
981
911
  return SQLFactory.any(values)
982
912
 
983
- # ===================
984
- # String Functions
985
- # ===================
986
-
987
913
  @staticmethod
988
914
  def concat(*expressions: Union[str, exp.Expression]) -> StringExpression:
989
915
  """Create a CONCAT expression.
@@ -1036,10 +962,6 @@ class SQLFactory:
1036
962
  col_expr = exp.column(column) if isinstance(column, str) else column
1037
963
  return StringExpression(exp.Length(this=col_expr))
1038
964
 
1039
- # ===================
1040
- # Math Functions
1041
- # ===================
1042
-
1043
965
  @staticmethod
1044
966
  def round(column: Union[str, exp.Expression], decimals: int = 0) -> MathExpression:
1045
967
  """Create a ROUND expression.
@@ -1056,16 +978,12 @@ class SQLFactory:
1056
978
  return MathExpression(exp.Round(this=col_expr))
1057
979
  return MathExpression(exp.Round(this=col_expr, expression=exp.Literal.number(decimals)))
1058
980
 
1059
- # ===================
1060
- # Conversion Functions
1061
- # ===================
1062
-
1063
981
  @staticmethod
1064
982
  def to_literal(value: Any) -> FunctionExpression:
1065
983
  """Convert a Python value to a SQLGlot literal expression.
1066
984
 
1067
- Uses SQLGlot's built-in exp.convert() function for optimal dialect-agnostic
1068
- literal creation. Handles all Python primitive types correctly:
985
+ Uses SQLGlot's built-in exp.convert() function for literal creation.
986
+ Handles all Python primitive types:
1069
987
  - None -> exp.Null (renders as NULL)
1070
988
  - bool -> exp.Boolean (renders as TRUE/FALSE or 1/0 based on dialect)
1071
989
  - int/float -> exp.Literal with is_number=True
@@ -1116,7 +1034,6 @@ class SQLFactory:
1116
1034
  if isinstance(value, ExpressionWrapper):
1117
1035
  return value.expression
1118
1036
  if isinstance(value, Case):
1119
- # Case has _expression property via trait
1120
1037
  return exp.Case(ifs=value._conditions, default=value._default)
1121
1038
  if isinstance(value, exp.Expression):
1122
1039
  return value
@@ -1142,7 +1059,6 @@ class SQLFactory:
1142
1059
 
1143
1060
  Example:
1144
1061
  ```python
1145
- # DECODE(status, 'A', 'Active', 'I', 'Inactive', 'Unknown')
1146
1062
  sql.decode(
1147
1063
  "status", "A", "Active", "I", "Inactive", "Unknown"
1148
1064
  )
@@ -1159,7 +1075,6 @@ class SQLFactory:
1159
1075
 
1160
1076
  for i in range(0, len(args) - 1, 2):
1161
1077
  if i + 1 >= len(args):
1162
- # Odd number of args means last one is default
1163
1078
  default = SQLFactory._to_expression(args[i])
1164
1079
  break
1165
1080
 
@@ -1239,7 +1154,6 @@ class SQLFactory:
1239
1154
 
1240
1155
  Example:
1241
1156
  ```python
1242
- # NVL2(salary, 'Has Salary', 'No Salary')
1243
1157
  sql.nvl2("salary", "Has Salary", "No Salary")
1244
1158
  ```
1245
1159
  """
@@ -1247,23 +1161,18 @@ class SQLFactory:
1247
1161
  not_null_expr = SQLFactory._to_expression(value_if_not_null)
1248
1162
  null_expr = SQLFactory._to_expression(value_if_null)
1249
1163
 
1250
- # Create CASE WHEN column IS NOT NULL THEN value_if_not_null ELSE value_if_null END
1251
1164
  is_null = exp.Is(this=col_expr, expression=exp.Null())
1252
1165
  condition = exp.Not(this=is_null)
1253
1166
  when_clause = exp.If(this=condition, true=not_null_expr)
1254
1167
 
1255
1168
  return ConversionExpression(exp.Case(ifs=[when_clause], default=null_expr))
1256
1169
 
1257
- # ===================
1258
- # Bulk Operations
1259
- # ===================
1260
-
1261
1170
  @staticmethod
1262
1171
  def bulk_insert(table_name: str, column_count: int, placeholder_style: str = "?") -> FunctionExpression:
1263
1172
  """Create bulk INSERT expression for executemany operations.
1264
1173
 
1265
- This is specifically for bulk loading operations like CSV ingestion where
1266
- we need an INSERT expression with placeholders for executemany().
1174
+ For bulk loading operations like CSV ingestion where
1175
+ an INSERT expression with placeholders for executemany() is needed.
1267
1176
 
1268
1177
  Args:
1269
1178
  table_name: Name of the table to insert into
@@ -1271,27 +1180,24 @@ class SQLFactory:
1271
1180
  placeholder_style: Placeholder style ("?" for SQLite/PostgreSQL, "%s" for MySQL, ":1" for Oracle)
1272
1181
 
1273
1182
  Returns:
1274
- INSERT expression with proper placeholders for bulk operations
1183
+ INSERT expression with placeholders for bulk operations
1275
1184
 
1276
1185
  Example:
1277
1186
  ```python
1278
1187
  from sqlspec import sql
1279
1188
 
1280
- # SQLite/PostgreSQL style
1189
+
1281
1190
  insert_expr = sql.bulk_insert("my_table", 3)
1282
- # Creates: INSERT INTO "my_table" VALUES (?, ?, ?)
1283
1191
 
1284
- # MySQL style
1192
+
1285
1193
  insert_expr = sql.bulk_insert(
1286
1194
  "my_table", 3, placeholder_style="%s"
1287
1195
  )
1288
- # Creates: INSERT INTO "my_table" VALUES (%s, %s, %s)
1289
1196
 
1290
- # Oracle style
1197
+
1291
1198
  insert_expr = sql.bulk_insert(
1292
1199
  "my_table", 3, placeholder_style=":1"
1293
1200
  )
1294
- # Creates: INSERT INTO "my_table" VALUES (:1, :2, :3)
1295
1201
  ```
1296
1202
  """
1297
1203
  return FunctionExpression(
@@ -1318,10 +1224,10 @@ class SQLFactory:
1318
1224
  ```python
1319
1225
  from sqlspec import sql
1320
1226
 
1321
- # Simple truncate
1227
+
1322
1228
  truncate_sql = sql.truncate_table("my_table").build().sql
1323
1229
 
1324
- # Truncate with options
1230
+
1325
1231
  truncate_sql = (
1326
1232
  sql.truncate_table("my_table")
1327
1233
  .cascade()
@@ -1331,13 +1237,7 @@ class SQLFactory:
1331
1237
  )
1332
1238
  ```
1333
1239
  """
1334
- builder = Truncate(dialect=self.dialect)
1335
- builder._table_name = table_name
1336
- return builder
1337
-
1338
- # ===================
1339
- # Case Expressions
1340
- # ===================
1240
+ return Truncate(table_name, dialect=self.dialect)
1341
1241
 
1342
1242
  @staticmethod
1343
1243
  def case() -> "Case":
@@ -1348,10 +1248,6 @@ class SQLFactory:
1348
1248
  """
1349
1249
  return Case()
1350
1250
 
1351
- # ===================
1352
- # Window Functions
1353
- # ===================
1354
-
1355
1251
  def row_number(
1356
1252
  self,
1357
1253
  partition_by: Optional[Union[str, list[str], exp.Expression]] = None,
@@ -1441,5 +1337,4 @@ class SQLFactory:
1441
1337
  return FunctionExpression(exp.Window(this=func_expr, **over_args))
1442
1338
 
1443
1339
 
1444
- # Create a default SQL factory instance
1445
1340
  sql = SQLFactory()