sqlspec 0.13.1__py3-none-any.whl → 0.16.2__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (185) hide show
  1. sqlspec/__init__.py +71 -8
  2. sqlspec/__main__.py +12 -0
  3. sqlspec/__metadata__.py +1 -3
  4. sqlspec/_serialization.py +1 -2
  5. sqlspec/_sql.py +930 -136
  6. sqlspec/_typing.py +278 -142
  7. sqlspec/adapters/adbc/__init__.py +4 -3
  8. sqlspec/adapters/adbc/_types.py +12 -0
  9. sqlspec/adapters/adbc/config.py +116 -285
  10. sqlspec/adapters/adbc/driver.py +462 -340
  11. sqlspec/adapters/aiosqlite/__init__.py +18 -3
  12. sqlspec/adapters/aiosqlite/_types.py +13 -0
  13. sqlspec/adapters/aiosqlite/config.py +202 -150
  14. sqlspec/adapters/aiosqlite/driver.py +226 -247
  15. sqlspec/adapters/asyncmy/__init__.py +18 -3
  16. sqlspec/adapters/asyncmy/_types.py +12 -0
  17. sqlspec/adapters/asyncmy/config.py +80 -199
  18. sqlspec/adapters/asyncmy/driver.py +257 -215
  19. sqlspec/adapters/asyncpg/__init__.py +19 -4
  20. sqlspec/adapters/asyncpg/_types.py +17 -0
  21. sqlspec/adapters/asyncpg/config.py +81 -214
  22. sqlspec/adapters/asyncpg/driver.py +284 -359
  23. sqlspec/adapters/bigquery/__init__.py +17 -3
  24. sqlspec/adapters/bigquery/_types.py +12 -0
  25. sqlspec/adapters/bigquery/config.py +191 -299
  26. sqlspec/adapters/bigquery/driver.py +474 -634
  27. sqlspec/adapters/duckdb/__init__.py +14 -3
  28. sqlspec/adapters/duckdb/_types.py +12 -0
  29. sqlspec/adapters/duckdb/config.py +414 -397
  30. sqlspec/adapters/duckdb/driver.py +342 -393
  31. sqlspec/adapters/oracledb/__init__.py +19 -5
  32. sqlspec/adapters/oracledb/_types.py +14 -0
  33. sqlspec/adapters/oracledb/config.py +123 -458
  34. sqlspec/adapters/oracledb/driver.py +505 -531
  35. sqlspec/adapters/psqlpy/__init__.py +13 -3
  36. sqlspec/adapters/psqlpy/_types.py +11 -0
  37. sqlspec/adapters/psqlpy/config.py +93 -307
  38. sqlspec/adapters/psqlpy/driver.py +504 -213
  39. sqlspec/adapters/psycopg/__init__.py +19 -5
  40. sqlspec/adapters/psycopg/_types.py +17 -0
  41. sqlspec/adapters/psycopg/config.py +143 -472
  42. sqlspec/adapters/psycopg/driver.py +704 -825
  43. sqlspec/adapters/sqlite/__init__.py +14 -3
  44. sqlspec/adapters/sqlite/_types.py +11 -0
  45. sqlspec/adapters/sqlite/config.py +208 -142
  46. sqlspec/adapters/sqlite/driver.py +263 -278
  47. sqlspec/base.py +105 -9
  48. sqlspec/{statement/builder → builder}/__init__.py +12 -14
  49. sqlspec/{statement/builder/base.py → builder/_base.py} +184 -86
  50. sqlspec/{statement/builder/column.py → builder/_column.py} +97 -60
  51. sqlspec/{statement/builder/ddl.py → builder/_ddl.py} +61 -131
  52. sqlspec/{statement/builder → builder}/_ddl_utils.py +4 -10
  53. sqlspec/{statement/builder/delete.py → builder/_delete.py} +10 -30
  54. sqlspec/builder/_insert.py +421 -0
  55. sqlspec/builder/_merge.py +71 -0
  56. sqlspec/{statement/builder → builder}/_parsing_utils.py +49 -26
  57. sqlspec/builder/_select.py +170 -0
  58. sqlspec/{statement/builder/update.py → builder/_update.py} +16 -20
  59. sqlspec/builder/mixins/__init__.py +55 -0
  60. sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
  61. sqlspec/{statement/builder/mixins/_delete_from.py → builder/mixins/_delete_operations.py} +8 -1
  62. sqlspec/builder/mixins/_insert_operations.py +244 -0
  63. sqlspec/{statement/builder/mixins/_join.py → builder/mixins/_join_operations.py} +45 -13
  64. sqlspec/{statement/builder/mixins/_merge_clauses.py → builder/mixins/_merge_operations.py} +188 -30
  65. sqlspec/builder/mixins/_order_limit_operations.py +135 -0
  66. sqlspec/builder/mixins/_pivot_operations.py +153 -0
  67. sqlspec/builder/mixins/_select_operations.py +604 -0
  68. sqlspec/builder/mixins/_update_operations.py +202 -0
  69. sqlspec/builder/mixins/_where_clause.py +644 -0
  70. sqlspec/cli.py +247 -0
  71. sqlspec/config.py +183 -138
  72. sqlspec/core/__init__.py +63 -0
  73. sqlspec/core/cache.py +871 -0
  74. sqlspec/core/compiler.py +417 -0
  75. sqlspec/core/filters.py +830 -0
  76. sqlspec/core/hashing.py +310 -0
  77. sqlspec/core/parameters.py +1237 -0
  78. sqlspec/core/result.py +677 -0
  79. sqlspec/{statement → core}/splitter.py +321 -191
  80. sqlspec/core/statement.py +676 -0
  81. sqlspec/driver/__init__.py +7 -10
  82. sqlspec/driver/_async.py +422 -163
  83. sqlspec/driver/_common.py +545 -287
  84. sqlspec/driver/_sync.py +426 -160
  85. sqlspec/driver/mixins/__init__.py +2 -13
  86. sqlspec/driver/mixins/_result_tools.py +193 -0
  87. sqlspec/driver/mixins/_sql_translator.py +65 -14
  88. sqlspec/exceptions.py +5 -252
  89. sqlspec/extensions/aiosql/adapter.py +93 -96
  90. sqlspec/extensions/litestar/__init__.py +2 -1
  91. sqlspec/extensions/litestar/cli.py +48 -0
  92. sqlspec/extensions/litestar/config.py +0 -1
  93. sqlspec/extensions/litestar/handlers.py +15 -26
  94. sqlspec/extensions/litestar/plugin.py +21 -16
  95. sqlspec/extensions/litestar/providers.py +17 -52
  96. sqlspec/loader.py +423 -104
  97. sqlspec/migrations/__init__.py +35 -0
  98. sqlspec/migrations/base.py +414 -0
  99. sqlspec/migrations/commands.py +443 -0
  100. sqlspec/migrations/loaders.py +402 -0
  101. sqlspec/migrations/runner.py +213 -0
  102. sqlspec/migrations/tracker.py +140 -0
  103. sqlspec/migrations/utils.py +129 -0
  104. sqlspec/protocols.py +51 -186
  105. sqlspec/storage/__init__.py +1 -1
  106. sqlspec/storage/backends/base.py +37 -40
  107. sqlspec/storage/backends/fsspec.py +136 -112
  108. sqlspec/storage/backends/obstore.py +138 -160
  109. sqlspec/storage/capabilities.py +5 -4
  110. sqlspec/storage/registry.py +57 -106
  111. sqlspec/typing.py +136 -115
  112. sqlspec/utils/__init__.py +2 -2
  113. sqlspec/utils/correlation.py +0 -3
  114. sqlspec/utils/deprecation.py +6 -6
  115. sqlspec/utils/fixtures.py +6 -6
  116. sqlspec/utils/logging.py +0 -2
  117. sqlspec/utils/module_loader.py +7 -12
  118. sqlspec/utils/singleton.py +0 -1
  119. sqlspec/utils/sync_tools.py +17 -38
  120. sqlspec/utils/text.py +12 -51
  121. sqlspec/utils/type_guards.py +482 -235
  122. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/METADATA +7 -2
  123. sqlspec-0.16.2.dist-info/RECORD +134 -0
  124. sqlspec-0.16.2.dist-info/entry_points.txt +2 -0
  125. sqlspec/driver/connection.py +0 -207
  126. sqlspec/driver/mixins/_csv_writer.py +0 -91
  127. sqlspec/driver/mixins/_pipeline.py +0 -512
  128. sqlspec/driver/mixins/_result_utils.py +0 -140
  129. sqlspec/driver/mixins/_storage.py +0 -926
  130. sqlspec/driver/mixins/_type_coercion.py +0 -130
  131. sqlspec/driver/parameters.py +0 -138
  132. sqlspec/service/__init__.py +0 -4
  133. sqlspec/service/_util.py +0 -147
  134. sqlspec/service/base.py +0 -1131
  135. sqlspec/service/pagination.py +0 -26
  136. sqlspec/statement/__init__.py +0 -21
  137. sqlspec/statement/builder/insert.py +0 -288
  138. sqlspec/statement/builder/merge.py +0 -95
  139. sqlspec/statement/builder/mixins/__init__.py +0 -65
  140. sqlspec/statement/builder/mixins/_aggregate_functions.py +0 -250
  141. sqlspec/statement/builder/mixins/_case_builder.py +0 -91
  142. sqlspec/statement/builder/mixins/_common_table_expr.py +0 -90
  143. sqlspec/statement/builder/mixins/_from.py +0 -63
  144. sqlspec/statement/builder/mixins/_group_by.py +0 -118
  145. sqlspec/statement/builder/mixins/_having.py +0 -35
  146. sqlspec/statement/builder/mixins/_insert_from_select.py +0 -47
  147. sqlspec/statement/builder/mixins/_insert_into.py +0 -36
  148. sqlspec/statement/builder/mixins/_insert_values.py +0 -67
  149. sqlspec/statement/builder/mixins/_limit_offset.py +0 -53
  150. sqlspec/statement/builder/mixins/_order_by.py +0 -46
  151. sqlspec/statement/builder/mixins/_pivot.py +0 -79
  152. sqlspec/statement/builder/mixins/_returning.py +0 -37
  153. sqlspec/statement/builder/mixins/_select_columns.py +0 -61
  154. sqlspec/statement/builder/mixins/_set_ops.py +0 -122
  155. sqlspec/statement/builder/mixins/_unpivot.py +0 -77
  156. sqlspec/statement/builder/mixins/_update_from.py +0 -55
  157. sqlspec/statement/builder/mixins/_update_set.py +0 -94
  158. sqlspec/statement/builder/mixins/_update_table.py +0 -29
  159. sqlspec/statement/builder/mixins/_where.py +0 -401
  160. sqlspec/statement/builder/mixins/_window_functions.py +0 -86
  161. sqlspec/statement/builder/select.py +0 -221
  162. sqlspec/statement/filters.py +0 -596
  163. sqlspec/statement/parameter_manager.py +0 -220
  164. sqlspec/statement/parameters.py +0 -867
  165. sqlspec/statement/pipelines/__init__.py +0 -210
  166. sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
  167. sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
  168. sqlspec/statement/pipelines/context.py +0 -115
  169. sqlspec/statement/pipelines/transformers/__init__.py +0 -7
  170. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
  171. sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
  172. sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
  173. sqlspec/statement/pipelines/validators/__init__.py +0 -23
  174. sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
  175. sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
  176. sqlspec/statement/pipelines/validators/_performance.py +0 -718
  177. sqlspec/statement/pipelines/validators/_security.py +0 -967
  178. sqlspec/statement/result.py +0 -435
  179. sqlspec/statement/sql.py +0 -1704
  180. sqlspec/statement/sql_compiler.py +0 -140
  181. sqlspec/utils/cached_property.py +0 -25
  182. sqlspec-0.13.1.dist-info/RECORD +0 -150
  183. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/WHEEL +0 -0
  184. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/LICENSE +0 -0
  185. {sqlspec-0.13.1.dist-info → sqlspec-0.16.2.dist-info}/licenses/NOTICE +0 -0
