sqlspec 0.13.1__py3-none-any.whl → 0.14.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 (110) hide show
  1. sqlspec/__init__.py +39 -1
  2. sqlspec/adapters/adbc/config.py +4 -40
  3. sqlspec/adapters/adbc/driver.py +29 -16
  4. sqlspec/adapters/aiosqlite/config.py +2 -20
  5. sqlspec/adapters/aiosqlite/driver.py +36 -18
  6. sqlspec/adapters/asyncmy/config.py +2 -33
  7. sqlspec/adapters/asyncmy/driver.py +23 -16
  8. sqlspec/adapters/asyncpg/config.py +5 -39
  9. sqlspec/adapters/asyncpg/driver.py +41 -18
  10. sqlspec/adapters/bigquery/config.py +2 -43
  11. sqlspec/adapters/bigquery/driver.py +26 -14
  12. sqlspec/adapters/duckdb/config.py +2 -49
  13. sqlspec/adapters/duckdb/driver.py +35 -16
  14. sqlspec/adapters/oracledb/config.py +4 -83
  15. sqlspec/adapters/oracledb/driver.py +54 -27
  16. sqlspec/adapters/psqlpy/config.py +2 -55
  17. sqlspec/adapters/psqlpy/driver.py +28 -8
  18. sqlspec/adapters/psycopg/config.py +4 -73
  19. sqlspec/adapters/psycopg/driver.py +69 -24
  20. sqlspec/adapters/sqlite/config.py +3 -21
  21. sqlspec/adapters/sqlite/driver.py +50 -26
  22. sqlspec/cli.py +248 -0
  23. sqlspec/config.py +18 -20
  24. sqlspec/driver/_async.py +28 -10
  25. sqlspec/driver/_common.py +5 -4
  26. sqlspec/driver/_sync.py +28 -10
  27. sqlspec/driver/mixins/__init__.py +6 -0
  28. sqlspec/driver/mixins/_cache.py +114 -0
  29. sqlspec/driver/mixins/_pipeline.py +0 -4
  30. sqlspec/{service/base.py → driver/mixins/_query_tools.py} +86 -421
  31. sqlspec/driver/mixins/_result_utils.py +0 -2
  32. sqlspec/driver/mixins/_sql_translator.py +0 -2
  33. sqlspec/driver/mixins/_storage.py +4 -18
  34. sqlspec/driver/mixins/_type_coercion.py +0 -2
  35. sqlspec/driver/parameters.py +4 -4
  36. sqlspec/extensions/aiosql/adapter.py +4 -4
  37. sqlspec/extensions/litestar/__init__.py +2 -1
  38. sqlspec/extensions/litestar/cli.py +48 -0
  39. sqlspec/extensions/litestar/plugin.py +3 -0
  40. sqlspec/loader.py +1 -1
  41. sqlspec/migrations/__init__.py +23 -0
  42. sqlspec/migrations/base.py +390 -0
  43. sqlspec/migrations/commands.py +525 -0
  44. sqlspec/migrations/runner.py +215 -0
  45. sqlspec/migrations/tracker.py +153 -0
  46. sqlspec/migrations/utils.py +89 -0
  47. sqlspec/protocols.py +37 -3
  48. sqlspec/statement/builder/__init__.py +8 -8
  49. sqlspec/statement/builder/{column.py → _column.py} +82 -52
  50. sqlspec/statement/builder/{ddl.py → _ddl.py} +5 -5
  51. sqlspec/statement/builder/_ddl_utils.py +1 -1
  52. sqlspec/statement/builder/{delete.py → _delete.py} +1 -1
  53. sqlspec/statement/builder/{insert.py → _insert.py} +1 -1
  54. sqlspec/statement/builder/{merge.py → _merge.py} +1 -1
  55. sqlspec/statement/builder/_parsing_utils.py +5 -3
  56. sqlspec/statement/builder/{select.py → _select.py} +59 -61
  57. sqlspec/statement/builder/{update.py → _update.py} +2 -2
  58. sqlspec/statement/builder/mixins/__init__.py +24 -30
  59. sqlspec/statement/builder/mixins/{_set_ops.py → _cte_and_set_ops.py} +86 -2
  60. sqlspec/statement/builder/mixins/{_delete_from.py → _delete_operations.py} +2 -0
  61. sqlspec/statement/builder/mixins/{_insert_values.py → _insert_operations.py} +70 -1
  62. sqlspec/statement/builder/mixins/{_merge_clauses.py → _merge_operations.py} +2 -0
  63. sqlspec/statement/builder/mixins/_order_limit_operations.py +123 -0
  64. sqlspec/statement/builder/mixins/{_pivot.py → _pivot_operations.py} +71 -2
  65. sqlspec/statement/builder/mixins/_select_operations.py +612 -0
  66. sqlspec/statement/builder/mixins/{_update_set.py → _update_operations.py} +73 -2
  67. sqlspec/statement/builder/mixins/_where_clause.py +536 -0
  68. sqlspec/statement/cache.py +50 -0
  69. sqlspec/statement/filters.py +37 -8
  70. sqlspec/statement/parameters.py +154 -25
  71. sqlspec/statement/pipelines/__init__.py +1 -1
  72. sqlspec/statement/pipelines/context.py +4 -4
  73. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +3 -3
  74. sqlspec/statement/pipelines/validators/_parameter_style.py +22 -22
  75. sqlspec/statement/pipelines/validators/_performance.py +1 -5
  76. sqlspec/statement/sql.py +246 -176
  77. sqlspec/utils/__init__.py +2 -1
  78. sqlspec/utils/statement_hashing.py +203 -0
  79. sqlspec/utils/type_guards.py +32 -0
  80. {sqlspec-0.13.1.dist-info → sqlspec-0.14.0.dist-info}/METADATA +1 -1
  81. sqlspec-0.14.0.dist-info/RECORD +143 -0
  82. sqlspec-0.14.0.dist-info/entry_points.txt +2 -0
  83. sqlspec/service/__init__.py +0 -4
  84. sqlspec/service/_util.py +0 -147
  85. sqlspec/service/pagination.py +0 -26
  86. sqlspec/statement/builder/mixins/_aggregate_functions.py +0 -250
  87. sqlspec/statement/builder/mixins/_case_builder.py +0 -91
  88. sqlspec/statement/builder/mixins/_common_table_expr.py +0 -90
  89. sqlspec/statement/builder/mixins/_from.py +0 -63
  90. sqlspec/statement/builder/mixins/_group_by.py +0 -118
  91. sqlspec/statement/builder/mixins/_having.py +0 -35
  92. sqlspec/statement/builder/mixins/_insert_from_select.py +0 -47
  93. sqlspec/statement/builder/mixins/_insert_into.py +0 -36
  94. sqlspec/statement/builder/mixins/_limit_offset.py +0 -53
  95. sqlspec/statement/builder/mixins/_order_by.py +0 -46
  96. sqlspec/statement/builder/mixins/_returning.py +0 -37
  97. sqlspec/statement/builder/mixins/_select_columns.py +0 -61
  98. sqlspec/statement/builder/mixins/_unpivot.py +0 -77
  99. sqlspec/statement/builder/mixins/_update_from.py +0 -55
  100. sqlspec/statement/builder/mixins/_update_table.py +0 -29
  101. sqlspec/statement/builder/mixins/_where.py +0 -401
  102. sqlspec/statement/builder/mixins/_window_functions.py +0 -86
  103. sqlspec/statement/parameter_manager.py +0 -220
  104. sqlspec/statement/sql_compiler.py +0 -140
  105. sqlspec-0.13.1.dist-info/RECORD +0 -150
  106. /sqlspec/statement/builder/{base.py → _base.py} +0 -0
  107. /sqlspec/statement/builder/mixins/{_join.py → _join_operations.py} +0 -0
  108. {sqlspec-0.13.1.dist-info → sqlspec-0.14.0.dist-info}/WHEEL +0 -0
  109. {sqlspec-0.13.1.dist-info → sqlspec-0.14.0.dist-info}/licenses/LICENSE +0 -0
  110. {sqlspec-0.13.1.dist-info → sqlspec-0.14.0.dist-info}/licenses/NOTICE +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from collections import abc
