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
@@ -0,0 +1,140 @@
1
+ """Migration version tracking for SQLSpec.
2
+
3
+ This module provides functionality to track applied migrations in the database.
4
+ """
5
+
6
+ import os
7
+ from typing import TYPE_CHECKING, Any, Optional
8
+
9
+ from sqlspec.migrations.base import BaseMigrationTracker
10
+
11
+ if TYPE_CHECKING:
12
+ from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
13
+
14
+ __all__ = ("AsyncMigrationTracker", "SyncMigrationTracker")
15
+
16
+
17
+ class SyncMigrationTracker(BaseMigrationTracker["SyncDriverAdapterBase"]):
18
+ """Tracks applied migrations in the database."""
19
+
20
+ def ensure_tracking_table(self, driver: "SyncDriverAdapterBase") -> None:
21
+ """Create the migration tracking table if it doesn't exist.
22
+
23
+ Args:
24
+ driver: The database driver to use.
25
+ """
26
+ driver.execute(self._get_create_table_sql())
27
+
28
+ def get_current_version(self, driver: "SyncDriverAdapterBase") -> Optional[str]:
29
+ """Get the latest applied migration version.
30
+
31
+ Args:
32
+ driver: The database driver to use.
33
+
34
+ Returns:
35
+ The current version number or None if no migrations applied.
36
+ """
37
+ result = driver.execute(self._get_current_version_sql())
38
+ return result.data[0]["version_num"] if result.data else None
39
+
40
+ def get_applied_migrations(self, driver: "SyncDriverAdapterBase") -> "list[dict[str, Any]]":
41
+ """Get all applied migrations in order.
42
+
43
+ Args:
44
+ driver: The database driver to use.
45
+
46
+ Returns:
47
+ List of migration records.
48
+ """
49
+ result = driver.execute(self._get_applied_migrations_sql())
50
+ return result.data or []
51
+
52
+ def record_migration(
53
+ self, driver: "SyncDriverAdapterBase", version: str, description: str, execution_time_ms: int, checksum: str
54
+ ) -> None:
55
+ """Record a successfully applied migration.
56
+
57
+ Args:
58
+ driver: The database driver to use.
59
+ version: Version number of the migration.
60
+ description: Description of the migration.
61
+ execution_time_ms: Execution time in milliseconds.
62
+ checksum: MD5 checksum of the migration content.
63
+ """
64
+ driver.execute(
65
+ self._get_record_migration_sql(
66
+ version, description, execution_time_ms, checksum, os.environ.get("USER", "unknown")
67
+ )
68
+ )
69
+
70
+ def remove_migration(self, driver: "SyncDriverAdapterBase", version: str) -> None:
71
+ """Remove a migration record (used during downgrade).
72
+
73
+ Args:
74
+ driver: The database driver to use.
75
+ version: Version number to remove.
76
+ """
77
+ driver.execute(self._get_remove_migration_sql(version))
78
+
79
+
80
+ class AsyncMigrationTracker(BaseMigrationTracker["AsyncDriverAdapterBase"]):
81
+ """Tracks applied migrations in the database."""
82
+
83
+ async def ensure_tracking_table(self, driver: "AsyncDriverAdapterBase") -> None:
84
+ """Create the migration tracking table if it doesn't exist.
85
+
86
+ Args:
87
+ driver: The database driver to use.
88
+ """
89
+ await driver.execute(self._get_create_table_sql())
90
+
91
+ async def get_current_version(self, driver: "AsyncDriverAdapterBase") -> Optional[str]:
92
+ """Get the latest applied migration version.
93
+
94
+ Args:
95
+ driver: The database driver to use.
96
+
97
+ Returns:
98
+ The current version number or None if no migrations applied.
99
+ """
100
+ result = await driver.execute(self._get_current_version_sql())
101
+ return result.data[0]["version_num"] if result.data else None
102
+
103
+ async def get_applied_migrations(self, driver: "AsyncDriverAdapterBase") -> "list[dict[str, Any]]":
104
+ """Get all applied migrations in order.
105
+
106
+ Args:
107
+ driver: The database driver to use.
108
+
109
+ Returns:
110
+ List of migration records.
111
+ """
112
+ result = await driver.execute(self._get_applied_migrations_sql())
113
+ return result.data or []
114
+
115
+ async def record_migration(
116
+ self, driver: "AsyncDriverAdapterBase", version: str, description: str, execution_time_ms: int, checksum: str
117
+ ) -> None:
118
+ """Record a successfully applied migration.
119
+
120
+ Args:
121
+ driver: The database driver to use.
122
+ version: Version number of the migration.
123
+ description: Description of the migration.
124
+ execution_time_ms: Execution time in milliseconds.
125
+ checksum: MD5 checksum of the migration content.
126
+ """
127
+ await driver.execute(
128
+ self._get_record_migration_sql(
129
+ version, description, execution_time_ms, checksum, os.environ.get("USER", "unknown")
130
+ )
131
+ )
132
+
133
+ async def remove_migration(self, driver: "AsyncDriverAdapterBase", version: str) -> None:
134
+ """Remove a migration record (used during downgrade).
135
+
136
+ Args:
137
+ driver: The database driver to use.
138
+ version: Version number to remove.
139
+ """
140
+ await driver.execute(self._get_remove_migration_sql(version))
@@ -0,0 +1,129 @@
1
+ """Utility functions for SQLSpec migrations.
2
+
3
+ This module provides helper functions for migration operations.
4
+ """
5
+
6
+ import os
7
+ from datetime import datetime, timezone
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING, Any, Optional
10
+
11
+ if TYPE_CHECKING:
12
+ from sqlspec.driver import AsyncDriverAdapterBase
13
+
14
+ __all__ = ("create_migration_file", "drop_all", "get_author")
15
+
16
+
17
+ def create_migration_file(migrations_dir: Path, version: str, message: str, file_type: str = "sql") -> Path:
18
+ """Create a new migration file from template.
19
+
20
+ Args:
21
+ migrations_dir: Directory to create the migration in.
22
+ version: Version number for the migration.
23
+ message: Description message for the migration.
24
+ file_type: Type of migration file to create ('sql' or 'py').
25
+
26
+ Returns:
27
+ Path to the created migration file.
28
+ """
29
+ safe_message = message.lower()
30
+ safe_message = "".join(c if c.isalnum() or c in " -" else "" for c in safe_message)
31
+ safe_message = safe_message.replace(" ", "_").replace("-", "_")
32
+ safe_message = "_".join(filter(None, safe_message.split("_")))[:50]
33
+
34
+ if file_type == "py":
35
+ filename = f"{version}_{safe_message}.py"
36
+ file_path = migrations_dir / filename
37
+ template = f'''"""SQLSpec Migration - {message}
38
+
39
+ Version: {version}
40
+ Created: {datetime.now(timezone.utc).isoformat()}
41
+ Author: {get_author()}
42
+
43
+ Migration functions can use either naming convention:
44
+ - Preferred: up()/down()
45
+ - Legacy: migrate_up()/migrate_down()
46
+
47
+ Both can be synchronous or asynchronous:
48
+ - def up(): ...
49
+ - async def up(): ...
50
+ """
51
+
52
+ from typing import List, Union
53
+
54
+
55
+ def up() -> Union[str, List[str]]:
56
+ """Apply the migration (upgrade).
57
+
58
+ Returns:
59
+ SQL statement(s) to execute for upgrade.
60
+ Can return a single string or list of strings.
61
+
62
+ Note: You can use either 'up()' or 'migrate_up()' for function names.
63
+ Both support async versions: 'async def up()' or 'async def migrate_up()'
64
+ """
65
+ return """
66
+ CREATE TABLE example (
67
+ id INTEGER PRIMARY KEY,
68
+ name TEXT NOT NULL
69
+ );
70
+ """
71
+
72
+
73
+ def down() -> Union[str, List[str]]:
74
+ """Reverse the migration (downgrade).
75
+
76
+ Returns:
77
+ SQL statement(s) to execute for downgrade.
78
+ Can return a single string or list of strings.
79
+ Return empty string or empty list if downgrade is not supported.
80
+
81
+ Note: You can use either 'down()' or 'migrate_down()' for function names.
82
+ Both support async versions: 'async def down()' or 'async def migrate_down()'
83
+ """
84
+ return "DROP TABLE example;"
85
+ '''
86
+ else:
87
+ filename = f"{version}_{safe_message}.sql"
88
+ file_path = migrations_dir / filename
89
+ template = f"""-- SQLSpec Migration
90
+ -- Version: {version}
91
+ -- Description: {message}
92
+ -- Created: {datetime.now(timezone.utc).isoformat()}
93
+ -- Author: {get_author()}
94
+
95
+ -- name: migrate-{version}-up
96
+ CREATE TABLE placeholder (
97
+ id INTEGER PRIMARY KEY
98
+ );
99
+
100
+ -- name: migrate-{version}-down
101
+ DROP TABLE placeholder;
102
+ """
103
+
104
+ file_path.write_text(template)
105
+ return file_path
106
+
107
+
108
+ def get_author() -> str:
109
+ """Get current user for migration metadata.
110
+
111
+ Returns:
112
+ Username from environment or 'unknown'.
113
+ """
114
+ return os.environ.get("USER", "unknown")
115
+
116
+
117
+ async def drop_all(engine: "AsyncDriverAdapterBase", version_table_name: str, metadata: Optional[Any] = None) -> None:
118
+ """Drop all tables from the database.
119
+
120
+ Args:
121
+ engine: The database engine/driver.
122
+ version_table_name: Name of the version tracking table.
123
+ metadata: Optional metadata object.
124
+
125
+ Raises:
126
+ NotImplementedError: Always raised.
127
+ """
128
+ msg = "drop_all functionality requires database-specific implementation"
129
+ raise NotImplementedError(msg)
sqlspec/protocols.py CHANGED
@@ -4,7 +4,6 @@ This module provides protocols that can be used for static type checking
4
4
  and runtime isinstance() checks, replacing defensive hasattr() patterns.
