sqlspec 0.32.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.
Files changed (262) hide show
  1. sqlspec/__init__.py +104 -0
  2. sqlspec/__main__.py +12 -0
  3. sqlspec/__metadata__.py +14 -0
  4. sqlspec/_serialization.py +312 -0
  5. sqlspec/_typing.py +784 -0
  6. sqlspec/adapters/__init__.py +0 -0
  7. sqlspec/adapters/adbc/__init__.py +5 -0
  8. sqlspec/adapters/adbc/_types.py +12 -0
  9. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  10. sqlspec/adapters/adbc/adk/store.py +880 -0
  11. sqlspec/adapters/adbc/config.py +436 -0
  12. sqlspec/adapters/adbc/data_dictionary.py +537 -0
  13. sqlspec/adapters/adbc/driver.py +841 -0
  14. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  15. sqlspec/adapters/adbc/litestar/store.py +504 -0
  16. sqlspec/adapters/adbc/type_converter.py +153 -0
  17. sqlspec/adapters/aiosqlite/__init__.py +29 -0
  18. sqlspec/adapters/aiosqlite/_types.py +13 -0
  19. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/adk/store.py +536 -0
  21. sqlspec/adapters/aiosqlite/config.py +310 -0
  22. sqlspec/adapters/aiosqlite/data_dictionary.py +260 -0
  23. sqlspec/adapters/aiosqlite/driver.py +463 -0
  24. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  25. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  26. sqlspec/adapters/aiosqlite/pool.py +500 -0
  27. sqlspec/adapters/asyncmy/__init__.py +25 -0
  28. sqlspec/adapters/asyncmy/_types.py +12 -0
  29. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/adk/store.py +503 -0
  31. sqlspec/adapters/asyncmy/config.py +246 -0
  32. sqlspec/adapters/asyncmy/data_dictionary.py +241 -0
  33. sqlspec/adapters/asyncmy/driver.py +632 -0
  34. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  35. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  36. sqlspec/adapters/asyncpg/__init__.py +23 -0
  37. sqlspec/adapters/asyncpg/_type_handlers.py +76 -0
  38. sqlspec/adapters/asyncpg/_types.py +23 -0
  39. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/adk/store.py +460 -0
  41. sqlspec/adapters/asyncpg/config.py +464 -0
  42. sqlspec/adapters/asyncpg/data_dictionary.py +321 -0
  43. sqlspec/adapters/asyncpg/driver.py +720 -0
  44. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  45. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  46. sqlspec/adapters/bigquery/__init__.py +18 -0
  47. sqlspec/adapters/bigquery/_types.py +12 -0
  48. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  49. sqlspec/adapters/bigquery/adk/store.py +585 -0
  50. sqlspec/adapters/bigquery/config.py +298 -0
  51. sqlspec/adapters/bigquery/data_dictionary.py +256 -0
  52. sqlspec/adapters/bigquery/driver.py +1073 -0
  53. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  54. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  55. sqlspec/adapters/bigquery/type_converter.py +125 -0
  56. sqlspec/adapters/duckdb/__init__.py +24 -0
  57. sqlspec/adapters/duckdb/_types.py +12 -0
  58. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  59. sqlspec/adapters/duckdb/adk/store.py +563 -0
  60. sqlspec/adapters/duckdb/config.py +396 -0
  61. sqlspec/adapters/duckdb/data_dictionary.py +264 -0
  62. sqlspec/adapters/duckdb/driver.py +604 -0
  63. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  64. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  65. sqlspec/adapters/duckdb/pool.py +273 -0
  66. sqlspec/adapters/duckdb/type_converter.py +133 -0
  67. sqlspec/adapters/oracledb/__init__.py +32 -0
  68. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  69. sqlspec/adapters/oracledb/_types.py +39 -0
  70. sqlspec/adapters/oracledb/_uuid_handlers.py +130 -0
  71. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  72. sqlspec/adapters/oracledb/adk/store.py +1632 -0
  73. sqlspec/adapters/oracledb/config.py +469 -0
  74. sqlspec/adapters/oracledb/data_dictionary.py +717 -0
  75. sqlspec/adapters/oracledb/driver.py +1493 -0
  76. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  77. sqlspec/adapters/oracledb/litestar/store.py +765 -0
  78. sqlspec/adapters/oracledb/migrations.py +532 -0
  79. sqlspec/adapters/oracledb/type_converter.py +207 -0
  80. sqlspec/adapters/psqlpy/__init__.py +16 -0
  81. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  82. sqlspec/adapters/psqlpy/_types.py +12 -0
  83. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  84. sqlspec/adapters/psqlpy/adk/store.py +483 -0
  85. sqlspec/adapters/psqlpy/config.py +271 -0
  86. sqlspec/adapters/psqlpy/data_dictionary.py +179 -0
  87. sqlspec/adapters/psqlpy/driver.py +892 -0
  88. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  90. sqlspec/adapters/psqlpy/type_converter.py +102 -0
  91. sqlspec/adapters/psycopg/__init__.py +32 -0
  92. sqlspec/adapters/psycopg/_type_handlers.py +90 -0
  93. sqlspec/adapters/psycopg/_types.py +18 -0
  94. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  95. sqlspec/adapters/psycopg/adk/store.py +962 -0
  96. sqlspec/adapters/psycopg/config.py +487 -0
  97. sqlspec/adapters/psycopg/data_dictionary.py +630 -0
  98. sqlspec/adapters/psycopg/driver.py +1336 -0
  99. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  100. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  101. sqlspec/adapters/spanner/__init__.py +38 -0
  102. sqlspec/adapters/spanner/_type_handlers.py +186 -0
  103. sqlspec/adapters/spanner/_types.py +12 -0
  104. sqlspec/adapters/spanner/adk/__init__.py +5 -0
  105. sqlspec/adapters/spanner/adk/store.py +435 -0
  106. sqlspec/adapters/spanner/config.py +241 -0
  107. sqlspec/adapters/spanner/data_dictionary.py +95 -0
  108. sqlspec/adapters/spanner/dialect/__init__.py +6 -0
  109. sqlspec/adapters/spanner/dialect/_spangres.py +52 -0
  110. sqlspec/adapters/spanner/dialect/_spanner.py +123 -0
  111. sqlspec/adapters/spanner/driver.py +366 -0
  112. sqlspec/adapters/spanner/litestar/__init__.py +5 -0
  113. sqlspec/adapters/spanner/litestar/store.py +266 -0
  114. sqlspec/adapters/spanner/type_converter.py +46 -0
  115. sqlspec/adapters/sqlite/__init__.py +18 -0
  116. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  117. sqlspec/adapters/sqlite/_types.py +11 -0
  118. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  119. sqlspec/adapters/sqlite/adk/store.py +582 -0
  120. sqlspec/adapters/sqlite/config.py +221 -0
  121. sqlspec/adapters/sqlite/data_dictionary.py +256 -0
  122. sqlspec/adapters/sqlite/driver.py +527 -0
  123. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  124. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  125. sqlspec/adapters/sqlite/pool.py +140 -0
  126. sqlspec/base.py +811 -0
  127. sqlspec/builder/__init__.py +146 -0
  128. sqlspec/builder/_base.py +900 -0
  129. sqlspec/builder/_column.py +517 -0
  130. sqlspec/builder/_ddl.py +1642 -0
  131. sqlspec/builder/_delete.py +84 -0
  132. sqlspec/builder/_dml.py +381 -0
  133. sqlspec/builder/_expression_wrappers.py +46 -0
  134. sqlspec/builder/_factory.py +1537 -0
  135. sqlspec/builder/_insert.py +315 -0
  136. sqlspec/builder/_join.py +375 -0
  137. sqlspec/builder/_merge.py +848 -0
  138. sqlspec/builder/_parsing_utils.py +297 -0
  139. sqlspec/builder/_select.py +1615 -0
  140. sqlspec/builder/_update.py +161 -0
  141. sqlspec/builder/_vector_expressions.py +259 -0
  142. sqlspec/cli.py +764 -0
  143. sqlspec/config.py +1540 -0
  144. sqlspec/core/__init__.py +305 -0
  145. sqlspec/core/cache.py +785 -0
  146. sqlspec/core/compiler.py +603 -0
  147. sqlspec/core/filters.py +872 -0
  148. sqlspec/core/hashing.py +274 -0
  149. sqlspec/core/metrics.py +83 -0
  150. sqlspec/core/parameters/__init__.py +64 -0
  151. sqlspec/core/parameters/_alignment.py +266 -0
  152. sqlspec/core/parameters/_converter.py +413 -0
  153. sqlspec/core/parameters/_processor.py +341 -0
  154. sqlspec/core/parameters/_registry.py +201 -0
  155. sqlspec/core/parameters/_transformers.py +226 -0
  156. sqlspec/core/parameters/_types.py +430 -0
  157. sqlspec/core/parameters/_validator.py +123 -0
  158. sqlspec/core/pipeline.py +187 -0
  159. sqlspec/core/result.py +1124 -0
  160. sqlspec/core/splitter.py +940 -0
  161. sqlspec/core/stack.py +163 -0
  162. sqlspec/core/statement.py +835 -0
  163. sqlspec/core/type_conversion.py +235 -0
  164. sqlspec/driver/__init__.py +36 -0
  165. sqlspec/driver/_async.py +1027 -0
  166. sqlspec/driver/_common.py +1236 -0
  167. sqlspec/driver/_sync.py +1025 -0
  168. sqlspec/driver/mixins/__init__.py +7 -0
  169. sqlspec/driver/mixins/_result_tools.py +61 -0
  170. sqlspec/driver/mixins/_sql_translator.py +122 -0
  171. sqlspec/driver/mixins/_storage.py +311 -0
  172. sqlspec/exceptions.py +321 -0
  173. sqlspec/extensions/__init__.py +0 -0
  174. sqlspec/extensions/adk/__init__.py +53 -0
  175. sqlspec/extensions/adk/_types.py +51 -0
  176. sqlspec/extensions/adk/converters.py +172 -0
  177. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  178. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  179. sqlspec/extensions/adk/service.py +181 -0
  180. sqlspec/extensions/adk/store.py +536 -0
  181. sqlspec/extensions/aiosql/__init__.py +10 -0
  182. sqlspec/extensions/aiosql/adapter.py +471 -0
  183. sqlspec/extensions/fastapi/__init__.py +19 -0
  184. sqlspec/extensions/fastapi/extension.py +341 -0
  185. sqlspec/extensions/fastapi/providers.py +543 -0
  186. sqlspec/extensions/flask/__init__.py +36 -0
  187. sqlspec/extensions/flask/_state.py +72 -0
  188. sqlspec/extensions/flask/_utils.py +40 -0
  189. sqlspec/extensions/flask/extension.py +402 -0
  190. sqlspec/extensions/litestar/__init__.py +23 -0
  191. sqlspec/extensions/litestar/_utils.py +52 -0
  192. sqlspec/extensions/litestar/cli.py +92 -0
  193. sqlspec/extensions/litestar/config.py +90 -0
  194. sqlspec/extensions/litestar/handlers.py +316 -0
  195. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  196. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  197. sqlspec/extensions/litestar/plugin.py +638 -0
  198. sqlspec/extensions/litestar/providers.py +454 -0
  199. sqlspec/extensions/litestar/store.py +265 -0
  200. sqlspec/extensions/otel/__init__.py +58 -0
  201. sqlspec/extensions/prometheus/__init__.py +107 -0
  202. sqlspec/extensions/starlette/__init__.py +10 -0
  203. sqlspec/extensions/starlette/_state.py +26 -0
  204. sqlspec/extensions/starlette/_utils.py +52 -0
  205. sqlspec/extensions/starlette/extension.py +257 -0
  206. sqlspec/extensions/starlette/middleware.py +154 -0
  207. sqlspec/loader.py +716 -0
  208. sqlspec/migrations/__init__.py +36 -0
  209. sqlspec/migrations/base.py +728 -0
  210. sqlspec/migrations/commands.py +1140 -0
  211. sqlspec/migrations/context.py +142 -0
  212. sqlspec/migrations/fix.py +203 -0
  213. sqlspec/migrations/loaders.py +450 -0
  214. sqlspec/migrations/runner.py +1024 -0
  215. sqlspec/migrations/templates.py +234 -0
  216. sqlspec/migrations/tracker.py +403 -0
  217. sqlspec/migrations/utils.py +256 -0
  218. sqlspec/migrations/validation.py +203 -0
  219. sqlspec/observability/__init__.py +22 -0
  220. sqlspec/observability/_config.py +228 -0
  221. sqlspec/observability/_diagnostics.py +67 -0
  222. sqlspec/observability/_dispatcher.py +151 -0
  223. sqlspec/observability/_observer.py +180 -0
  224. sqlspec/observability/_runtime.py +381 -0
  225. sqlspec/observability/_spans.py +158 -0
  226. sqlspec/protocols.py +530 -0
  227. sqlspec/py.typed +0 -0
  228. sqlspec/storage/__init__.py +46 -0
  229. sqlspec/storage/_utils.py +104 -0
  230. sqlspec/storage/backends/__init__.py +1 -0
  231. sqlspec/storage/backends/base.py +163 -0
  232. sqlspec/storage/backends/fsspec.py +398 -0
  233. sqlspec/storage/backends/local.py +377 -0
  234. sqlspec/storage/backends/obstore.py +580 -0
  235. sqlspec/storage/errors.py +104 -0
  236. sqlspec/storage/pipeline.py +604 -0
  237. sqlspec/storage/registry.py +289 -0
  238. sqlspec/typing.py +219 -0
  239. sqlspec/utils/__init__.py +31 -0
  240. sqlspec/utils/arrow_helpers.py +95 -0
  241. sqlspec/utils/config_resolver.py +153 -0
  242. sqlspec/utils/correlation.py +132 -0
  243. sqlspec/utils/data_transformation.py +114 -0
  244. sqlspec/utils/dependencies.py +79 -0
  245. sqlspec/utils/deprecation.py +113 -0
  246. sqlspec/utils/fixtures.py +250 -0
  247. sqlspec/utils/logging.py +172 -0
  248. sqlspec/utils/module_loader.py +273 -0
  249. sqlspec/utils/portal.py +325 -0
  250. sqlspec/utils/schema.py +288 -0
  251. sqlspec/utils/serializers.py +396 -0
  252. sqlspec/utils/singleton.py +41 -0
  253. sqlspec/utils/sync_tools.py +277 -0
  254. sqlspec/utils/text.py +108 -0
  255. sqlspec/utils/type_converters.py +99 -0
  256. sqlspec/utils/type_guards.py +1324 -0
  257. sqlspec/utils/version.py +444 -0
  258. sqlspec-0.32.0.dist-info/METADATA +202 -0
  259. sqlspec-0.32.0.dist-info/RECORD +262 -0
  260. sqlspec-0.32.0.dist-info/WHEEL +4 -0
  261. sqlspec-0.32.0.dist-info/entry_points.txt +2 -0
  262. sqlspec-0.32.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,835 @@