5
+ from collections.abc import Sequence
5
6
  from dataclasses import dataclass
6
7
  from datetime import datetime
7
8
  from typing import TYPE_CHECKING, Any, Generic, Literal, Optional, Protocol, Union, runtime_checkable
@@ -25,6 +26,7 @@ __all__ = (
25
26
  "NotAnyCollectionFilter",
26
27
  "NotInCollectionFilter",
27
28
  "NotInSearchFilter",
29
+ "OffsetPagination",
28
30
  "OnBeforeAfterFilter",
29
31
  "OrderByFilter",
30
32
  "PaginationFilter",
@@ -430,8 +432,7 @@ class LimitOffsetFilter(PaginationFilter):
430
432
  _, named_params = self.extract_parameters()
431
433
  for name, value in named_params.items():
432
434
  result = result.add_named_parameter(name, value)
433
-
434
- return result
435
+ return result.filter(self)
435
436
 
436
437
 
437
438
  @dataclass
@@ -449,12 +450,21 @@ class OrderByFilter(StatementFilter):
449
450
  return [], {}
450
451
 
451
452
  def append_to_statement(self, statement: "SQL") -> "SQL":
452
- normalized_sort_order = self.sort_order.lower()
453
- if normalized_sort_order not in {"asc", "desc"}:
454
- normalized_sort_order = "asc"
455
- if normalized_sort_order == "desc":
456
- return statement.order_by(exp.column(self.field_name).desc())
457
- return statement.order_by(exp.column(self.field_name).asc())
453
+ converted_sort_order = self.sort_order.lower()
454
+ if converted_sort_order not in {"asc", "desc"}:
455
+ converted_sort_order = "asc"
456
+
457
+ col_expr = exp.column(self.field_name)
458
+ order_expr = col_expr.desc() if converted_sort_order == "desc" else col_expr.asc()
459
+
460
+ # Check if the statement supports ORDER BY directly
461
+ if isinstance(statement._statement, exp.Select):
462
+ new_statement = statement._statement.order_by(order_expr)
463
+ else:
464
+ # Wrap in a SELECT if the statement doesn't support ORDER BY directly
465
+ new_statement = exp.Select().from_(statement._statement).order_by(order_expr)
466
+
467
+ return statement.copy(statement=new_statement)
458
468
 
459
469
 
460
470
  @dataclass
@@ -568,6 +578,25 @@ class NotInSearchFilter(SearchFilter):
568
578
  return result
569
579
 
570
580
 
581
+ @dataclass
582
+ class OffsetPagination(Generic[T]):
583
+ """Container for data returned using limit/offset pagination."""
584
+
585
+ __slots__ = ("items", "limit", "offset", "total")
586
+
587
+ items: Sequence[T]
588
+ """List of data being sent as part of the response."""
589
+ limit: int
590
+ """Maximal number of items to send."""
591
+ offset: int
592
+ """Offset from the beginning of the query.
593
+
594
+ Identical to an index.
595
+ """
596
+ total: int
597
+ """Total number of items."""
598
+
599
+
571
600
  def apply_filter(statement: "SQL", filter_obj: StatementFilter) -> "SQL":
572
601
  """Apply a statement filter to a SQL query object.
573
602
 
@@ -20,12 +20,15 @@ from sqlspec.typing import SQLParameterType
20
20
  if TYPE_CHECKING:
21
21
  from sqlglot import exp
22
22
 
23
+ # Constants
24
+ MAX_32BIT_INT: Final[int] = 2147483647
25
+
23
26
  __all__ = (
24
27
  "ConvertedParameters",
25
28
  "ParameterConverter",
26
29
  "ParameterInfo",
27
- "ParameterNormalizationState",
28
30
  "ParameterStyle",
31
+ "ParameterStyleTransformationState",
29
32
  "ParameterValidator",
30
33
  "SQLParameterType",
31
34
  "TypedParameter",
@@ -140,40 +143,56 @@ class TypedParameter:
140
143
  semantic_name: "Optional[str]" = None
141
144
  """Optional semantic name derived from SQL context (e.g., 'user_id', 'email')."""
142
145
 
146
+ def __hash__(self) -> int:
147
+ """Make TypedParameter hashable for use in cache keys.
148
+
149
+ We hash based on the value and type_hint, which are the key attributes
150
+ that affect SQL compilation and parameter handling.
151
+ """
152
+ if isinstance(self.value, (list, dict)):
153
+ value_hash = hash(repr(self.value))
154
+ else:
155
+ try:
156
+ value_hash = hash(self.value)
157
+ except TypeError:
158
+ value_hash = hash(repr(self.value))
159
+
160
+ return hash((value_hash, self.type_hint, self.semantic_name))
143
161
 
144
- class NormalizationInfo(TypedDict, total=False):
145
- """Information about SQL parameter normalization."""
146
162
 
147
- was_normalized: bool
163
+ class ParameterStyleInfo(TypedDict, total=False):
164
+ """Information about SQL parameter style transformation."""
165
+
166
+ was_converted: bool
148
167
  placeholder_map: dict[str, Union[str, int]]
149
168
  original_styles: list[ParameterStyle]
150
169
 
151
170
 
152
171
  @dataclass
153
- class ParameterNormalizationState:
154
- """Encapsulates all information about parameter normalization.
172
+ class ParameterStyleTransformationState:
173
+ """Encapsulates all information about parameter style transformation.
155
174
 
156
175
  This class provides a single source of truth for parameter style conversions,
157
- making it easier to track and reverse normalizations applied for SQLGlot compatibility.
176
+ making it easier to track and reverse transformations applied for SQLGlot compatibility.
158
177
  """
159
178
 
160
- was_normalized: bool = False
161
- """Whether parameter normalization was applied."""
179
+ was_transformed: bool = False
180
+ """Whether parameter transformation was applied."""
162
181
 
163
182
  original_styles: list[ParameterStyle] = field(default_factory=list)
164
183
  """Original parameter style(s) detected in the SQL."""
165
184
 
166
- normalized_style: Optional[ParameterStyle] = None
167
- """Target style used for normalization (if normalized)."""
185
+ transformation_style: Optional[ParameterStyle] = None
186
+ """Target style used for transformation (if transformed)."""
168
187
 
169
188
  placeholder_map: dict[str, Union[str, int]] = field(default_factory=dict)
170
- """Mapping from normalized names to original names/positions."""
189
+ """Mapping from transformed names to original names/positions."""
171
190
 
172
191
  reverse_map: dict[Union[str, int], str] = field(default_factory=dict)
173
192
  """Reverse mapping for quick lookups."""
174
193
 
175
194
  original_param_info: list["ParameterInfo"] = field(default_factory=list)
176
- """Original parameter info before normalization."""
195
+ """Original parameter info before conversion."""
177
196
 
178
197
  def __post_init__(self) -> None:
179
198
  """Build reverse map if not provided."""
@@ -194,8 +213,8 @@ class ConvertedParameters:
194
213
  merged_parameters: "SQLParameterType"
195
214
  """Parameters after merging from various sources."""
196
215
 
197
- normalization_state: ParameterNormalizationState
198
- """Complete normalization state for tracking conversions."""
216
+ conversion_state: ParameterStyleTransformationState
217
+ """Complete conversion state for tracking conversions."""
199
218
 
200
219
 
201
220
  @dataclass
@@ -673,7 +692,7 @@ class ParameterConverter:
673
692
  """
674
693
  parameters_info = self.validator.extract_parameters(sql)
675
694
 
676
- needs_normalization = any(p.style in SQLGLOT_INCOMPATIBLE_STYLES for p in parameters_info)
695
+ needs_conversion = any(p.style in SQLGLOT_INCOMPATIBLE_STYLES for p in parameters_info)
677
696
 
678
697
  has_positional = any(p.name is None for p in parameters_info)
679
698
  has_named = any(p.name is not None for p in parameters_info)
@@ -686,19 +705,19 @@ class ParameterConverter:
686
705
 
687
706
  if validate:
688
707
  self.validator.validate_parameters(parameters_info, merged_params, sql)
689
- if needs_normalization:
708
+ if needs_conversion:
690
709
  transformed_sql, placeholder_map = self._transform_sql_for_parsing(sql, parameters_info)
691
- normalization_state = ParameterNormalizationState(
692
- was_normalized=True,
710
+ conversion_state = ParameterStyleTransformationState(
711
+ was_transformed=True,
693
712
  original_styles=list({p.style for p in parameters_info}),
694
- normalized_style=ParameterStyle.NAMED_COLON,
713
+ transformation_style=ParameterStyle.NAMED_COLON,
695
714
  placeholder_map=placeholder_map,
696
715
  original_param_info=parameters_info,
697
716
  )
698
717
  else:
699
718
  transformed_sql = sql
700
- normalization_state = ParameterNormalizationState(
701
- was_normalized=False,
719
+ conversion_state = ParameterStyleTransformationState(
720
+ was_transformed=False,
702
721
  original_styles=list({p.style for p in parameters_info}),
703
722
  original_param_info=parameters_info,
704
723
  )
@@ -707,7 +726,7 @@ class ParameterConverter:
707
726
  transformed_sql=transformed_sql,
708
727
  parameter_info=parameters_info,
709
728
  merged_parameters=merged_params,
710
- normalization_state=normalization_state,
729
+ conversion_state=conversion_state,
711
730
  )
712
731
 
713
732
  @staticmethod
@@ -781,7 +800,117 @@ class ParameterConverter:
781
800
  Returns:
782
801
  Parameters with TypedParameter wrapping where appropriate
783
802
  """
784
- return None if parameters is None else parameters
803
+ if parameters is None:
804
+ return None
805
+
806
+ # Import here to avoid circular imports
807
+ from datetime import date, datetime, time
808
+ from decimal import Decimal
809
+
810
+ def infer_type_from_value(value: Any) -> tuple[str, "exp.DataType"]:
811
+ """Infer SQL type hint and SQLGlot DataType from Python value."""
812
+ # Import here to avoid issues
813
+ from sqlglot import exp
814
+
815
+ # None/NULL
816
+ if value is None:
817
+ return "null", exp.DataType.build("NULL")
818
+
819
+ # Boolean
820
+ if isinstance(value, bool):
821
+ return "boolean", exp.DataType.build("BOOLEAN")
822
+
823
+ # Integer types
824
+ if isinstance(value, int) and not isinstance(value, bool):
825
+ if abs(value) > MAX_32BIT_INT:
826
+ return "bigint", exp.DataType.build("BIGINT")
827
+ return "integer", exp.DataType.build("INT")
828
+
829
+ # Float/Decimal
830
+ if isinstance(value, float):
831
+ return "float", exp.DataType.build("FLOAT")
832
+ if isinstance(value, Decimal):
833
+ return "decimal", exp.DataType.build("DECIMAL")
834
+
835
+ # Date/Time types
836
+ if isinstance(value, datetime):
837
+ return "timestamp", exp.DataType.build("TIMESTAMP")
838
+ if isinstance(value, date):
839
+ return "date", exp.DataType.build("DATE")
840
+ if isinstance(value, time):
841
+ return "time", exp.DataType.build("TIME")
842
+
843
+ # JSON/Dict
844
+ if isinstance(value, dict):
845
+ return "json", exp.DataType.build("JSON")
846
+
847
+ # Array/List
848
+ if isinstance(value, (list, tuple)):
849
+ return "array", exp.DataType.build("ARRAY")
850
+
851
+ if isinstance(value, str):
852
+ return "string", exp.DataType.build("VARCHAR")
853
+
854
+ # Bytes
855
+ if isinstance(value, bytes):
856
+ return "binary", exp.DataType.build("BINARY")
857
+
858
+ # Default fallback
859
+ return "string", exp.DataType.build("VARCHAR")
860
+
861
+ def wrap_value(value: Any, semantic_name: Optional[str] = None) -> Any:
862
+ """Wrap a single value with TypedParameter if beneficial."""
863
+ # Don't wrap if already a TypedParameter
864
+ if hasattr(value, "__class__") and value.__class__.__name__ == "TypedParameter":
865
+ return value
866
+
867
+ # Don't wrap simple scalar types unless they need special handling
868
+ if isinstance(value, (str, int, float)) and not isinstance(value, bool):
869
+ # For simple types, only wrap if we have special type needs
870
+ # (e.g., bigint, decimal precision, etc.)
871
+ if isinstance(value, int) and abs(value) > MAX_32BIT_INT:
872
+ # Wrap large integers as bigint
873
+ type_hint, sqlglot_type = infer_type_from_value(value)
874
+ return TypedParameter(
875
+ value=value, sqlglot_type=sqlglot_type, type_hint=type_hint, semantic_name=semantic_name
876
+ )
877
+ # Otherwise, return unwrapped for performance
878
+ return value
879
+
880
+ # Wrap complex types and types needing special handling
881
+ if isinstance(value, (datetime, date, time, Decimal, dict, list, tuple, bytes, bool, type(None))):
882
+ type_hint, sqlglot_type = infer_type_from_value(value)
883
+ return TypedParameter(
884
+ value=value, sqlglot_type=sqlglot_type, type_hint=type_hint, semantic_name=semantic_name
885
+ )
886
+
887
+ # Default: return unwrapped
888
+ return value
889
+
890
+ # Handle different parameter structures
891
+ if isinstance(parameters, dict):
892
+ # Wrap dict values selectively
893
+ wrapped_dict = {}
894
+ for key, value in parameters.items():
895
+ wrapped_dict[key] = wrap_value(value, semantic_name=key)
896
+ return wrapped_dict
897
+
898
+ if isinstance(parameters, (list, tuple)):
899
+ # Wrap list/tuple values selectively
900
+ wrapped_list: list[Any] = []
901
+ for i, value in enumerate(parameters):
902
+ # Try to get semantic name from parameters_info if available
903
+ semantic_name = None
904
+ if parameters_info and i < len(parameters_info) and parameters_info[i].name:
905
+ semantic_name = parameters_info[i].name
906
+ wrapped_list.append(wrap_value(value, semantic_name=semantic_name))
907
+ return wrapped_list if isinstance(parameters, list) else tuple(wrapped_list)
908
+
909
+ # Single scalar parameter
910
+ semantic_name = None
911
+ if parameters_info and parameters_info[0].name:
912
+ semantic_name = parameters_info[0].name
913
+ return wrap_value(parameters, semantic_name=semantic_name)
785
914
 
786
915
  def _convert_sql_placeholders(
787
916
  self, rendered_sql: str, final_parameter_info: "list[ParameterInfo]", target_style: "ParameterStyle"
@@ -816,7 +945,7 @@ class ParameterConverter:
816
945
  from sqlspec.exceptions import SQLTransformationError
817
946
 
818
947
  msg = (
819
- f"Parameter count mismatch during denormalization. "
948
+ f"Parameter count mismatch during deconversion. "
820
949
  f"Expected at least {len(final_parameter_info)} parameters, "
821
950
  f"found {len(canonical_params)} in SQL"
822
951
  )
@@ -155,7 +155,7 @@ class StatementPipeline:
155
155
  UnsupportedParameterStyleError,
156
156
  )
157
157
 
158
- if context.config.strict_mode and isinstance(
158
+ if not context.config.parse_errors_as_warnings and isinstance(
159
159
  e, (MissingParameterError, MixedParameterStyleError, UnsupportedParameterStyleError)
160
160
  ):
161
161
  raise
@@ -8,7 +8,7 @@ from sqlspec.exceptions import RiskLevel
8
8
  if TYPE_CHECKING:
9
9
  from sqlglot.dialects.dialect import DialectType
10
10
 
11
- from sqlspec.statement.parameters import ParameterInfo, ParameterNormalizationState
11
+ from sqlspec.statement.parameters import ParameterInfo, ParameterStyleTransformationState
12
12
  from sqlspec.statement.sql import SQLConfig
13
13
  from sqlspec.typing import SQLParameterType
14
14
 
@@ -97,10 +97,10 @@ class SQLProcessingContext:
97
97
  statement_type: Optional[str] = None
98
98
  """The detected type of the SQL statement (e.g., SELECT, INSERT, DDL)."""
99
99
  extra_info: dict[str, Any] = field(default_factory=dict)
100
- """Extra information from parameter processing, including normalization state."""
100
+ """Extra information from parameter processing, including conversion state."""
101
101
 
102
- parameter_normalization: "Optional[ParameterNormalizationState]" = None
103
- """Single source of truth for parameter normalization tracking."""
102
+ parameter_conversion: "Optional[ParameterStyleTransformationState]" = None
103
+ """Single source of truth for parameter style conversion tracking."""
104
104
 
105
105
  @property
106
106
  def has_errors(self) -> bool:
@@ -21,7 +21,7 @@ class SimplificationConfig:
21
21
  enable_literal_folding: bool = True
22
22
  enable_boolean_optimization: bool = True
23
23
  enable_connector_optimization: bool = True
24
- enable_equality_normalization: bool = True
24
+ enable_equality_conversion: bool = True
25
25
  enable_complement_removal: bool = True
26
26
 
27
27
 
@@ -74,8 +74,8 @@ class ExpressionSimplifier(ProcessorProtocol):
74
74
  optimizations.append("boolean_optimization")
75
75
  if self.config.enable_connector_optimization:
76
76
  optimizations.append("connector_optimization")
77
- if self.config.enable_equality_normalization:
78
- optimizations.append("equality_normalization")
77
+ if self.config.enable_equality_conversion:
78
+ optimizations.append("equality_conversion")
79
79
  if self.config.enable_complement_removal:
80
80
  optimizations.append("complement_removal")
81
81
 
@@ -73,13 +73,13 @@ class ParameterStyleValidator(ProcessorProtocol):
73
73
  config = context.config
74
74
  param_info = context.parameter_info
75
75
 
76
- # Check if parameters were normalized by looking for param_ placeholders
77
- # This happens when Oracle numeric parameters (:1, :2) are normalized
78
- is_normalized = param_info and any(p.name and p.name.startswith("param_") for p in param_info)
76
+ # Check if parameters were converted by looking for param_ placeholders
77
+ # This happens when Oracle numeric parameters (:1, :2) are converted
78
+ is_converted = param_info and any(p.name and p.name.startswith("param_") for p in param_info)
79
79
 
80
- # First check parameter styles if configured (skip if normalized)
80
+ # First check parameter styles if configured (skip if converted)
81
81
  has_style_errors = False
82
- if not is_normalized and config.allowed_parameter_styles is not None and param_info:
82
+ if not is_converted and config.allowed_parameter_styles is not None and param_info:
83
83
  unique_styles = {p.style for p in param_info}
84
84
 
85
85
  if len(unique_styles) > 1 and not config.allow_mixed_parameter_styles:
@@ -279,11 +279,11 @@ class ParameterStyleValidator(ProcessorProtocol):
279
279
  """Handle validation for named parameters."""
280
280
  missing: list[str] = []
281
281
 
282
- # Check if we have normalized parameters (e.g., param_0)
283
- is_normalized = any(p.name and p.name.startswith("param_") for p in param_info)
282
+ # Check if we have converted parameters (e.g., param_0)
283
+ is_converted = any(p.name and p.name.startswith("param_") for p in param_info)
284
284
 
285
- if is_normalized and hasattr(context, "extra_info"):
286
- # For normalized parameters, we need to check against the original placeholder mapping
285
+ if is_converted and hasattr(context, "extra_info"):
286
+ # For converted parameters, we need to check against the original placeholder mapping
287
287
  placeholder_map = context.extra_info.get("placeholder_map", {})
288
288
 
289
289
  # Check if we have Oracle numeric keys in merged_params
@@ -291,9 +291,9 @@ class ParameterStyleValidator(ProcessorProtocol):
291
291
 
292
292
  if all_numeric_keys:
293
293
  # Parameters were provided as list and converted to Oracle numeric dict {"1": val1, "2": val2}
294
- for i, _p in enumerate(param_info):
295
- normalized_name = f"param_{i}"
296
- original_key = placeholder_map.get(normalized_name)
294
+ for i in range(len(param_info)):
295
+ converted_name = f"param_{i}"
296
+ original_key = placeholder_map.get(converted_name)
297
297
 
298
298
  if original_key is not None:
299
299
  # Check using the original key (e.g., "1", "2" for Oracle)
@@ -309,11 +309,11 @@ class ParameterStyleValidator(ProcessorProtocol):
309
309
 
310
310
  if all_param_keys:
311
311
  # This was originally a list converted to dict with param_N keys
312
- for i, _p in enumerate(param_info):
313
- normalized_name = f"param_{i}"
314
- if normalized_name not in merged_params or merged_params[normalized_name] is None:
312
+ for i in range(len(param_info)):
313
+ converted_name = f"param_{i}"
314
+ if converted_name not in merged_params or merged_params[converted_name] is None:
315
315
  # Get original parameter style from placeholder map
316
- original_key = placeholder_map.get(normalized_name)
316
+ original_key = placeholder_map.get(converted_name)
317
317
  if original_key is not None:
318
318
  original_key_str = str(original_key)
319
319
  if original_key_str.isdigit():
@@ -322,16 +322,16 @@ class ParameterStyleValidator(ProcessorProtocol):
322
322
  missing.append(f":{original_key}")
323
323
  else:
324
324
  # Mixed parameter names, check using placeholder map
325
- for i, _p in enumerate(param_info):
326
- normalized_name = f"param_{i}"
327
- original_key = placeholder_map.get(normalized_name)
325
+ for i in range(len(param_info)):
326
+ converted_name = f"param_{i}"
327
+ original_key = placeholder_map.get(converted_name)
328
328
 
329
329
  if original_key is not None:
330
- # For mixed params, check both normalized and original keys
330
+ # For mixed params, check both converted and original keys
331
331
  original_key_str = str(original_key)
332
332
 
333
- # First check with normalized name
334
- found = normalized_name in merged_params and merged_params[normalized_name] is not None
333
+ # First check with converted name
334
+ found = converted_name in merged_params and merged_params[converted_name] is not None
335
335
 
336
336
  # If not found, check with original key
337
337
  if not found:
@@ -601,11 +601,7 @@ class PerformanceValidator(ProcessorProtocol):
601
601
  ),
602
602
  ("join_optimization", optimize_joins.optimize_joins, "Optimize join order and conditions"),
603
603
  ("simplification", simplify.simplify, "Simplify expressions and conditions"),
604
- (
605
- "identifier_normalization",
606
- normalize_identifiers.normalize_identifiers,
607
- "Normalize identifier casing",
608
- ),
604
+ ("identifier_conversion", normalize_identifiers.normalize_identifiers, "Normalize identifier casing"),
609
605
  ]
610
606
 
611
607
  best_optimized = expression.copy()