5
5
  """
6
6
 
7
- from abc import abstractmethod
8
7
  from typing import TYPE_CHECKING, Any, ClassVar, Optional, Protocol, Union, runtime_checkable
9
8
 
10
9
  from typing_extensions import Self
@@ -15,41 +14,31 @@ if TYPE_CHECKING:
15
14
 
16
15
  from sqlglot import exp
17
16
 
18
- from sqlspec.statement.pipelines.context import SQLProcessingContext
19
17
  from sqlspec.storage.capabilities import StorageCapabilities
20
18
  from sqlspec.typing import ArrowRecordBatch, ArrowTable
21
19
 
22
20
  __all__ = (
23
- # Database Connection Protocols
24
- "AsyncCloseableConnectionProtocol",
25
- "AsyncCopyCapableConnectionProtocol",
26
- "AsyncPipelineCapableDriverProtocol",
27
- "AsyncTransactionCapableConnectionProtocol",
28
- "AsyncTransactionStateConnectionProtocol",
29
21
  "BytesConvertibleProtocol",
30
22
  "DictProtocol",
31
23
  "FilterAppenderProtocol",
32
24
  "FilterParameterProtocol",
25
+ "HasExpressionProtocol",
33
26
  "HasExpressionsProtocol",
34
27
  "HasLimitProtocol",
35
28
  "HasOffsetProtocol",
36
29
  "HasOrderByProtocol",
37
- "HasRiskLevelProtocol",
30
+ "HasParameterBuilderProtocol",
31
+ "HasSQLGlotExpressionProtocol",
38
32
  "HasSQLMethodProtocol",
33
+ "HasToStatementProtocol",
39
34
  "HasWhereProtocol",
40
35
  "IndexableRow",
41
36
  "IterableParameters",
42
37
  "ObjectStoreItemProtocol",
43
38
  "ObjectStoreProtocol",
44
39
  "ParameterValueProtocol",
45
- "ProcessorProtocol",
46
40
  "SQLBuilderProtocol",
47
41
  "SelectBuilderProtocol",
48
- "SyncCloseableConnectionProtocol",
49
- "SyncCopyCapableConnectionProtocol",
50
- "SyncPipelineCapableDriverProtocol",
51
- "SyncTransactionCapableConnectionProtocol",
52
- "SyncTransactionStateConnectionProtocol",
53
42
  "WithMethodProtocol",
54
43
  )
55
44
 
@@ -167,16 +156,6 @@ class ParameterValueProtocol(Protocol):
167
156
  type_hint: str
168
157
 
169
158
 
170
- @runtime_checkable
171
- class HasRiskLevelProtocol(Protocol):
172
- """Protocol for objects with a risk_level attribute."""
173
-
174
- @property
175
- def risk_level(self) -> Any:
176
- """Get the risk level of this object."""
177
- ...
178
-
179
-
180
159
  @runtime_checkable
181
160
  class DictProtocol(Protocol):
182
161
  """Protocol for objects with a __dict__ attribute."""
@@ -184,25 +163,6 @@ class DictProtocol(Protocol):
184
163
  __dict__: dict[str, Any]
185
164
 
186
165
 
187
- class ProcessorProtocol(Protocol):
188
- """Defines the interface for a single processing step in the SQL pipeline."""
189
-
190
- @abstractmethod
191
- def process(
192
- self, expression: "Optional[exp.Expression]", context: "SQLProcessingContext"
193
- ) -> "Optional[exp.Expression]":
194
- """Processes an SQL expression.
195
-
196
- Args:
197
- expression: The SQL expression to process.
198
- context: The SQLProcessingContext holding the current state and config.
199
-
200
- Returns:
201
- The (possibly modified) SQL expression for transformers, or None for validators/analyzers.
202
- """
203
- ...
204
-
205
-
206
166
  @runtime_checkable
207
167
  class BytesConvertibleProtocol(Protocol):
208
168
  """Protocol for objects that can be converted to bytes."""
@@ -220,138 +180,20 @@ class ObjectStoreItemProtocol(Protocol):
220
180
  key: "Optional[str]"
221
181
 
222
182
 
223
- @runtime_checkable
224
- class SyncTransactionCapableConnectionProtocol(Protocol):
225
- """Protocol for sync connections that support transactions."""
226
-
227
- def commit(self) -> None:
228
- """Commit the current transaction."""
229
- ...
230
-
231
- def rollback(self) -> None:
232
- """Rollback the current transaction."""
233
- ...
234
-
235
-
236
- @runtime_checkable
237
- class AsyncTransactionCapableConnectionProtocol(Protocol):
238
- """Protocol for async connections that support transactions."""
239
-
240
- async def commit(self) -> None:
241
- """Commit the current transaction."""
242
- ...
243
-
244
- async def rollback(self) -> None:
245
- """Rollback the current transaction."""
246
- ...
247
-
248
-
249
- @runtime_checkable
250
- class SyncTransactionStateConnectionProtocol(SyncTransactionCapableConnectionProtocol, Protocol):
251
- """Protocol for sync connections that can report transaction state."""
252
-
253
- def in_transaction(self) -> bool:
254
- """Check if connection is currently in a transaction."""
255
- ...
256
-
257
- def begin(self) -> None:
258
- """Begin a new transaction."""
259
- ...
260
-
261
-
262
- @runtime_checkable
263
- class AsyncTransactionStateConnectionProtocol(AsyncTransactionCapableConnectionProtocol, Protocol):
264
- """Protocol for async connections that can report transaction state."""
265
-
266
- def in_transaction(self) -> bool:
267
- """Check if connection is currently in a transaction."""
268
- ...
269
-
270
- async def begin(self) -> None:
271
- """Begin a new transaction."""
272
- ...
273
-
274
-
275
- @runtime_checkable
276
- class SyncCloseableConnectionProtocol(Protocol):
277
- """Protocol for sync connections that can be closed."""
278
-
279
- def close(self) -> None:
280
- """Close the connection."""
281
- ...
282
-
283
-
284
- @runtime_checkable
285
- class AsyncCloseableConnectionProtocol(Protocol):
286
- """Protocol for async connections that can be closed."""
287
-
288
- async def close(self) -> None:
289
- """Close the connection."""
290
- ...
291
-
292
-
293
- @runtime_checkable
294
- class SyncCopyCapableConnectionProtocol(Protocol):
295
- """Protocol for sync connections that support COPY operations."""
296
-
297
- def copy_from(self, table: str, file: Any, **kwargs: Any) -> None:
298
- """Copy data from file to table."""
299
- ...
300
-
301
- def copy_to(self, table: str, file: Any, **kwargs: Any) -> None:
302
- """Copy data from table to file."""
303
- ...
304
-
305
-
306
- @runtime_checkable
307
- class AsyncCopyCapableConnectionProtocol(Protocol):
308
- """Protocol for async connections that support COPY operations."""
309
-
310
- async def copy_from(self, table: str, file: Any, **kwargs: Any) -> None:
311
- """Copy data from file to table."""
312
- ...
313
-
314
- async def copy_to(self, table: str, file: Any, **kwargs: Any) -> None:
315
- """Copy data from table to file."""
316
- ...
317
-
318
-
319
- @runtime_checkable
320
- class SyncPipelineCapableDriverProtocol(Protocol):
321
- """Protocol for sync drivers that support native pipeline execution."""
322
-
323
- def _execute_pipeline_native(self, operations: list[Any], **options: Any) -> list[Any]:
324
- """Execute pipeline operations natively."""
325
- ...
326
-
327
-
328
- @runtime_checkable
329
- class AsyncPipelineCapableDriverProtocol(Protocol):
330
- """Protocol for async drivers that support native pipeline execution."""
331
-
332
- async def _execute_pipeline_native(self, operations: list[Any], **options: Any) -> list[Any]:
333
- """Execute pipeline operations natively."""
334
- ...
335
-
336
-
337
183
  @runtime_checkable
338
184
  class ObjectStoreProtocol(Protocol):
339
- """Unified protocol for object storage operations.
185
+ """Protocol for object storage operations.
340
186
 
341
- This protocol defines the interface for all storage backends with built-in
342
- instrumentation support. Backends must implement both sync and async operations
343
- where possible, with async operations suffixed with _async.
344
-
345
- All methods use 'path' terminology for consistency with object store patterns.
187
+ Defines the interface for storage backends with both sync and async operations.
346
188
  """