1
+ """SQL statement and configuration management."""
2
+
3
+ from typing import TYPE_CHECKING, Any, Final, Optional, TypeAlias
4
+
5
+ import sqlglot
6
+ from mypy_extensions import mypyc_attr
7
+ from sqlglot import exp
8
+ from sqlglot.errors import ParseError
9
+
10
+ import sqlspec.exceptions
11
+ from sqlspec.core.compiler import OperationProfile, OperationType
12
+ from sqlspec.core.parameters import (
13
+ ParameterConverter,
14
+ ParameterProfile,
15
+ ParameterStyle,
16
+ ParameterStyleConfig,
17
+ ParameterValidator,
18
+ )
19
+ from sqlspec.core.pipeline import compile_with_shared_pipeline
20
+ from sqlspec.typing import Empty, EmptyEnum
21
+ from sqlspec.utils.logging import get_logger
22
+ from sqlspec.utils.type_guards import is_statement_filter, supports_where
23
+
24
+ if TYPE_CHECKING:
25
+ from collections.abc import Callable
26
+
27
+ from sqlglot.dialects.dialect import DialectType
28
+
29
+ from sqlspec.core.cache import FiltersView
30
+ from sqlspec.core.filters import StatementFilter
31
+
32
+
33
+ __all__ = (
34
+ "SQL",
35
+ "ProcessedState",
36
+ "Statement",
37
+ "StatementConfig",
38
+ "get_default_config",
39
+ "get_default_parameter_config",
40
+ )
41
+ logger = get_logger("sqlspec.core.statement")
42
+
43
+ RETURNS_ROWS_OPERATIONS: Final = {"SELECT", "WITH", "VALUES", "TABLE", "SHOW", "DESCRIBE", "PRAGMA"}
44
+ MODIFYING_OPERATIONS: Final = {"INSERT", "UPDATE", "DELETE", "MERGE", "UPSERT"}
45
+
46
+ SQL_CONFIG_SLOTS: Final = (
47
+ "pre_process_steps",
48
+ "post_process_steps",
49
+ "dialect",
50
+ "enable_analysis",
51
+ "enable_caching",
52
+ "enable_expression_simplification",
53
+ "enable_parameter_type_wrapping",
54
+ "enable_parsing",
55
+ "enable_transformations",
56
+ "enable_validation",
57
+ "execution_mode",
58
+ "execution_args",
59
+ "output_transformer",
60
+ "parameter_config",
61
+ "parameter_converter",
62
+ "parameter_validator",
63
+ )
64
+
65
+ PROCESSED_STATE_SLOTS: Final = (
66
+ "compiled_sql",
67
+ "execution_parameters",
68
+ "parsed_expression",
69
+ "operation_type",
70
+ "parameter_casts",
71
+ "parameter_profile",
72
+ "operation_profile",
73
+ "validation_errors",
74
+ "is_many",
75
+ )
76
+
77
+
78
+ @mypyc_attr(allow_interpreted_subclasses=False)
79
+ class ProcessedState:
80
+ """Processing results for SQL statements.
81
+
82
+ Contains the compiled SQL, execution parameters, parsed expression,
83
+ operation type, and validation errors for a processed SQL statement.
84
+ """
85
+
86
+ __slots__ = PROCESSED_STATE_SLOTS
87
+ operation_type: "OperationType"
88
+
89
+ def __init__(
90
+ self,
91
+ compiled_sql: str,
92
+ execution_parameters: Any,
93
+ parsed_expression: "exp.Expression | None" = None,
94
+ operation_type: "OperationType" = "UNKNOWN",
95
+ parameter_casts: "dict[int, str] | None" = None,
96
+ validation_errors: "list[str] | None" = None,
97
+ parameter_profile: "ParameterProfile | None" = None,
98
+ operation_profile: "OperationProfile | None" = None,
99
+ is_many: bool = False,
100
+ ) -> None:
101
+ self.compiled_sql = compiled_sql
102
+ self.execution_parameters = execution_parameters
103
+ self.parsed_expression = parsed_expression
104
+ self.operation_type = operation_type
105
+ self.parameter_casts = parameter_casts or {}
106
+ self.validation_errors = validation_errors or []
107
+ self.parameter_profile = parameter_profile or ParameterProfile.empty()
108
+ self.operation_profile = operation_profile or OperationProfile.empty()
109
+ self.is_many = is_many
110
+
111
+ def __hash__(self) -> int:
112
+ return hash((self.compiled_sql, str(self.execution_parameters), self.operation_type))
113
+
114
+
115
+ @mypyc_attr(allow_interpreted_subclasses=False)
116
+ class SQL:
117
+ """SQL statement with parameter and filter support.
118
+
119
+ Represents a SQL statement that can be compiled with parameters and filters.
120
+ Supports both positional and named parameters, statement filtering,
121
+ and various execution modes including batch operations.
122
+ """
123
+
124
+ __slots__ = (
125
+ "_dialect",
126
+ "_filters",
127
+ "_hash",
128
+ "_is_many",
129
+ "_is_script",
130
+ "_named_parameters",
131
+ "_original_parameters",
132
+ "_positional_parameters",
133
+ "_processed_state",
134
+ "_raw_sql",
135
+ "_statement_config",
136
+ )
137
+
138
+ def __init__(
139
+ self,
140
+ statement: "str | exp.Expression | 'SQL'",
141
+ *parameters: "Any | StatementFilter | list[Any | StatementFilter]",
142
+ statement_config: Optional["StatementConfig"] = None,
143
+ is_many: bool | None = None,
144
+ **kwargs: Any,
145
+ ) -> None:
146
+ """Initialize SQL statement.
147
+
148
+ Args:
149
+ statement: SQL string, expression, or existing SQL object
150
+ *parameters: Parameters and filters
151
+ statement_config: Configuration
152
+ is_many: Mark as execute_many operation
153
+ **kwargs: Additional parameters
154
+ """
155
+ config = statement_config or self._create_auto_config(statement, parameters, kwargs)
156
+ self._statement_config = config
157
+ self._dialect = self._normalize_dialect(config.dialect)
158
+ self._processed_state: EmptyEnum | ProcessedState = Empty
159
+ self._hash: int | None = None
160
+ self._filters: list[StatementFilter] = []
161
+ self._named_parameters: dict[str, Any] = {}
162
+ self._positional_parameters: list[Any] = []
163
+ self._is_script = False
164
+
165
+ if isinstance(statement, SQL):
166
+ self._init_from_sql_object(statement)
167
+ if is_many is not None:
168
+ self._is_many = is_many
169
+ else:
170
+ if isinstance(statement, str):
171
+ self._raw_sql = statement
172
+ else:
173
+ dialect = self._dialect
174
+ self._raw_sql = statement.sql(dialect=str(dialect) if dialect else None)
175
+
176
+ self._is_many = is_many if is_many is not None else self._should_auto_detect_many(parameters)
177
+
178
+ self._original_parameters = parameters
179
+ self._process_parameters(*parameters, **kwargs)
180
+
181
+ def _create_auto_config(
182
+ self, _statement: "str | exp.Expression | 'SQL'", _parameters: tuple, _kwargs: dict[str, Any]
183
+ ) -> "StatementConfig":
184
+ """Create default StatementConfig when none provided.
185
+
186
+ Args:
187
+ _statement: The SQL statement (unused)
188
+ _parameters: Statement parameters (unused)
189
+ _kwargs: Additional keyword arguments (unused)
190
+
191
+ Returns:
192
+ Default StatementConfig instance
193
+ """
194
+ return get_default_config()
195
+
196
+ def _normalize_dialect(self, dialect: "DialectType | None") -> "str | None":
197
+ """Convert dialect to string representation.
198
+
199
+ Args:
200
+ dialect: Dialect type or string
201
+
202
+ Returns:
203
+ String representation of the dialect or None
204
+ """
205
+ if dialect is None:
206
+ return None
207
+ if isinstance(dialect, str):
208
+ return dialect
209
+ return dialect.__class__.__name__.lower()
210
+
211
+ def _init_from_sql_object(self, sql_obj: "SQL") -> None:
212
+ """Initialize instance attributes from existing SQL object.
213
+
214
+ Args:
215
+ sql_obj: Existing SQL object to copy from
216
+ """
217
+ self._raw_sql = sql_obj.raw_sql
218
+ self._filters = sql_obj.filters.copy()
219
+ self._named_parameters = sql_obj.named_parameters.copy()
220
+ self._positional_parameters = sql_obj.positional_parameters.copy()
221
+ self._is_many = sql_obj.is_many
222
+ self._is_script = sql_obj.is_script
223
+ if sql_obj.is_processed:
224
+ self._processed_state = sql_obj.get_processed_state()
225
+
226
+ def _should_auto_detect_many(self, parameters: tuple) -> bool:
227
+ """Detect execute_many mode from parameter structure.
228
+
229
+ Args:
230
+ parameters: Parameter tuple to analyze
231
+
232
+ Returns:
233
+ True if parameters indicate batch execution
234
+ """
235
+ if len(parameters) == 1 and isinstance(parameters[0], list):
236
+ param_list = parameters[0]
237
+ if param_list and all(isinstance(item, (tuple, list)) for item in param_list):
238
+ return len(param_list) > 1
239
+ return False
240
+
241
+ def _process_parameters(self, *parameters: Any, dialect: str | None = None, **kwargs: Any) -> None:
242
+ """Process and organize parameters and filters.
243
+
244
+ Args:
245
+ *parameters: Variable parameters and filters
246
+ dialect: SQL dialect override
247
+ **kwargs: Additional named parameters
248
+ """
249
+ if dialect is not None:
250
+ self._dialect = self._normalize_dialect(dialect)
251
+
252
+ if "is_script" in kwargs:
253
+ self._is_script = bool(kwargs.pop("is_script"))
254
+
255
+ filters: list[StatementFilter] = []
256
+ actual_params: list[Any] = []
257
+ for p in parameters:
258
+ if is_statement_filter(p):
259
+ filters.append(p)
260
+ else:
261
+ actual_params.append(p)
262
+
263
+ self._filters.extend(filters)
264
+
265
+ if actual_params:
266
+ param_count = len(actual_params)
267
+ if param_count == 1:
268
+ param = actual_params[0]
269
+ if isinstance(param, dict):
270
+ self._named_parameters.update(param)
271
+ elif isinstance(param, (list, tuple)):
272
+ if self._is_many:
273
+ self._positional_parameters = list(param)
274
+ else:
275
+ # For drivers with native list expansion support, each item in the tuple/list
276
+ # should be treated as a separate parameter (but preserve inner lists/arrays)
277
+ # This allows passing arrays/lists as single JSONB parameters
278
+ self._positional_parameters.extend(param)
279
+ else:
280
+ self._positional_parameters.append(param)
281
+ else:
282
+ self._positional_parameters.extend(actual_params)
283
+
284
+ self._named_parameters.update(kwargs)
285
+
286
+ @property
287
+ def sql(self) -> str:
288
+ """Get the raw SQL string."""
289
+ return self._raw_sql
290
+
291
+ @property
292
+ def raw_sql(self) -> str:
293
+ """Get raw SQL string (public API).
294
+
295
+ Returns:
296
+ The raw SQL string
297
+ """
298
+ return self._raw_sql
299
+
300
+ @property
301
+ def parameters(self) -> Any:
302
+ """Get the original parameters."""
303
+ if self._named_parameters:
304
+ return self._named_parameters
305
+ return self._positional_parameters or []
306
+
307
+ @property
308
+ def positional_parameters(self) -> "list[Any]":
309
+ """Get positional parameters (public API)."""
310
+ return self._positional_parameters or []
311
+
312
+ @property
313
+ def named_parameters(self) -> "dict[str, Any]":
314
+ """Get named parameters (public API)."""
315
+ return self._named_parameters
316
+
317
+ @property
318
+ def original_parameters(self) -> Any:
319
+ """Get original parameters (public API)."""
320
+ return self._original_parameters
321
+
322
+ @property
323
+ def operation_type(self) -> "OperationType":
324
+ """SQL operation type."""
325
+ if self._processed_state is Empty:
326
+ return "UNKNOWN"
327
+ return self._processed_state.operation_type
328
+
329
+ @property
330
+ def statement_config(self) -> "StatementConfig":
331
+ """Statement configuration."""
332
+ return self._statement_config
333
+
334
+ @property
335
+ def expression(self) -> "exp.Expression | None":
336
+ """SQLGlot expression."""
337
+ if self._processed_state is not Empty:
338
+ return self._processed_state.parsed_expression
339
+ return None
340
+
341
+ @property
342
+ def filters(self) -> "list[StatementFilter]":
343
+ """Applied filters."""
344
+ return self._filters.copy()
345
+
346
+ def get_filters_view(self) -> "FiltersView":
347
+ """Get zero-copy filters view (public API).
348
+
349
+ Returns:
350
+ Read-only view of filters without copying
351
+ """
352
+ from sqlspec.core.cache import FiltersView
353
+
354
+ return FiltersView(self._filters)
355
+
356
+ @property
357
+ def is_processed(self) -> bool:
358
+ """Check if SQL has been processed (public API)."""
359
+ return self._processed_state is not Empty
360
+
361
+ def get_processed_state(self) -> Any:
362
+ """Get processed state (public API)."""
363
+ return self._processed_state
364
+
365
+ @property
366
+ def dialect(self) -> "str | None":
367
+ """SQL dialect."""
368
+ return self._dialect
369
+
370
+ @property
371
+ def _statement(self) -> "exp.Expression | None":
372
+ """Internal SQLGlot expression."""
373
+ return self.expression
374
+
375
+ @property
376
+ def statement_expression(self) -> "exp.Expression | None":
377
+ """Get parsed statement expression (public API).
378
+
379
+ Returns:
380
+ Parsed SQLGlot expression or None if not parsed
381
+ """
382
+ if self._processed_state is not Empty:
383
+ return self._processed_state.parsed_expression
384
+ return None
385
+
386
+ @property
387
+ def is_many(self) -> bool:
388
+ """Check if this is execute_many."""
389
+ return self._is_many
390
+
391
+ @property
392
+ def is_script(self) -> bool:
393
+ """Check if this is script execution."""
394
+ return self._is_script
395
+
396
+ @property
397
+ def validation_errors(self) -> "list[str]":
398
+ """Validation errors."""
399
+ if self._processed_state is Empty:
400
+ return []
401
+ return self._processed_state.validation_errors.copy()
402
+
403
+ @property
404
+ def has_errors(self) -> bool:
405
+ """Check if there are validation errors."""
406
+ return len(self.validation_errors) > 0
407
+
408
+ def returns_rows(self) -> bool:
409
+ """Check if statement returns rows.
410
+
411
+ Returns:
412
+ True if the SQL statement returns result rows
413
+ """
414
+ if self._processed_state is Empty:
415
+ self.compile()
416
+ if self._processed_state is Empty:
417
+ return False
418
+
419
+ profile = getattr(self._processed_state, "operation_profile", None)
420
+ if profile and profile.returns_rows:
421
+ return True
422
+
423
+ op_type = self._processed_state.operation_type
424
+ if op_type in RETURNS_ROWS_OPERATIONS:
425
+ return True
426
+
427
+ if self._processed_state.parsed_expression:
428
+ expr = self._processed_state.parsed_expression
429
+ if isinstance(expr, (exp.Insert, exp.Update, exp.Delete)) and expr.args.get("returning"):
430
+ return True
431
+
432
+ return False
433
+
434
+ def is_modifying_operation(self) -> bool:
435
+ """Check if the SQL statement is a modifying operation.
436
+
437
+ Returns:
438
+ True if the operation modifies data (INSERT/UPDATE/DELETE)
439
+ """
440
+ if self._processed_state is Empty:
441
+ return False
442
+
443
+ profile = getattr(self._processed_state, "operation_profile", None)
444
+ if profile and profile.modifies_rows:
445
+ return True
446
+
447
+ op_type = self._processed_state.operation_type
448
+ if op_type in MODIFYING_OPERATIONS:
449
+ return True
450
+
451
+ if self._processed_state.parsed_expression:
452
+ return isinstance(self._processed_state.parsed_expression, (exp.Insert, exp.Update, exp.Delete, exp.Merge))
453
+
454
+ return False
455
+
456
+ def compile(self) -> tuple[str, Any]:
457
+ """Compile SQL statement with parameters.
458
+
459
+ Returns:
460
+ Tuple of compiled SQL string and execution parameters
461
+ """
462
+ if self._processed_state is Empty:
463
+ try:
464
+ config = self._statement_config
465
+ raw_sql = self._raw_sql
466
+ params = self._named_parameters or self._positional_parameters
467
+ is_many = self._is_many
468
+ compiled_result = compile_with_shared_pipeline(config, raw_sql, params, is_many=is_many)
469
+
470
+ self._processed_state = ProcessedState(
471
+ compiled_sql=compiled_result.compiled_sql,
472
+ execution_parameters=compiled_result.execution_parameters,
473
+ parsed_expression=compiled_result.expression,
474
+ operation_type=compiled_result.operation_type,
475
+ parameter_casts=compiled_result.parameter_casts,
476
+ parameter_profile=compiled_result.parameter_profile,
477
+ operation_profile=compiled_result.operation_profile,
478
+ validation_errors=[],
479
+ is_many=self._is_many,
480
+ )
481
+ except sqlspec.exceptions.SQLSpecError:
482
+ raise
483
+ except Exception as e:
484
+ self._processed_state = self._handle_compile_failure(e)
485
+
486
+ return self._processed_state.compiled_sql, self._processed_state.execution_parameters
487
+
488
+ def as_script(self) -> "SQL":
489
+ """Create copy marked for script execution.
490
+
491
+ Returns:
492
+ New SQL instance configured for script execution
493
+ """
494
+ original_params = self._original_parameters
495
+ config = self._statement_config
496
+ is_many = self._is_many
497
+ new_sql = SQL(self._raw_sql, *original_params, statement_config=config, is_many=is_many)
498
+ new_sql._named_parameters.update(self._named_parameters)
499
+ new_sql._positional_parameters = self._positional_parameters.copy()
500
+ new_sql._filters = self._filters.copy()
501
+ new_sql._is_script = True
502
+ return new_sql
503
+
504
+ def copy(
505
+ self, statement: "str | exp.Expression | None" = None, parameters: Any | None = None, **kwargs: Any
506
+ ) -> "SQL":
507
+ """Create copy with modifications.
508
+
509
+ Args:
510
+ statement: New SQL statement to use
511
+ parameters: New parameters to use
512
+ **kwargs: Additional modifications
513
+
514
+ Returns:
515
+ New SQL instance with modifications applied
516
+ """
517
+ new_sql = SQL(
518
+ statement or self._raw_sql,
519
+ *(parameters if parameters is not None else self._original_parameters),
520
+ statement_config=self._statement_config,
521
+ is_many=self._is_many,
522
+ **kwargs,
523
+ )
524
+ if parameters is None:
525
+ new_sql._named_parameters.update(self._named_parameters)
526
+ new_sql._positional_parameters = self._positional_parameters.copy()
527
+ new_sql._filters = self._filters.copy()
528
+ return new_sql
529
+
530
+ def _handle_compile_failure(self, error: Exception) -> ProcessedState:
531
+ logger.warning("Processing failed, using fallback: %s", error)
532
+ return ProcessedState(
533
+ compiled_sql=self._raw_sql,
534
+ execution_parameters=self._named_parameters or self._positional_parameters,
535
+ operation_type="UNKNOWN",
536
+ parameter_casts={},
537
+ parameter_profile=ParameterProfile.empty(),
538
+ operation_profile=OperationProfile.empty(),
539
+ is_many=self._is_many,
540
+ )
541
+
542
+ def add_named_parameter(self, name: str, value: Any) -> "SQL":
543
+ """Add a named parameter and return a new SQL instance.
544
+
545
+ Args:
546
+ name: Parameter name
547
+ value: Parameter value
548
+
549
+ Returns:
550
+ New SQL instance with the added parameter
551
+ """
552
+ original_params = self._original_parameters
553
+ config = self._statement_config
554
+ is_many = self._is_many
555
+ new_sql = SQL(self._raw_sql, *original_params, statement_config=config, is_many=is_many)
556
+ new_sql._named_parameters.update(self._named_parameters)
557
+ new_sql._named_parameters[name] = value
558
+ new_sql._positional_parameters = self._positional_parameters.copy()
559
+ new_sql._filters = self._filters.copy()
560
+ return new_sql
561
+
562
+ def where(self, condition: "str | exp.Expression") -> "SQL":
563
+ """Add WHERE condition to the SQL statement.
564
+
565
+ Args:
566
+ condition: WHERE condition as string or SQLGlot expression
567
+
568
+ Returns:
569
+ New SQL instance with the WHERE condition applied
570
+ """
571
+ try:
572
+ current_expr = sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
573
+ except ParseError:
574
+ subquery_sql = f"SELECT * FROM ({self._raw_sql}) AS subquery"
575
+ current_expr = sqlglot.parse_one(subquery_sql, dialect=self._dialect)
576
+
577
+ condition_expr: exp.Expression
578
+ if isinstance(condition, str):
579
+ try:
580
+ condition_expr = sqlglot.parse_one(condition, dialect=self._dialect, into=exp.Condition)
581
+ except ParseError:
582
+ condition_expr = exp.Condition(this=condition)
583
+ else:
584
+ condition_expr = condition
585
+
586
+ if isinstance(current_expr, exp.Select) or supports_where(current_expr):
587
+ new_expr = current_expr.where(condition_expr, copy=False)
588
+ else:
589
+ new_expr = exp.Select().from_(current_expr).where(condition_expr, copy=False)
590
+
591
+ original_params = self._original_parameters
592
+ config = self._statement_config
593
+ is_many = self._is_many
594
+ new_sql = SQL(new_expr, *original_params, statement_config=config, is_many=is_many)
595
+
596
+ new_sql._named_parameters.update(self._named_parameters)
597
+ new_sql._positional_parameters = self._positional_parameters.copy()
598
+ new_sql._filters = self._filters.copy()
599
+ return new_sql
600
+
601
+ def __hash__(self) -> int:
602
+ """Hash value computation."""
603
+ if self._hash is None:
604
+ positional_tuple = tuple(self._positional_parameters)
605
+ named_tuple = tuple(sorted(self._named_parameters.items())) if self._named_parameters else ()
606
+ raw_sql = self._raw_sql
607
+ is_many = self._is_many
608
+ is_script = self._is_script
609
+ self._hash = hash((raw_sql, positional_tuple, named_tuple, is_many, is_script))
610
+ return self._hash
611
+
612
+ def __eq__(self, other: object) -> bool:
613
+ """Equality comparison."""
614
+ if not isinstance(other, SQL):
615
+ return False
616
+ return (
617
+ self._raw_sql == other._raw_sql
618
+ and self._positional_parameters == other._positional_parameters
619
+ and self._named_parameters == other._named_parameters
620
+ and self._is_many == other._is_many
621
+ and self._is_script == other._is_script
622
+ )
623
+
624
+ def __repr__(self) -> str:
625
+ """String representation."""
626
+ params_parts = []
627
+ if self._positional_parameters:
628
+ params_parts.append(f"params={self._positional_parameters}")
629
+ if self._named_parameters:
630
+ params_parts.append(f"named_params={self._named_parameters}")
631
+ params_str = f", {', '.join(params_parts)}" if params_parts else ""
632
+
633
+ flags = []
634
+ if self._is_many:
635
+ flags.append("is_many")
636
+ if self._is_script:
637
+ flags.append("is_script")
638
+ flags_str = f", {', '.join(flags)}" if flags else ""
639
+
640
+ return f"SQL({self._raw_sql!r}{params_str}{flags_str})"
641
+
642
+
643
+ @mypyc_attr(allow_interpreted_subclasses=False)
644
+ class StatementConfig:
645
+ """Configuration for SQL statement processing.
646
+
647
+ Controls SQL parsing, validation, transformations, parameter handling,
648
+ and other processing options for SQL statements.
649
+ """
650
+
651
+ __slots__ = SQL_CONFIG_SLOTS
652
+
653
+ def __init__(
654
+ self,
655
+ parameter_config: "ParameterStyleConfig | None" = None,
656
+ enable_parsing: bool = True,
657
+ enable_validation: bool = True,
658
+ enable_transformations: bool = True,
659
+ enable_analysis: bool = False,
660
+ enable_expression_simplification: bool = False,
661
+ enable_parameter_type_wrapping: bool = True,
662
+ enable_caching: bool = True,
663
+ parameter_converter: "ParameterConverter | None" = None,
664
+ parameter_validator: "ParameterValidator | None" = None,
665
+ dialect: "DialectType | None" = None,
666
+ pre_process_steps: "list[Any] | None" = None,
667
+ post_process_steps: "list[Any] | None" = None,
668
+ execution_mode: "str | None" = None,
669
+ execution_args: "dict[str, Any] | None" = None,
670
+ output_transformer: "Callable[[str, Any], tuple[str, Any]] | None" = None,
671
+ ) -> None:
672
+ """Initialize StatementConfig.
673
+
674
+ Args:
675
+ parameter_config: Parameter style configuration
676
+ enable_parsing: Enable SQL parsing
677
+ enable_validation: Run SQL validators
678
+ enable_transformations: Apply SQL transformers
679
+ enable_analysis: Run SQL analyzers
680
+ enable_expression_simplification: Apply expression simplification
681
+ enable_parameter_type_wrapping: Wrap parameters with type information
682
+ enable_caching: Cache processed SQL statements
683
+ parameter_converter: Handles parameter style conversions
684
+ parameter_validator: Validates parameter usage and styles
685
+ dialect: SQL dialect
686
+ pre_process_steps: Optional list of preprocessing steps
687
+ post_process_steps: Optional list of postprocessing steps
688
+ execution_mode: Special execution mode
689
+ execution_args: Arguments for special execution modes
690
+ output_transformer: Optional output transformation function
691
+ """
692
+ self.enable_parsing = enable_parsing
693
+ self.enable_validation = enable_validation
694
+ self.enable_transformations = enable_transformations
695
+ self.enable_analysis = enable_analysis
696
+ self.enable_expression_simplification = enable_expression_simplification
697
+ self.enable_parameter_type_wrapping = enable_parameter_type_wrapping
698
+ self.enable_caching = enable_caching
699
+ self.parameter_converter = parameter_converter or ParameterConverter()
700
+ self.parameter_validator = parameter_validator or ParameterValidator()
701
+ self.parameter_config = parameter_config or ParameterStyleConfig(
702
+ default_parameter_style=ParameterStyle.QMARK, supported_parameter_styles={ParameterStyle.QMARK}
703
+ )
704
+
705
+ self.dialect = dialect
706
+ self.pre_process_steps = pre_process_steps
707
+ self.post_process_steps = post_process_steps
708
+ self.execution_mode = execution_mode
709
+ self.execution_args = execution_args
710
+ self.output_transformer = output_transformer
711
+
712
+ def replace(self, **kwargs: Any) -> "StatementConfig":
713
+ """Immutable update pattern.
714
+
715
+ Args:
716
+ **kwargs: Attributes to update
717
+
718
+ Returns:
719
+ New StatementConfig instance with updated attributes
720
+ """
721
+ for key in kwargs:
722
+ if key not in SQL_CONFIG_SLOTS:
723
+ msg = f"{key!r} is not a field in {type(self).__name__}"
724
+ raise TypeError(msg)
725
+
726
+ current_kwargs: dict[str, Any] = {
727
+ "parameter_config": self.parameter_config,
728
+ "enable_parsing": self.enable_parsing,
729
+ "enable_validation": self.enable_validation,
730
+ "enable_transformations": self.enable_transformations,
731
+ "enable_analysis": self.enable_analysis,
732
+ "enable_expression_simplification": self.enable_expression_simplification,
733
+ "enable_parameter_type_wrapping": self.enable_parameter_type_wrapping,
734
+ "enable_caching": self.enable_caching,
735
+ "parameter_converter": self.parameter_converter,
736
+ "parameter_validator": self.parameter_validator,
737
+ "dialect": self.dialect,
738
+ "pre_process_steps": self.pre_process_steps,
739
+ "post_process_steps": self.post_process_steps,
740
+ "execution_mode": self.execution_mode,
741
+ "execution_args": self.execution_args,
742
+ "output_transformer": self.output_transformer,
743
+ }
744
+ current_kwargs.update(kwargs)
745
+ return type(self)(**current_kwargs)
746
+
747
+ def __hash__(self) -> int:
748
+ """Hash based on configuration settings."""
749
+ return hash((
750
+ self.enable_parsing,
751
+ self.enable_validation,
752
+ self.enable_transformations,
753
+ self.enable_analysis,
754
+ self.enable_expression_simplification,
755
+ self.enable_parameter_type_wrapping,
756
+ self.enable_caching,
757
+ str(self.dialect),
758
+ ))
759
+
760
+ def __repr__(self) -> str:
761
+ """String representation of the StatementConfig instance."""
762
+ field_strs = [
763
+ f"parameter_config={self.parameter_config!r}",
764
+ f"enable_parsing={self.enable_parsing!r}",
765
+ f"enable_validation={self.enable_validation!r}",
766
+ f"enable_transformations={self.enable_transformations!r}",
767
+ f"enable_analysis={self.enable_analysis!r}",
768
+ f"enable_expression_simplification={self.enable_expression_simplification!r}",
769
+ f"enable_parameter_type_wrapping={self.enable_parameter_type_wrapping!r}",
770
+ f"enable_caching={self.enable_caching!r}",
771
+ f"parameter_converter={self.parameter_converter!r}",
772
+ f"parameter_validator={self.parameter_validator!r}",
773
+ f"dialect={self.dialect!r}",
774
+ f"pre_process_steps={self.pre_process_steps!r}",
775
+ f"post_process_steps={self.post_process_steps!r}",
776
+ f"execution_mode={self.execution_mode!r}",
777
+ f"execution_args={self.execution_args!r}",
778
+ f"output_transformer={self.output_transformer!r}",
779
+ ]
780
+ return f"{self.__class__.__name__}({', '.join(field_strs)})"
781
+
782
+ def __eq__(self, other: object) -> bool:
783
+ """Equality comparison."""
784
+ if not isinstance(other, type(self)):
785
+ return False
786
+
787
+ if not self._compare_parameter_configs(self.parameter_config, other.parameter_config):
788
+ return False
789
+
790
+ return (
791
+ self.enable_parsing == other.enable_parsing
792
+ and self.enable_validation == other.enable_validation
793
+ and self.enable_transformations == other.enable_transformations
794
+ and self.enable_analysis == other.enable_analysis
795
+ and self.enable_expression_simplification == other.enable_expression_simplification
796
+ and self.enable_parameter_type_wrapping == other.enable_parameter_type_wrapping
797
+ and self.enable_caching == other.enable_caching
798
+ and self.dialect == other.dialect
799
+ and self.pre_process_steps == other.pre_process_steps
800
+ and self.post_process_steps == other.post_process_steps
801
+ and self.execution_mode == other.execution_mode
802
+ and self.execution_args == other.execution_args
803
+ and self.output_transformer == other.output_transformer
804
+ )
805
+
806
+ def _compare_parameter_configs(self, config1: Any, config2: Any) -> bool:
807
+ """Compare parameter configs."""
808
+ return bool(
809
+ config1.default_parameter_style == config2.default_parameter_style
810
+ and config1.supported_parameter_styles == config2.supported_parameter_styles
811
+ and config1.supported_execution_parameter_styles == config2.supported_execution_parameter_styles
812
+ )
813
+
814
+
815
+ def get_default_config() -> StatementConfig:
816
+ """Get default statement configuration.
817
+
818
+ Returns:
819
+ StatementConfig with default settings
820
+ """
821
+ return StatementConfig()
822
+
823
+
824
+ def get_default_parameter_config() -> ParameterStyleConfig:
825
+ """Get default parameter configuration.
826
+
827
+ Returns:
828
+ ParameterStyleConfig with QMARK style as default
829
+ """
830
+ return ParameterStyleConfig(
831
+ default_parameter_style=ParameterStyle.QMARK, supported_parameter_styles={ParameterStyle.QMARK}
832
+ )
833
+
834
+
835
+ Statement: TypeAlias = str | exp.Expression | SQL