@@ -1,221 +0,0 @@
1
- """Safe SQL query builder with validation and parameter binding.
2
-
3
- This module provides a fluent interface for building SQL queries safely,
4
- with automatic parameter binding and validation.
5
- """
6
-
7
- import re
8
- from dataclasses import dataclass, field
9
- from typing import Any, Optional, Union, cast
10
-
11
- from sqlglot import exp
12
- from typing_extensions import Self
13
-
14
- from sqlspec.statement.builder.base import QueryBuilder, SafeQuery
15
- from sqlspec.statement.builder.mixins import (
16
- AggregateFunctionsMixin,
17
- CaseBuilderMixin,
18
- CommonTableExpressionMixin,
19
- FromClauseMixin,
20
- GroupByClauseMixin,
21
- HavingClauseMixin,
22
- JoinClauseMixin,
23
- LimitOffsetClauseMixin,
24
- OrderByClauseMixin,
25
- PivotClauseMixin,
26
- SelectColumnsMixin,
27
- SetOperationMixin,
28
- UnpivotClauseMixin,
29
- WhereClauseMixin,
30
- WindowFunctionsMixin,
31
- )
32
- from sqlspec.statement.result import SQLResult
33
- from sqlspec.typing import RowT
34
-
35
- __all__ = ("Select",)
36
-
37
-
38
- @dataclass
39
- class Select(
40
- QueryBuilder[RowT],
41
- WhereClauseMixin,
42
- OrderByClauseMixin,
43
- LimitOffsetClauseMixin,
44
- SelectColumnsMixin,
45
- JoinClauseMixin,
46
- FromClauseMixin,
47
- GroupByClauseMixin,
48
- HavingClauseMixin,
49
- SetOperationMixin,
50
- CommonTableExpressionMixin,
51
- AggregateFunctionsMixin,
52
- WindowFunctionsMixin,
53
- CaseBuilderMixin,
54
- PivotClauseMixin,
55
- UnpivotClauseMixin,
56
- ):
57
- """Type-safe builder for SELECT queries with schema/model integration.
58
-
59
- This builder provides a fluent, safe interface for constructing SQL SELECT statements.
60
- It supports type-safe result mapping via the `as_schema()` method, allowing users to
61
- associate a schema/model (such as a Pydantic model, dataclass, or msgspec.Struct) with
62
- the query for static type checking and IDE support.
63
-
64
- Example:
65
- >>> class User(BaseModel):
66
- ... id: int
67
- ... name: str
68
- >>> builder = (
69
- ... SelectBuilder()
70
- ... .select("id", "name")
71
- ... .from_("users")
72
- ... .as_schema(User)
73
- ... )
74
- >>> result: list[User] = driver.execute(builder)
75
-
76
- Attributes:
77
- _schema: The schema/model class for row typing, if set via as_schema().
78
- """
79
-
80
- _with_parts: "dict[str, Union[exp.CTE, Select]]" = field(default_factory=dict, init=False)
81
- _expression: Optional[exp.Expression] = field(default=None, init=False, repr=False, compare=False, hash=False)
82
- _schema: Optional[type[RowT]] = None
83
- _hints: "list[dict[str, object]]" = field(default_factory=list, init=False, repr=False)
84
-
85
- def __init__(self, *columns: str, **kwargs: Any) -> None:
86
- """Initialize SELECT with optional columns.
87
-
88
- Args:
89
- *columns: Column names to select (e.g., "id", "name", "u.email")
90
- **kwargs: Additional QueryBuilder arguments (dialect, schema, etc.)
91
-
92
- Examples:
93
- Select("id", "name") # Shorthand for Select().select("id", "name")
94
- Select() # Same as SelectBuilder() - start empty
95
- """
96
- super().__init__(**kwargs)
97
-
98
- # Initialize fields from dataclass
99
- self._with_parts = {}
100
- self._expression = None
101
- self._schema = None
102
- self._hints = []
103
-
104
- if self._expression is None:
105
- self._create_base_expression()
106
-
107
- # Add columns if provided - just a shorthand for .select()
108
- if columns:
109
- self.select(*columns)
110
-
111
- @property
112
- def _expected_result_type(self) -> "type[SQLResult[RowT]]":
113
- """Get the expected result type for SELECT operations.
114
-
115
- Returns:
116
- type: The SelectResult type.
117
- """
118
- return SQLResult[RowT]
119
-
120
- def _create_base_expression(self) -> "exp.Select":
121
- if self._expression is None or not isinstance(self._expression, exp.Select):
122
- self._expression = exp.Select()
123
- # At this point, self._expression is exp.Select
124
- return self._expression
125
-
126
- def as_schema(self, schema: "type[RowT]") -> "Select[RowT]":
127
- """Return a new Select instance parameterized with the given schema/model type.
128
-
129
- This enables type-safe result mapping: the returned builder will carry the schema type
130
- for static analysis and IDE autocompletion. The schema should be a class such as a Pydantic
131
- model, dataclass, or msgspec.Struct that describes the expected row shape.
132
-
133
- Args:
134
- schema: The schema/model class to use for row typing (e.g., a Pydantic model, dataclass, or msgspec.Struct).
135
-
136
- Returns:
137
- Select[RowT]: A new Select instance with RowT set to the provided schema/model type.
138
- """
139
- new_builder = Select()
140
- new_builder._expression = self._expression.copy() if self._expression is not None else None
141
- new_builder._parameters = self._parameters.copy()
142
- new_builder._parameter_counter = self._parameter_counter
143
- new_builder.dialect = self.dialect
144
- new_builder._schema = schema # type: ignore[assignment]
145
- return cast("Select[RowT]", new_builder)
146
-
147
- def with_hint(
148
- self,
149
- hint: "str",
150
- *,
151
- location: "str" = "statement",
152
- table: "Optional[str]" = None,
153
- dialect: "Optional[str]" = None,
154
- ) -> "Self":
155
- """Attach an optimizer or dialect-specific hint to the query.
156
-
157
- Args:
158
- hint: The raw hint string (e.g., 'INDEX(users idx_users_name)').
159
- location: Where to apply the hint ('statement', 'table').
160
- table: Table name if the hint is for a specific table.
161
- dialect: Restrict the hint to a specific dialect (optional).
162
-
163
- Returns:
164
- The current builder instance for method chaining.
165
- """
166
- self._hints.append({"hint": hint, "location": location, "table": table, "dialect": dialect})
167
- return self
168
-
169
- def build(self) -> "SafeQuery":
170
- """Builds the SQL query string and parameters with hint injection.
171
-
172
- Returns:
173
- SafeQuery: A dataclass containing the SQL string and parameters.
174
- """
175
- safe_query = super().build()
176
-
177
- if hasattr(self, "_hints") and self._hints:
178
- modified_expr = self._expression.copy() if self._expression else None
179
-
180
- if modified_expr and isinstance(modified_expr, exp.Select):
181
- statement_hints = [h["hint"] for h in self._hints if h.get("location") == "statement"]
182
- if statement_hints:
183
- # Parse each hint and create proper hint expressions
184
- hint_expressions = []
185
- for hint in statement_hints:
186
- try:
187
- # Try to parse hint as an expression (e.g., "INDEX(users idx_name)")
188
- hint_str = str(hint) # Ensure hint is a string
189
- hint_expr: Optional[exp.Expression] = exp.maybe_parse(hint_str, dialect=self.dialect_name)
190
- if hint_expr:
191
- hint_expressions.append(hint_expr)
192
- else:
193
- hint_expressions.append(exp.Anonymous(this=hint_str))
194
- except Exception: # noqa: PERF203
195
- hint_expressions.append(exp.Anonymous(this=str(hint)))
196
-
197
- if hint_expressions:
198
- hint_node = exp.Hint(expressions=hint_expressions)
199
- modified_expr.set("hint", hint_node)
200
-
201
- # For table-level hints, we'll fall back to comment injection in SQL
202
- # since SQLGlot doesn't have a standard way to attach hints to individual tables
203
- modified_sql = modified_expr.sql(dialect=self.dialect_name, pretty=True)
204
-
205
- table_hints = [h for h in self._hints if h.get("location") == "table" and h.get("table")]
206
- if table_hints:
207
- for th in table_hints:
208
- table = str(th["table"])
209
- hint = th["hint"]
210
- # More precise regex that captures the table and optional alias
211
- pattern = rf"\b{re.escape(table)}\b(\s+AS\s+\w+)?"
212
-
213
- def replacement_func(match: re.Match[str]) -> str:
214
- alias_part = match.group(1) or ""
215
- return f"/*+ {hint} */ {table}{alias_part}" # noqa: B023
216
-
217
- modified_sql = re.sub(pattern, replacement_func, modified_sql, flags=re.IGNORECASE, count=1)
218
-
219
- return SafeQuery(sql=modified_sql, parameters=safe_query.parameters, dialect=safe_query.dialect)
220
-
221
- return safe_query