347
189
 
348
- # Class-level capability descriptor
349
190
  capabilities: ClassVar["StorageCapabilities"]
350
191
 
192
+ protocol: str
193
+
351
194
  def __init__(self, uri: str, **kwargs: Any) -> None:
352
195
  return
353
196
 
354
- # Core Operations (sync)
355
197
  def read_bytes(self, path: "Union[str, Path]", **kwargs: Any) -> bytes:
356
198
  """Read bytes from an object."""
357
199
  return b""
@@ -368,7 +210,6 @@ class ObjectStoreProtocol(Protocol):
368
210
  """Write text to an object."""
369
211
  return
370
212
 
371
- # Object Operations
372
213
  def exists(self, path: "Union[str, Path]", **kwargs: Any) -> bool:
373
214
  """Check if an object exists."""
374
215
  return False
@@ -385,7 +226,6 @@ class ObjectStoreProtocol(Protocol):
385
226
  """Move an object."""
386
227
  return
387
228
 
388
- # Listing Operations
389
229
  def list_objects(self, prefix: str = "", recursive: bool = True, **kwargs: Any) -> list[str]:
390
230
  """List objects with optional prefix."""
391
231
  return []
@@ -394,7 +234,6 @@ class ObjectStoreProtocol(Protocol):
394
234
  """Find objects matching a glob pattern."""
