sqlspec 0.26.0__py3-none-any.whl → 0.27.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (197) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +55 -25
  3. sqlspec/_typing.py +62 -52
  4. sqlspec/adapters/adbc/_types.py +1 -1
  5. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  6. sqlspec/adapters/adbc/adk/store.py +870 -0
  7. sqlspec/adapters/adbc/config.py +62 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +52 -2
  9. sqlspec/adapters/adbc/driver.py +144 -45
  10. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  11. sqlspec/adapters/adbc/litestar/store.py +504 -0
  12. sqlspec/adapters/adbc/type_converter.py +44 -50
  13. sqlspec/adapters/aiosqlite/_types.py +1 -1
  14. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  15. sqlspec/adapters/aiosqlite/adk/store.py +527 -0
  16. sqlspec/adapters/aiosqlite/config.py +86 -16
  17. sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
  18. sqlspec/adapters/aiosqlite/driver.py +127 -38
  19. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  21. sqlspec/adapters/aiosqlite/pool.py +7 -7
  22. sqlspec/adapters/asyncmy/__init__.py +7 -1
  23. sqlspec/adapters/asyncmy/_types.py +1 -1
  24. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  25. sqlspec/adapters/asyncmy/adk/store.py +493 -0
  26. sqlspec/adapters/asyncmy/config.py +59 -17
  27. sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
  28. sqlspec/adapters/asyncmy/driver.py +293 -62
  29. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  31. sqlspec/adapters/asyncpg/__init__.py +2 -1
  32. sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
  33. sqlspec/adapters/asyncpg/_types.py +11 -7
  34. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  35. sqlspec/adapters/asyncpg/adk/store.py +450 -0
  36. sqlspec/adapters/asyncpg/config.py +57 -36
  37. sqlspec/adapters/asyncpg/data_dictionary.py +41 -2
  38. sqlspec/adapters/asyncpg/driver.py +153 -23
  39. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  41. sqlspec/adapters/bigquery/_types.py +1 -1
  42. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  43. sqlspec/adapters/bigquery/adk/store.py +576 -0
  44. sqlspec/adapters/bigquery/config.py +25 -11
  45. sqlspec/adapters/bigquery/data_dictionary.py +42 -2
  46. sqlspec/adapters/bigquery/driver.py +352 -144
  47. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  48. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  49. sqlspec/adapters/bigquery/type_converter.py +55 -23
  50. sqlspec/adapters/duckdb/_types.py +2 -2
  51. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  52. sqlspec/adapters/duckdb/adk/store.py +553 -0
  53. sqlspec/adapters/duckdb/config.py +79 -21
  54. sqlspec/adapters/duckdb/data_dictionary.py +41 -2
  55. sqlspec/adapters/duckdb/driver.py +138 -43
  56. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  57. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  58. sqlspec/adapters/duckdb/pool.py +5 -5
  59. sqlspec/adapters/duckdb/type_converter.py +51 -21
  60. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  61. sqlspec/adapters/oracledb/_types.py +20 -2
  62. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  63. sqlspec/adapters/oracledb/adk/store.py +1745 -0
  64. sqlspec/adapters/oracledb/config.py +120 -36
  65. sqlspec/adapters/oracledb/data_dictionary.py +87 -20
  66. sqlspec/adapters/oracledb/driver.py +292 -84
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +767 -0
  69. sqlspec/adapters/oracledb/migrations.py +316 -25
  70. sqlspec/adapters/oracledb/type_converter.py +91 -16
  71. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  72. sqlspec/adapters/psqlpy/_types.py +2 -1
  73. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  74. sqlspec/adapters/psqlpy/adk/store.py +482 -0
  75. sqlspec/adapters/psqlpy/config.py +45 -19
  76. sqlspec/adapters/psqlpy/data_dictionary.py +41 -2
  77. sqlspec/adapters/psqlpy/driver.py +101 -31
  78. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  79. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  80. sqlspec/adapters/psqlpy/type_converter.py +40 -11
  81. sqlspec/adapters/psycopg/_type_handlers.py +80 -0
  82. sqlspec/adapters/psycopg/_types.py +2 -1
  83. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  84. sqlspec/adapters/psycopg/adk/store.py +944 -0
  85. sqlspec/adapters/psycopg/config.py +65 -37
  86. sqlspec/adapters/psycopg/data_dictionary.py +77 -3
  87. sqlspec/adapters/psycopg/driver.py +200 -78
  88. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  90. sqlspec/adapters/sqlite/__init__.py +2 -1
  91. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  92. sqlspec/adapters/sqlite/_types.py +1 -1
  93. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  94. sqlspec/adapters/sqlite/adk/store.py +572 -0
  95. sqlspec/adapters/sqlite/config.py +85 -16
  96. sqlspec/adapters/sqlite/data_dictionary.py +34 -2
  97. sqlspec/adapters/sqlite/driver.py +120 -52
  98. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  99. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  100. sqlspec/adapters/sqlite/pool.py +5 -5
  101. sqlspec/base.py +45 -26
  102. sqlspec/builder/__init__.py +73 -4
  103. sqlspec/builder/_base.py +91 -58
  104. sqlspec/builder/_column.py +5 -5
  105. sqlspec/builder/_ddl.py +98 -89
  106. sqlspec/builder/_delete.py +5 -4
  107. sqlspec/builder/_dml.py +388 -0
  108. sqlspec/{_sql.py → builder/_factory.py} +41 -44
  109. sqlspec/builder/_insert.py +5 -82
  110. sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
  111. sqlspec/builder/_merge.py +446 -11
  112. sqlspec/builder/_parsing_utils.py +9 -11
  113. sqlspec/builder/_select.py +1313 -25
  114. sqlspec/builder/_update.py +11 -42
  115. sqlspec/cli.py +76 -69
  116. sqlspec/config.py +231 -60
  117. sqlspec/core/__init__.py +5 -4
  118. sqlspec/core/cache.py +18 -18
  119. sqlspec/core/compiler.py +6 -8
  120. sqlspec/core/filters.py +37 -37
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +76 -45
  123. sqlspec/core/result.py +102 -46
  124. sqlspec/core/splitter.py +16 -17
  125. sqlspec/core/statement.py +32 -31
  126. sqlspec/core/type_conversion.py +3 -2
  127. sqlspec/driver/__init__.py +1 -3
  128. sqlspec/driver/_async.py +95 -161
  129. sqlspec/driver/_common.py +133 -80
  130. sqlspec/driver/_sync.py +95 -162
  131. sqlspec/driver/mixins/_result_tools.py +20 -236
  132. sqlspec/driver/mixins/_sql_translator.py +4 -4
  133. sqlspec/exceptions.py +70 -7
  134. sqlspec/extensions/adk/__init__.py +53 -0
  135. sqlspec/extensions/adk/_types.py +51 -0
  136. sqlspec/extensions/adk/converters.py +172 -0
  137. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  138. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  139. sqlspec/extensions/adk/service.py +181 -0
  140. sqlspec/extensions/adk/store.py +536 -0
  141. sqlspec/extensions/aiosql/adapter.py +73 -53
  142. sqlspec/extensions/litestar/__init__.py +21 -4
  143. sqlspec/extensions/litestar/cli.py +54 -10
  144. sqlspec/extensions/litestar/config.py +59 -266
  145. sqlspec/extensions/litestar/handlers.py +46 -17
  146. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  147. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  148. sqlspec/extensions/litestar/plugin.py +324 -223
  149. sqlspec/extensions/litestar/providers.py +25 -25
  150. sqlspec/extensions/litestar/store.py +265 -0
  151. sqlspec/loader.py +30 -49
  152. sqlspec/migrations/base.py +200 -76
  153. sqlspec/migrations/commands.py +591 -62
  154. sqlspec/migrations/context.py +6 -9
  155. sqlspec/migrations/fix.py +199 -0
  156. sqlspec/migrations/loaders.py +47 -19
  157. sqlspec/migrations/runner.py +241 -75
  158. sqlspec/migrations/tracker.py +237 -21
  159. sqlspec/migrations/utils.py +51 -3
  160. sqlspec/migrations/validation.py +177 -0
  161. sqlspec/protocols.py +66 -36
  162. sqlspec/storage/_utils.py +98 -0
  163. sqlspec/storage/backends/fsspec.py +134 -106
  164. sqlspec/storage/backends/local.py +78 -51
  165. sqlspec/storage/backends/obstore.py +278 -162
  166. sqlspec/storage/registry.py +75 -39
  167. sqlspec/typing.py +14 -84
  168. sqlspec/utils/config_resolver.py +6 -6
  169. sqlspec/utils/correlation.py +4 -5
  170. sqlspec/utils/data_transformation.py +3 -2
  171. sqlspec/utils/deprecation.py +9 -8
  172. sqlspec/utils/fixtures.py +4 -4
  173. sqlspec/utils/logging.py +46 -6
  174. sqlspec/utils/module_loader.py +2 -2
  175. sqlspec/utils/schema.py +288 -0
  176. sqlspec/utils/serializers.py +3 -3
  177. sqlspec/utils/sync_tools.py +21 -17
  178. sqlspec/utils/text.py +1 -2
  179. sqlspec/utils/type_guards.py +111 -20
  180. sqlspec/utils/version.py +433 -0
  181. {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
  182. sqlspec-0.27.0.dist-info/RECORD +207 -0
  183. sqlspec/builder/mixins/__init__.py +0 -55
  184. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
  185. sqlspec/builder/mixins/_delete_operations.py +0 -50
  186. sqlspec/builder/mixins/_insert_operations.py +0 -282
  187. sqlspec/builder/mixins/_merge_operations.py +0 -698
  188. sqlspec/builder/mixins/_order_limit_operations.py +0 -145
  189. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  190. sqlspec/builder/mixins/_select_operations.py +0 -930
  191. sqlspec/builder/mixins/_update_operations.py +0 -199
  192. sqlspec/builder/mixins/_where_clause.py +0 -1298
  193. sqlspec-0.26.0.dist-info/RECORD +0 -157
  194. sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
  195. {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
  196. {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
  197. {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,24 +4,21 @@ Provides a fluent interface for building SQL UPDATE queries with
4
4
  parameter binding and validation.
5
5
  """
6
6
 
7
- from typing import TYPE_CHECKING, Any, Optional, Union
7
+ from typing import TYPE_CHECKING, Any, cast
8
8
 
9
9
  from sqlglot import exp
10
10
  from typing_extensions import Self
11
11
 
12
12
  from sqlspec.builder._base import QueryBuilder, SafeQuery
13
- from sqlspec.builder.mixins import (
14
- ReturningClauseMixin,
15
- UpdateFromClauseMixin,
16
- UpdateSetClauseMixin,
17
- UpdateTableClauseMixin,
18
- WhereClauseMixin,
19
- )
13
+ from sqlspec.builder._dml import UpdateFromClauseMixin, UpdateSetClauseMixin, UpdateTableClauseMixin
14
+ from sqlspec.builder._join import build_join_clause
15
+ from sqlspec.builder._select import ReturningClauseMixin, WhereClauseMixin
20
16
  from sqlspec.core.result import SQLResult
21
17
  from sqlspec.exceptions import SQLBuilderError
22
18
 
23
19
  if TYPE_CHECKING:
24
20
  from sqlspec.builder._select import Select
21
+ from sqlspec.protocols import SQLBuilderProtocol
25
22
 
26
23
  __all__ = ("Update",)
27
24
 
@@ -70,9 +67,9 @@ class Update(
70
67
  """
71
68
 
72
69
  __slots__ = ("_table",)
73
- _expression: Optional[exp.Expression]
70
+ _expression: exp.Expression | None
74
71
 
75
- def __init__(self, table: Optional[str] = None, **kwargs: Any) -> None:
72
+ def __init__(self, table: str | None = None, **kwargs: Any) -> None:
76
73
  """Initialize UPDATE with optional table.
77
74
 
78
75
  Args:
@@ -100,9 +97,9 @@ class Update(
100
97
 
101
98
  def join(
102
99
  self,
103
- table: "Union[str, exp.Expression, Select]",
104
- on: "Union[str, exp.Expression]",
105
- alias: "Optional[str]" = None,
100
+ table: "str | exp.Expression | Select",
101
+ on: "str | exp.Expression",
102
+ alias: "str | None" = None,
106
103
  join_type: str = "INNER",
107
104
  ) -> "Self":
108
105
  """Add JOIN clause to the UPDATE statement.
@@ -123,35 +120,7 @@ class Update(
123
120
  msg = "Cannot add JOIN clause to non-UPDATE expression."
124
121
  raise SQLBuilderError(msg)
125
122
 
126
- table_expr: exp.Expression
127
- if isinstance(table, str):
128
- table_expr = exp.table_(table, alias=alias)
129
- elif isinstance(table, QueryBuilder):
130
- subquery = table.build()
131
- subquery_exp = exp.paren(exp.maybe_parse(subquery.sql, dialect=self.dialect))
132
- table_expr = exp.alias_(subquery_exp, alias) if alias else subquery_exp
133
-
134
- subquery_parameters = table.parameters
135
- if subquery_parameters:
136
- for p_name, p_value in subquery_parameters.items():
137
- self.add_parameter(p_value, name=p_name)
138
- else:
139
- table_expr = table
140
-
141
- on_expr: exp.Expression = exp.condition(on) if isinstance(on, str) else on
142
-
143
- join_type_upper = join_type.upper()
144
- if join_type_upper == "INNER":
145
- join_expr = exp.Join(this=table_expr, on=on_expr)
146
- elif join_type_upper == "LEFT":
147
- join_expr = exp.Join(this=table_expr, on=on_expr, side="LEFT")
148
- elif join_type_upper == "RIGHT":
149
- join_expr = exp.Join(this=table_expr, on=on_expr, side="RIGHT")
150
- elif join_type_upper == "FULL":
151
- join_expr = exp.Join(this=table_expr, on=on_expr, side="FULL", kind="OUTER")
152
- else:
153
- msg = f"Unsupported join type: {join_type}"
154
- raise SQLBuilderError(msg)
123
+ join_expr = build_join_clause(cast("SQLBuilderProtocol", self), table, on, alias, join_type)
155
124
 
156
125
  if not self._expression.args.get("joins"):
157
126
  self._expression.set("joins", [])
sqlspec/cli.py CHANGED
@@ -3,12 +3,15 @@ import inspect
3
3
  import sys
4
4
  from collections.abc import Sequence
5
5
  from pathlib import Path
6
- from typing import TYPE_CHECKING, Any, Optional, Union, cast
6
+ from typing import TYPE_CHECKING, Any, cast
7
+
8
+ import rich_click as click
7
9
 
8
10
  if TYPE_CHECKING:
9
- from click import Group
11
+ from rich_click import Group
10
12
 
11
13
  from sqlspec.config import AsyncDatabaseConfig, SyncDatabaseConfig
14
+ from sqlspec.migrations.commands import AsyncMigrationCommands, SyncMigrationCommands
12
15
 
13
16
  __all__ = ("add_migration_commands", "get_sqlspec_group")
14
17
 
@@ -16,21 +19,9 @@ __all__ = ("add_migration_commands", "get_sqlspec_group")
16
19
  def get_sqlspec_group() -> "Group":
17
20
  """Get the SQLSpec CLI group.
18
21
 
19
- Raises:
20
- MissingDependencyError: If the `click` package is not installed.
21
-
22
22
  Returns:
23
23
  The SQLSpec CLI group.
24
24
  """
25
- from sqlspec.exceptions import MissingDependencyError
26
-
27
- try:
28
- import rich_click as click
29
- except ImportError:
30
- try:
31
- import click # type: ignore[no-redef]
32
- except ImportError as e:
33
- raise MissingDependencyError(package="click", install_package="cli") from e
34
25
 
35
26
  @click.group(name="sqlspec")
36
27
  @click.option(
@@ -89,27 +80,15 @@ def get_sqlspec_group() -> "Group":
89
80
  return sqlspec_group
90
81
 
91
82
 
92
- def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
83
+ def add_migration_commands(database_group: "Group | None" = None) -> "Group":
93
84
  """Add migration commands to the database group.
94
85
 
95
86
  Args:
96
87
  database_group: The database group to add the commands to.
97
88
 
98
- Raises:
99
- MissingDependencyError: If the `click` package is not installed.
100
-
101
89
  Returns:
102
90
  The database group with the migration commands added.
103
91
  """
104
- from sqlspec.exceptions import MissingDependencyError
105
-
106
- try:
107
- import rich_click as click
108
- except ImportError:
109
- try:
110
- import click # type: ignore[no-redef]
111
- except ImportError as e:
112
- raise MissingDependencyError(package="click", install_package="cli") from e
113
92
  from rich import get_console
114
93
 
115
94
  console = get_console()
@@ -145,10 +124,16 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
145
124
  default="auto",
146
125
  help="Force execution mode (auto-detects by default)",
147
126
  )
127
+ no_auto_sync_option = click.option(
128
+ "--no-auto-sync",
129
+ is_flag=True,
130
+ default=False,
131
+ help="Disable automatic version reconciliation when migrations have been renamed",
132
+ )
148
133
 
149
134
  def get_config_by_bind_key(
150
- ctx: "click.Context", bind_key: Optional[str]
151
- ) -> "Union[AsyncDatabaseConfig[Any, Any, Any], SyncDatabaseConfig[Any, Any, Any]]":
135
+ ctx: "click.Context", bind_key: str | None
136
+ ) -> "AsyncDatabaseConfig[Any, Any, Any] | SyncDatabaseConfig[Any, Any, Any]":
152
137
  """Get the SQLSpec config for the specified bind key.
153
138
 
154
139
  Args:
@@ -173,13 +158,7 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
173
158
  console.print(f"[red]No config found for bind key: {bind_key}[/]")
174
159
  sys.exit(1)
175
160
 
176
- # Extract the actual config from DatabaseConfig wrapper if needed
177
- from sqlspec.extensions.litestar.config import DatabaseConfig
178
-
179
- if isinstance(config, DatabaseConfig):
180
- config = config.config
181
-
182
- return cast("Union[AsyncDatabaseConfig[Any, Any, Any], SyncDatabaseConfig[Any, Any, Any]]", config)
161
+ return cast("AsyncDatabaseConfig[Any, Any, Any] | SyncDatabaseConfig[Any, Any, Any]", config)
183
162
 
184
163
  def get_configs_with_migrations(ctx: "click.Context", enabled_only: bool = False) -> "list[tuple[str, Any]]":
185
164
  """Get all configurations that have migrations enabled.
@@ -194,18 +173,13 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
194
173
  configs = ctx.obj["configs"]
195
174
  migration_configs = []
196
175
 
197
- from sqlspec.extensions.litestar.config import DatabaseConfig
198
-
199
176
  for config in configs:
200
- # Extract the actual config from DatabaseConfig wrapper if needed
201
- actual_config = config.config if isinstance(config, DatabaseConfig) else config
202
-
203
- migration_config = actual_config.migration_config
177
+ migration_config = config.migration_config
204
178
  if migration_config:
205
179
  enabled = migration_config.get("enabled", True)
206
180
  if not enabled_only or enabled:
207
- config_name = actual_config.bind_key or str(type(actual_config).__name__)
208
- migration_configs.append((config_name, actual_config))
181
+ config_name = config.bind_key or str(type(config).__name__)
182
+ migration_configs.append((config_name, config))
209
183
 
210
184
  return migration_configs
211
185
 
@@ -237,12 +211,12 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
237
211
 
238
212
  def process_multiple_configs(
239
213
  ctx: "click.Context",
240
- bind_key: Optional[str],
214
+ bind_key: str | None,
241
215
  include: "tuple[str, ...]",
242
216
  exclude: "tuple[str, ...]",
243
217
  dry_run: bool,
244
218
  operation_name: str,
245
- ) -> "Optional[list[tuple[str, Any]]]":
219
+ ) -> "list[tuple[str, Any]] | None":
246
220
  """Process configuration selection for multi-config operations.
247
221
 
248
222
  Args:
@@ -290,7 +264,7 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
290
264
  @include_option
291
265
  @exclude_option
292
266
  def show_database_revision( # pyright: ignore[reportUnusedFunction]
293
- bind_key: Optional[str], verbose: bool, include: "tuple[str, ...]", exclude: "tuple[str, ...]"
267
+ bind_key: str | None, verbose: bool, include: "tuple[str, ...]", exclude: "tuple[str, ...]"
294
268
  ) -> None:
295
269
  """Show current database revision."""
296
270
  from sqlspec.migrations.commands import create_migration_commands
@@ -318,7 +292,9 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
318
292
  for config_name, config in configs_to_process:
319
293
  console.print(f"\n[blue]Configuration: {config_name}[/]")
320
294
  try:
321
- migration_commands = create_migration_commands(config=config)
295
+ migration_commands: SyncMigrationCommands[Any] | AsyncMigrationCommands[Any] = (
296
+ create_migration_commands(config=config)
297
+ )
322
298
  await maybe_await(migration_commands.current(verbose=verbose))
323
299
  except Exception as e:
324
300
  console.print(f"[red]✗ Failed to get current revision for {config_name}: {e}[/]")
@@ -338,7 +314,7 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
338
314
  @dry_run_option
339
315
  @click.argument("revision", type=str, default="-1")
340
316
  def downgrade_database( # pyright: ignore[reportUnusedFunction]
341
- bind_key: Optional[str],
317
+ bind_key: str | None,
342
318
  revision: str,
343
319
  no_prompt: bool,
344
320
  include: "tuple[str, ...]",
@@ -379,8 +355,10 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
379
355
  for config_name, config in configs_to_process:
380
356
  console.print(f"[blue]Downgrading configuration: {config_name}[/]")
381
357
  try:
382
- migration_commands = create_migration_commands(config=config)
383
- await maybe_await(migration_commands.downgrade(revision=revision))
358
+ migration_commands: SyncMigrationCommands[Any] | AsyncMigrationCommands[Any] = (
359
+ create_migration_commands(config=config)
360
+ )
361
+ await maybe_await(migration_commands.downgrade(revision=revision, dry_run=dry_run))
384
362
  console.print(f"[green]✓ Successfully downgraded: {config_name}[/]")
385
363
  except Exception as e:
386
364
  console.print(f"[red]✗ Failed to downgrade {config_name}: {e}[/]")
@@ -395,7 +373,7 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
395
373
  if input_confirmed:
396
374
  sqlspec_config = get_config_by_bind_key(cast("click.Context", ctx), bind_key)
397
375
  migration_commands = create_migration_commands(config=sqlspec_config)
398
- await maybe_await(migration_commands.downgrade(revision=revision))
376
+ await maybe_await(migration_commands.downgrade(revision=revision, dry_run=dry_run))
399
377
 
400
378
  run_(_downgrade_database)()
401
379
 
@@ -406,15 +384,17 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
406
384
  @exclude_option
407
385
  @dry_run_option
408
386
  @execution_mode_option
387
+ @no_auto_sync_option
409
388
  @click.argument("revision", type=str, default="head")
410
389
  def upgrade_database( # pyright: ignore[reportUnusedFunction]
411
- bind_key: Optional[str],
390
+ bind_key: str | None,
412
391
  revision: str,
413
392
  no_prompt: bool,
414
393
  include: "tuple[str, ...]",
415
394
  exclude: "tuple[str, ...]",
416
395
  dry_run: bool,
417
396
  execution_mode: str,
397
+ no_auto_sync: bool,
418
398
  ) -> None:
419
399
  """Upgrade the database to the latest revision."""
420
400
  from rich.prompt import Confirm
@@ -449,8 +429,12 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
449
429
  for config_name, config in configs_to_process:
450
430
  console.print(f"[blue]Upgrading configuration: {config_name}[/]")
451
431
  try:
452
- migration_commands = create_migration_commands(config=config)
453
- await maybe_await(migration_commands.upgrade(revision=revision))
432
+ migration_commands: SyncMigrationCommands[Any] | AsyncMigrationCommands[Any] = (
433
+ create_migration_commands(config=config)
434
+ )
435
+ await maybe_await(
436
+ migration_commands.upgrade(revision=revision, auto_sync=not no_auto_sync, dry_run=dry_run)
437
+ )
454
438
  console.print(f"[green]✓ Successfully upgraded: {config_name}[/]")
455
439
  except Exception as e:
456
440
  console.print(f"[red]✗ Failed to upgrade {config_name}: {e}[/]")
@@ -467,14 +451,16 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
467
451
  if input_confirmed:
468
452
  sqlspec_config = get_config_by_bind_key(cast("click.Context", ctx), bind_key)
469
453
  migration_commands = create_migration_commands(config=sqlspec_config)
470
- await maybe_await(migration_commands.upgrade(revision=revision))
454
+ await maybe_await(
455
+ migration_commands.upgrade(revision=revision, auto_sync=not no_auto_sync, dry_run=dry_run)
456
+ )
471
457
 
472
458
  run_(_upgrade_database)()
473
459
 
474
460
  @database_group.command(help="Stamp the revision table with the given revision")
475
461
  @click.argument("revision", type=str)
476
462
  @bind_key_option
477
- def stamp(bind_key: Optional[str], revision: str) -> None: # pyright: ignore[reportUnusedFunction]
463
+ def stamp(bind_key: str | None, revision: str) -> None: # pyright: ignore[reportUnusedFunction]
478
464
  """Stamp the revision table with the given revision."""
479
465
  from sqlspec.migrations.commands import create_migration_commands
480
466
  from sqlspec.utils.sync_tools import run_
@@ -494,12 +480,11 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
494
480
  @click.option("--package", is_flag=True, default=True, help="Create `__init__.py` for created folder")
495
481
  @no_prompt_option
496
482
  def init_sqlspec( # pyright: ignore[reportUnusedFunction]
497
- bind_key: Optional[str], directory: Optional[str], package: bool, no_prompt: bool
483
+ bind_key: str | None, directory: str | None, package: bool, no_prompt: bool
498
484
  ) -> None:
499
485
  """Initialize the database migrations."""
500
486
  from rich.prompt import Confirm
501
487
 
502
- from sqlspec.extensions.litestar.config import DatabaseConfig
503
488
  from sqlspec.migrations.commands import create_migration_commands
504
489
  from sqlspec.utils.sync_tools import run_
505
490
 
@@ -520,23 +505,23 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
520
505
  )
521
506
 
522
507
  for config in configs:
523
- # Extract the actual config from DatabaseConfig wrapper if needed
524
- actual_config = config.config if isinstance(config, DatabaseConfig) else config
525
- migration_config = getattr(actual_config, "migration_config", {})
508
+ migration_config = getattr(config, "migration_config", {})
526
509
  target_directory = (
527
- migration_config.get("script_location", "migrations") if directory is None else directory
510
+ str(migration_config.get("script_location", "migrations")) if directory is None else directory
528
511
  )
529
- migration_commands = create_migration_commands(config=actual_config)
530
- await maybe_await(migration_commands.init(directory=cast("str", target_directory), package=package))
512
+ migration_commands = create_migration_commands(config=config)
513
+ await maybe_await(migration_commands.init(directory=target_directory, package=package))
531
514
 
532
515
  run_(_init_sqlspec)()
533
516
 
534
- @database_group.command(name="make-migrations", help="Create a new migration revision.")
517
+ @database_group.command(
518
+ name="create-migration", aliases=["make-migration"], help="Create a new migration revision."
519
+ )
535
520
  @bind_key_option
536
521
  @click.option("-m", "--message", default=None, help="Revision message")
537
522
  @no_prompt_option
538
523
  def create_revision( # pyright: ignore[reportUnusedFunction]
539
- bind_key: Optional[str], message: Optional[str], no_prompt: bool
524
+ bind_key: str | None, message: str | None, no_prompt: bool
540
525
  ) -> None:
541
526
  """Create a new database revision."""
542
527
  from rich.prompt import Prompt
@@ -560,9 +545,31 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
560
545
 
561
546
  run_(_create_revision)()
562
547
 
548
+ @database_group.command(name="fix", help="Convert timestamp migrations to sequential format.")
549
+ @bind_key_option
550
+ @dry_run_option
551
+ @click.option("--yes", is_flag=True, help="Skip confirmation prompt")
552
+ @click.option("--no-database", is_flag=True, help="Skip database record updates")
553
+ def fix_migrations( # pyright: ignore[reportUnusedFunction]
554
+ bind_key: str | None, dry_run: bool, yes: bool, no_database: bool
555
+ ) -> None:
556
+ """Convert timestamp migrations to sequential format."""
557
+ from sqlspec.migrations.commands import create_migration_commands
558
+ from sqlspec.utils.sync_tools import run_
559
+
560
+ ctx = click.get_current_context()
561
+
562
+ async def _fix_migrations() -> None:
563
+ console.rule("[yellow]Migration Fix Command[/]", align="left")
564
+ sqlspec_config = get_config_by_bind_key(cast("click.Context", ctx), bind_key)
565
+ migration_commands = create_migration_commands(config=sqlspec_config)
566
+ await maybe_await(migration_commands.fix(dry_run=dry_run, update_database=not no_database, yes=yes))
567
+
568
+ run_(_fix_migrations)()
569
+
563
570
  @database_group.command(name="show-config", help="Show all configurations with migrations enabled.")
564
571
  @bind_key_option
565
- def show_config(bind_key: Optional[str] = None) -> None: # pyright: ignore[reportUnusedFunction]
572
+ def show_config(bind_key: str | None = None) -> None: # pyright: ignore[reportUnusedFunction]
566
573
  """Show and display all configurations with migrations enabled."""
567
574
  from rich.table import Table
568
575
 
@@ -593,7 +600,7 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
593
600
  for config_name, config in migration_configs:
594
601
  migration_config = getattr(config, "migration_config", {})
595
602
  script_location = migration_config.get("script_location", "migrations")
596
- table.add_row(config_name, script_location, "Migration Enabled")
603
+ table.add_row(config_name, str(script_location), "Migration Enabled")
597
604
 
598
605
  console.print(table)
599
606
  console.print(f"[blue]Found {len(migration_configs)} configuration(s) with migrations enabled.[/]")