395
235
  return []
396
236
 
397
- # Path Operations
398
237
  def is_object(self, path: "Union[str, Path]") -> bool:
399
238
  """Check if path points to an object."""
400
239
  return False
@@ -407,32 +246,21 @@ class ObjectStoreProtocol(Protocol):
407
246
  """Get object metadata."""
408
247
  return {}
409
248
 
410
- # Arrow Operations
411
249
  def read_arrow(self, path: "Union[str, Path]", **kwargs: Any) -> "ArrowTable":
412
- """Read an Arrow table from storage.
413
-
414
- For obstore backend, this should use native arrow operations when available.
415
- """
250
+ """Read an Arrow table from storage."""
416
251
  msg = "Arrow reading not implemented"
417
252
  raise NotImplementedError(msg)
418
253
 
419
254
  def write_arrow(self, path: "Union[str, Path]", table: "ArrowTable", **kwargs: Any) -> None:
420
- """Write an Arrow table to storage.
421
-
422
- For obstore backend, this should use native arrow operations when available.
423
- """
255
+ """Write an Arrow table to storage."""
424
256
  msg = "Arrow writing not implemented"
425
257
  raise NotImplementedError(msg)
426
258
 
427
259
  def stream_arrow(self, pattern: str, **kwargs: Any) -> "Iterator[ArrowRecordBatch]":
428
- """Stream Arrow record batches from matching objects.
429
-
430
- For obstore backend, this should use native streaming when available.
431
- """
260
+ """Stream Arrow record batches from matching objects."""
432
261
  msg = "Arrow streaming not implemented"
433
262
  raise NotImplementedError(msg)
434
263
 
435
- # Async versions
436
264
  async def read_bytes_async(self, path: "Union[str, Path]", **kwargs: Any) -> bytes:
437
265
  """Async read bytes from an object."""
438
266
  msg = "Async operations not implemented"
@@ -501,9 +329,39 @@ class ObjectStoreProtocol(Protocol):
501
329
  raise NotImplementedError(msg)
502
330
 
503
331
 
504
- # =============================================================================
505
- # SQL Builder Protocols
506
- # =============================================================================
332
+ @runtime_checkable
333
+ class HasSQLGlotExpressionProtocol(Protocol):
334
+ """Protocol for objects with a sqlglot_expression property."""
335
+
336
+ @property
337
+ def sqlglot_expression(self) -> "Optional[exp.Expression]":
338
+ """Return the SQLGlot expression for this object."""
339
+ ...
340
+
341
+
342
+ @runtime_checkable
343
+ class HasParameterBuilderProtocol(Protocol):
344
+ """Protocol for objects that can add parameters."""
345
+
346
+ def add_parameter(self, value: Any, name: "Optional[str]" = None) -> tuple[Any, str]:
347
+ """Add a parameter to the builder."""
348
+ ...
349
+
350
+
351
+ @runtime_checkable
352
+ class HasExpressionProtocol(Protocol):
353
+ """Protocol for objects with an _expression attribute."""
354
+
355
+ _expression: "Optional[exp.Expression]"
356
+
357
+
358
+ @runtime_checkable
359
+ class HasToStatementProtocol(Protocol):
360
+ """Protocol for objects with a to_statement method."""
361
+
362
+ def to_statement(self) -> Any:
363
+ """Convert to SQL statement."""
364
+ ...
507
365
 
508
366
 
509
367
  @runtime_checkable
@@ -513,6 +371,9 @@ class SQLBuilderProtocol(Protocol):
513
371
  _expression: "Optional[exp.Expression]"
514
372
  _parameters: dict[str, Any]
515
373
  _parameter_counter: int
374
+ _columns: Any # Optional attribute for some builders
375
+ _table: Any # Optional attribute for some builders
376
+ _with_ctes: Any # Optional attribute for some builders
516
377
  dialect: Any
517
378
  dialect_name: "Optional[str]"
518
379
 
@@ -525,6 +386,10 @@ class SQLBuilderProtocol(Protocol):
525
386
  """Add a parameter to the builder."""
526
387
  ...
527
388
 
389
+ def _generate_unique_parameter_name(self, base_name: str) -> str:
390
+ """Generate a unique parameter name."""
391
+ ...
392
+
528
393
  def _parameterize_expression(self, expression: "exp.Expression") -> "exp.Expression":
529
394
  """Replace literal values in an expression with bound parameters."""
530
395
  ...
@@ -1,6 +1,6 @@
1
1
  """Storage abstraction layer for SQLSpec.
2
2
 
3
- This module provides a flexible storage system with:
3
+ Provides a flexible storage system with:
4
4
  - Multiple backend support (local, fsspec, obstore)
5
5
  - Lazy loading and configuration-based registration
6
6
  - URI scheme-based automatic backend resolution