sqlspec 0.14.1__py3-none-any.whl → 0.15.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 (158) hide show
  1. sqlspec/__init__.py +50 -25
  2. sqlspec/__main__.py +1 -1
  3. sqlspec/__metadata__.py +1 -3
  4. sqlspec/_serialization.py +1 -2
  5. sqlspec/_sql.py +256 -120
  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 +115 -260
  10. sqlspec/adapters/adbc/driver.py +462 -367
  11. sqlspec/adapters/aiosqlite/__init__.py +18 -3
  12. sqlspec/adapters/aiosqlite/_types.py +13 -0
  13. sqlspec/adapters/aiosqlite/config.py +199 -129
  14. sqlspec/adapters/aiosqlite/driver.py +230 -269
  15. sqlspec/adapters/asyncmy/__init__.py +18 -3
  16. sqlspec/adapters/asyncmy/_types.py +12 -0
  17. sqlspec/adapters/asyncmy/config.py +80 -168
  18. sqlspec/adapters/asyncmy/driver.py +260 -225
  19. sqlspec/adapters/asyncpg/__init__.py +19 -4
  20. sqlspec/adapters/asyncpg/_types.py +17 -0
  21. sqlspec/adapters/asyncpg/config.py +82 -181
  22. sqlspec/adapters/asyncpg/driver.py +285 -383
  23. sqlspec/adapters/bigquery/__init__.py +17 -3
  24. sqlspec/adapters/bigquery/_types.py +12 -0
  25. sqlspec/adapters/bigquery/config.py +191 -258
  26. sqlspec/adapters/bigquery/driver.py +474 -646
  27. sqlspec/adapters/duckdb/__init__.py +14 -3
  28. sqlspec/adapters/duckdb/_types.py +12 -0
  29. sqlspec/adapters/duckdb/config.py +415 -351
  30. sqlspec/adapters/duckdb/driver.py +343 -413
  31. sqlspec/adapters/oracledb/__init__.py +19 -5
  32. sqlspec/adapters/oracledb/_types.py +14 -0
  33. sqlspec/adapters/oracledb/config.py +123 -379
  34. sqlspec/adapters/oracledb/driver.py +507 -560
  35. sqlspec/adapters/psqlpy/__init__.py +13 -3
  36. sqlspec/adapters/psqlpy/_types.py +11 -0
  37. sqlspec/adapters/psqlpy/config.py +93 -254
  38. sqlspec/adapters/psqlpy/driver.py +505 -234
  39. sqlspec/adapters/psycopg/__init__.py +19 -5
  40. sqlspec/adapters/psycopg/_types.py +17 -0
  41. sqlspec/adapters/psycopg/config.py +143 -403
  42. sqlspec/adapters/psycopg/driver.py +706 -872
  43. sqlspec/adapters/sqlite/__init__.py +14 -3
  44. sqlspec/adapters/sqlite/_types.py +11 -0
  45. sqlspec/adapters/sqlite/config.py +202 -118
  46. sqlspec/adapters/sqlite/driver.py +264 -303
  47. sqlspec/base.py +105 -9
  48. sqlspec/{statement/builder → builder}/__init__.py +12 -14
  49. sqlspec/{statement/builder → builder}/_base.py +120 -55
  50. sqlspec/{statement/builder → builder}/_column.py +17 -6
  51. sqlspec/{statement/builder → builder}/_ddl.py +46 -79
  52. sqlspec/{statement/builder → builder}/_ddl_utils.py +5 -10
  53. sqlspec/{statement/builder → builder}/_delete.py +6 -25
  54. sqlspec/{statement/builder → builder}/_insert.py +6 -64
  55. sqlspec/builder/_merge.py +56 -0
  56. sqlspec/{statement/builder → builder}/_parsing_utils.py +3 -10
  57. sqlspec/{statement/builder → builder}/_select.py +11 -56
  58. sqlspec/{statement/builder → builder}/_update.py +12 -18
  59. sqlspec/{statement/builder → builder}/mixins/__init__.py +10 -14
  60. sqlspec/{statement/builder → builder}/mixins/_cte_and_set_ops.py +48 -59
  61. sqlspec/{statement/builder → builder}/mixins/_insert_operations.py +22 -16
  62. sqlspec/{statement/builder → builder}/mixins/_join_operations.py +1 -3
  63. sqlspec/{statement/builder → builder}/mixins/_merge_operations.py +3 -5
  64. sqlspec/{statement/builder → builder}/mixins/_order_limit_operations.py +3 -3
  65. sqlspec/{statement/builder → builder}/mixins/_pivot_operations.py +4 -8
  66. sqlspec/{statement/builder → builder}/mixins/_select_operations.py +21 -36
  67. sqlspec/{statement/builder → builder}/mixins/_update_operations.py +3 -14
  68. sqlspec/{statement/builder → builder}/mixins/_where_clause.py +52 -79
  69. sqlspec/cli.py +4 -5
  70. sqlspec/config.py +180 -133
  71. sqlspec/core/__init__.py +63 -0
  72. sqlspec/core/cache.py +873 -0
  73. sqlspec/core/compiler.py +396 -0
  74. sqlspec/core/filters.py +828 -0
  75. sqlspec/core/hashing.py +310 -0
  76. sqlspec/core/parameters.py +1209 -0
  77. sqlspec/core/result.py +664 -0
  78. sqlspec/{statement → core}/splitter.py +321 -191
  79. sqlspec/core/statement.py +651 -0
  80. sqlspec/driver/__init__.py +7 -10
  81. sqlspec/driver/_async.py +387 -176
  82. sqlspec/driver/_common.py +527 -289
  83. sqlspec/driver/_sync.py +390 -172
  84. sqlspec/driver/mixins/__init__.py +2 -19
  85. sqlspec/driver/mixins/_result_tools.py +168 -0
  86. sqlspec/driver/mixins/_sql_translator.py +6 -3
  87. sqlspec/exceptions.py +5 -252
  88. sqlspec/extensions/aiosql/adapter.py +93 -96
  89. sqlspec/extensions/litestar/config.py +0 -1
  90. sqlspec/extensions/litestar/handlers.py +15 -26
  91. sqlspec/extensions/litestar/plugin.py +16 -14
  92. sqlspec/extensions/litestar/providers.py +17 -52
  93. sqlspec/loader.py +424 -105
  94. sqlspec/migrations/__init__.py +12 -0
  95. sqlspec/migrations/base.py +92 -68
  96. sqlspec/migrations/commands.py +24 -106
  97. sqlspec/migrations/loaders.py +402 -0
  98. sqlspec/migrations/runner.py +49 -51
  99. sqlspec/migrations/tracker.py +31 -44
  100. sqlspec/migrations/utils.py +64 -24
  101. sqlspec/protocols.py +7 -183
  102. sqlspec/storage/__init__.py +1 -1
  103. sqlspec/storage/backends/base.py +37 -40
  104. sqlspec/storage/backends/fsspec.py +136 -112
  105. sqlspec/storage/backends/obstore.py +138 -160
  106. sqlspec/storage/capabilities.py +5 -4
  107. sqlspec/storage/registry.py +57 -106
  108. sqlspec/typing.py +136 -115
  109. sqlspec/utils/__init__.py +2 -3
  110. sqlspec/utils/correlation.py +0 -3
  111. sqlspec/utils/deprecation.py +6 -6
  112. sqlspec/utils/fixtures.py +6 -6
  113. sqlspec/utils/logging.py +0 -2
  114. sqlspec/utils/module_loader.py +7 -12
  115. sqlspec/utils/singleton.py +0 -1
  116. sqlspec/utils/sync_tools.py +16 -37
  117. sqlspec/utils/text.py +12 -51
  118. sqlspec/utils/type_guards.py +443 -232
  119. {sqlspec-0.14.1.dist-info → sqlspec-0.15.0.dist-info}/METADATA +7 -2
  120. sqlspec-0.15.0.dist-info/RECORD +134 -0
  121. sqlspec/adapters/adbc/transformers.py +0 -108
  122. sqlspec/driver/connection.py +0 -207
  123. sqlspec/driver/mixins/_cache.py +0 -114
  124. sqlspec/driver/mixins/_csv_writer.py +0 -91
  125. sqlspec/driver/mixins/_pipeline.py +0 -508
  126. sqlspec/driver/mixins/_query_tools.py +0 -796
  127. sqlspec/driver/mixins/_result_utils.py +0 -138
  128. sqlspec/driver/mixins/_storage.py +0 -912
  129. sqlspec/driver/mixins/_type_coercion.py +0 -128
  130. sqlspec/driver/parameters.py +0 -138
  131. sqlspec/statement/__init__.py +0 -21
  132. sqlspec/statement/builder/_merge.py +0 -95
  133. sqlspec/statement/cache.py +0 -50
  134. sqlspec/statement/filters.py +0 -625
  135. sqlspec/statement/parameters.py +0 -956
  136. sqlspec/statement/pipelines/__init__.py +0 -210
  137. sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
  138. sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
  139. sqlspec/statement/pipelines/context.py +0 -109
  140. sqlspec/statement/pipelines/transformers/__init__.py +0 -7
  141. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
  142. sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
  143. sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
  144. sqlspec/statement/pipelines/validators/__init__.py +0 -23
  145. sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
  146. sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
  147. sqlspec/statement/pipelines/validators/_performance.py +0 -714
  148. sqlspec/statement/pipelines/validators/_security.py +0 -967
  149. sqlspec/statement/result.py +0 -435
  150. sqlspec/statement/sql.py +0 -1774
  151. sqlspec/utils/cached_property.py +0 -25
  152. sqlspec/utils/statement_hashing.py +0 -203
  153. sqlspec-0.14.1.dist-info/RECORD +0 -145
  154. /sqlspec/{statement/builder → builder}/mixins/_delete_operations.py +0 -0
  155. {sqlspec-0.14.1.dist-info → sqlspec-0.15.0.dist-info}/WHEEL +0 -0
  156. {sqlspec-0.14.1.dist-info → sqlspec-0.15.0.dist-info}/entry_points.txt +0 -0
  157. {sqlspec-0.14.1.dist-info → sqlspec-0.15.0.dist-info}/licenses/LICENSE +0 -0
  158. {sqlspec-0.14.1.dist-info → sqlspec-0.15.0.dist-info}/licenses/NOTICE +0 -0
@@ -8,11 +8,11 @@ from typing import TYPE_CHECKING, Any, Union, cast
8
8
  from rich.console import Console
9
9
  from rich.table import Table
10
10
 
11
+ from sqlspec._sql import sql
11
12
  from sqlspec.migrations.base import BaseMigrationCommands
12
13
  from sqlspec.migrations.runner import AsyncMigrationRunner, SyncMigrationRunner
13
14
  from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
14
15
  from sqlspec.migrations.utils import create_migration_file
15
- from sqlspec.statement.sql import SQL
16
16
  from sqlspec.utils.logging import get_logger
17
17
  from sqlspec.utils.sync_tools import await_
18
18
 
@@ -26,7 +26,7 @@ console = Console()
26
26
 
27
27
 
28
28
  class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
29
- """SQLSpec native migration commands (sync version)."""
29
+ """SQLSpec native migration commands."""
30
30
 
31
31
  def __init__(self, config: "SyncConfigT") -> None:
32
32
  """Initialize migration commands.
@@ -95,8 +95,6 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
95
95
 
96
96
  current = self.tracker.get_current_version(driver)
97
97
  all_migrations = self.runner.get_migration_files()
98
-
99
- # Determine pending migrations
100
98
  pending = []
101
99
  for version, file_path in all_migrations:
102
100
  if (current is None or version > current) and (revision == "head" or version <= revision):
@@ -108,21 +106,16 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
108
106
 
109
107
  console.print(f"[yellow]Found {len(pending)} pending migrations[/]")
110
108
 
111
- # Execute migrations
112
109
  for version, file_path in pending:
113
110
  migration = self.runner.load_migration(file_path)
114
111
 
115
112
  console.print(f"\n[cyan]Applying {version}:[/] {migration['description']}")
116
113
 
117
114
  try:
118
- # Execute migration
119
115
  _, execution_time = self.runner.execute_upgrade(driver, migration)
120
-
121
- # Record in tracking table
122
116
  self.tracker.record_migration(
123
117
  driver, migration["version"], migration["description"], execution_time, migration["checksum"]
124
118
  )
125
-
126
119
  console.print(f"[green]✓ Applied in {execution_time}ms[/]")
127
120
 
128
121
  except Exception as e:
@@ -137,19 +130,14 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
137
130
  """
138
131
  with self.config.provide_session() as driver:
139
132
  self.tracker.ensure_tracking_table(driver)
140
-
141
133
  applied = self.tracker.get_applied_migrations(driver)
142
134
  if not applied:
143
135
  console.print("[yellow]No migrations to downgrade[/]")
144
136
  return
145
-
146
- # Determine migrations to revert
147
137
  to_revert = []
148
138
  if revision == "-1":
149
- # Downgrade one step
150
139
  to_revert = [applied[-1]]
151
140
  else:
152
- # Downgrade to specific version
153
141
  for migration in reversed(applied):
154
142
  if migration["version_num"] > revision:
155
143
  to_revert.append(migration)
@@ -159,29 +147,18 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
159
147
  return
160
148
 
161
149
  console.print(f"[yellow]Reverting {len(to_revert)} migrations[/]")
162
-
163
- # Load migration files
164
150
  all_files = dict(self.runner.get_migration_files())
165
-
166
151
  for migration_record in to_revert:
167
152
  version = migration_record["version_num"]
168
-
169
153
  if version not in all_files:
170
154
  console.print(f"[red]Migration file not found for {version}[/]")
171
155
  continue
172
-
173
156
  migration = self.runner.load_migration(all_files[version])
174
157
  console.print(f"\n[cyan]Reverting {version}:[/] {migration['description']}")
175
-
176
158
  try:
177
- # Execute downgrade
178
159
  _, execution_time = self.runner.execute_downgrade(driver, migration)
179
-
180
- # Remove from tracking table
181
160
  self.tracker.remove_migration(driver, version)
182
-
183
161
  console.print(f"[green]✓ Reverted in {execution_time}ms[/]")
184
-
185
162
  except Exception as e:
186
163
  console.print(f"[red]✗ Failed: {e}[/]")
187
164
  raise
@@ -194,51 +171,37 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
194
171
  """
195
172
  with self.config.provide_session() as driver:
196
173
  self.tracker.ensure_tracking_table(driver)
197
-
198
- # Validate revision exists
199
174
  all_migrations = dict(self.runner.get_migration_files())
200
175
  if revision not in all_migrations:
201
176
  console.print(f"[red]Unknown revision: {revision}[/]")
202
177
  return
203
-
204
- # Clear existing records and stamp
205
- clear_sql = SQL(f"DELETE FROM {self.tracker.version_table}")
178
+ clear_sql = sql.delete().from_(self.tracker.version_table)
206
179
  driver.execute(clear_sql)
207
-
208
180
  self.tracker.record_migration(driver, revision, f"Stamped to {revision}", 0, "manual-stamp")
209
-
210
181
  console.print(f"[green]Database stamped at revision {revision}[/]")
211
182
 
212
- def revision(self, message: str) -> None:
183
+ def revision(self, message: str, file_type: str = "sql") -> None:
213
184
  """Create a new migration file.
214
185
 
215
186
  Args:
216
187
  message: Description for the migration.
188
+ file_type: Type of migration file to create ('sql' or 'py').
217
189
  """
218
- # Determine next version number
219
190
  existing = self.runner.get_migration_files()
220
- if existing:
221
- last_version = existing[-1][0]
222
- next_num = int(last_version) + 1
223
- else:
224
- next_num = 1
225
-
191
+ next_num = int(existing[-1][0]) + 1 if existing else 1
226
192
  next_version = str(next_num).zfill(4)
227
-
228
- # Create migration file
229
- file_path = create_migration_file(self.migrations_path, next_version, message)
230
-
193
+ file_path = create_migration_file(self.migrations_path, next_version, message, file_type)
231
194
  console.print(f"[green]Created migration:[/] {file_path}")
232
195
 
233
196
 
234
197
  class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
235
- """SQLSpec native async migration commands."""
198
+ """SQLSpec native migration commands."""
236
199
 
237
200
  def __init__(self, sqlspec_config: "AsyncConfigT") -> None:
238
- """Initialize async migration commands.
201
+ """Initialize migration commands.
239
202
 
240
203
  Args:
241
- sqlspec_config: The async SQLSpec configuration.
204
+ sqlspec_config: The SQLSpec configuration.
242
205
  """
243
206
  super().__init__(sqlspec_config)
244
207
  self.tracker = AsyncMigrationTracker(self.version_table)
@@ -251,7 +214,6 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
251
214
  directory: Directory path for migrations.
252
215
  package: Whether to create __init__.py in the directory.
253
216
  """
254
- # For async, we still use sync directory operations
255
217
  self.init_directory(directory, package)
256
218
 
257
219
  async def current(self, verbose: bool = False) -> None:
@@ -269,17 +231,14 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
269
231
  return
270
232
 
271
233
  console.print(f"[green]Current version:[/] {current}")
272
-
273
234
  if verbose:
274
235
  applied = await self.tracker.get_applied_migrations(driver)
275
-
276
236
  table = Table(title="Applied Migrations")
277
237
  table.add_column("Version", style="cyan")
278
238
  table.add_column("Description")
279
239
  table.add_column("Applied At")
280
240
  table.add_column("Time (ms)", justify="right")
281
241
  table.add_column("Applied By")
282
-
283
242
  for migration in applied:
284
243
  table.add_row(
285
244
  migration["version_num"],
@@ -288,50 +247,36 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
288
247
  str(migration.get("execution_time_ms", "")),
289
248
  migration.get("applied_by", ""),
290
249
  )
291
-
292
250
  console.print(table)
293
251
 
294
252
  async def upgrade(self, revision: str = "head") -> None:
295
253
  """Upgrade to a target revision.
296
254
 
297
255
  Args:
298
- revision: Target revision (default: "head" for latest).
256
+ revision: Target revision or "head" for latest.
299
257
  """
300
258
  async with self.config.provide_session() as driver:
301
259
  await self.tracker.ensure_tracking_table(driver)
302
260
 
303
261
  current = await self.tracker.get_current_version(driver)
304
262
  all_migrations = await self.runner.get_migration_files()
305
-
306
- # Determine pending migrations
307
263
  pending = []
308
264
  for version, file_path in all_migrations:
309
265
  if (current is None or version > current) and (revision == "head" or version <= revision):
310
266
  pending.append((version, file_path))
311
-
312
267
  if not pending:
313
268
  console.print("[green]Already at latest version[/]")
314
269
  return
315
-
316
270
  console.print(f"[yellow]Found {len(pending)} pending migrations[/]")
317
-
318
- # Execute migrations
319
271
  for version, file_path in pending:
320
272
  migration = await self.runner.load_migration(file_path)
321
-
322
273
  console.print(f"\n[cyan]Applying {version}:[/] {migration['description']}")
323
-
324
274
  try:
325
- # Execute migration
326
275
  _, execution_time = await self.runner.execute_upgrade(driver, migration)
327
-
328
- # Record in tracking table
329
276
  await self.tracker.record_migration(
330
277
  driver, migration["version"], migration["description"], execution_time, migration["checksum"]
331
278
  )
332
-
333
279
  console.print(f"[green]✓ Applied in {execution_time}ms[/]")
334
-
335
280
  except Exception as e:
336
281
  console.print(f"[red]✗ Failed: {e}[/]")
337
282
  raise
@@ -340,7 +285,7 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
340
285
  """Downgrade to a target revision.
341
286
 
342
287
  Args:
343
- revision: Target revision (default: "-1" for one step back).
288
+ revision: Target revision or "-1" for one step back.
344
289
  """
345
290
  async with self.config.provide_session() as driver:
346
291
  await self.tracker.ensure_tracking_table(driver)
@@ -349,30 +294,21 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
349
294
  if not applied:
350
295
  console.print("[yellow]No migrations to downgrade[/]")
351
296
  return
352
-
353
- # Determine migrations to revert
354
297
  to_revert = []
355
298
  if revision == "-1":
356
- # Downgrade one step
357
299
  to_revert = [applied[-1]]
358
300
  else:
359
- # Downgrade to specific version
360
301
  for migration in reversed(applied):
361
302
  if migration["version_num"] > revision:
362
303
  to_revert.append(migration)
363
-
364
304
  if not to_revert:
365
305
  console.print("[yellow]Nothing to downgrade[/]")
366
306
  return
367
307
 
368
308
  console.print(f"[yellow]Reverting {len(to_revert)} migrations[/]")
369
-
370
- # Load migration files
371
309
  all_files = dict(await self.runner.get_migration_files())
372
-
373
310
  for migration_record in to_revert:
374
311
  version = migration_record["version_num"]
375
-
376
312
  if version not in all_files:
377
313
  console.print(f"[red]Migration file not found for {version}[/]")
378
314
  continue
@@ -381,14 +317,9 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
381
317
  console.print(f"\n[cyan]Reverting {version}:[/] {migration['description']}")
382
318
 
383
319
  try:
384
- # Execute downgrade
385
320
  _, execution_time = await self.runner.execute_downgrade(driver, migration)
386
-
387
- # Remove from tracking table
388
321
  await self.tracker.remove_migration(driver, version)
389
-
390
322
  console.print(f"[green]✓ Reverted in {execution_time}ms[/]")
391
-
392
323
  except Exception as e:
393
324
  console.print(f"[red]✗ Failed: {e}[/]")
394
325
  raise
@@ -402,39 +333,27 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
402
333
  async with self.config.provide_session() as driver:
403
334
  await self.tracker.ensure_tracking_table(driver)
404
335
 
405
- # Validate revision exists
406
336
  all_migrations = dict(await self.runner.get_migration_files())
407
337
  if revision not in all_migrations:
408
338
  console.print(f"[red]Unknown revision: {revision}[/]")
409
339
  return
410
340
 
411
- # Clear existing records and stamp
412
- clear_sql = SQL(f"DELETE FROM {self.tracker.version_table}")
341
+ clear_sql = sql.delete().from_(self.tracker.version_table)
413
342
  await driver.execute(clear_sql)
414
-
415
343
  await self.tracker.record_migration(driver, revision, f"Stamped to {revision}", 0, "manual-stamp")
416
-
417
344
  console.print(f"[green]Database stamped at revision {revision}[/]")
418
345
 
419
- async def revision(self, message: str) -> None:
346
+ async def revision(self, message: str, file_type: str = "sql") -> None:
420
347
  """Create a new migration file.
421
348
 
422
349
  Args:
423
- message: Description of the migration.
350
+ message: Description for the migration.
351
+ file_type: Type of migration file to create ('sql' or 'py').
424
352
  """
425
- # Determine next version number
426
353
  existing = await self.runner.get_migration_files()
427
- if existing:
428
- last_version = existing[-1][0]
429
- next_num = int(last_version) + 1
430
- else:
431
- next_num = 1
432
-
354
+ next_num = int(existing[-1][0]) + 1 if existing else 1
433
355
  next_version = str(next_num).zfill(4)
434
-
435
- # Create migration file
436
- file_path = create_migration_file(self.migrations_path, next_version, message)
437
-
356
+ file_path = create_migration_file(self.migrations_path, next_version, message, file_type)
438
357
  console.print(f"[green]Created migration:[/] {file_path}")
439
358
 
440
359
 
@@ -442,19 +361,17 @@ class MigrationCommands:
442
361
  """Unified migration commands that adapt to sync/async configs."""
443
362
 
444
363
  def __init__(self, config: "Union[SyncConfigT, AsyncConfigT]") -> None:
445
- """Initialize migration commands with appropriate sync/async implementation.
364
+ """Initialize migration commands with sync/async implementation.
446
365
 
447
366
  Args:
448
- config: The SQLSpec configuration (sync or async).
367
+ config: The SQLSpec configuration.
449
368
  """
450
-
451
369
  if config.is_async:
452
370
  self._impl: Union[AsyncMigrationCommands[Any], SyncMigrationCommands[Any]] = AsyncMigrationCommands(
453
371
  cast("AsyncConfigT", config)
454
372
  )
455
373
  else:
456
374
  self._impl = SyncMigrationCommands(cast("SyncConfigT", config))
457
-
458
375
  self._is_async = config.is_async
459
376
 
460
377
  def init(self, directory: str, package: bool = True) -> None:
@@ -513,13 +430,14 @@ class MigrationCommands:
513
430
  else:
514
431
  cast("SyncMigrationCommands[Any]", self._impl).stamp(revision)
515
432
 
516
- def revision(self, message: str) -> None:
433
+ def revision(self, message: str, file_type: str = "sql") -> None:
517
434
  """Create a new migration file.
518
435
 
519
436
  Args:
520
437
  message: Description for the migration.
438
+ file_type: Type of migration file to create ('sql' or 'py').
521
439
  """
522
440
  if self._is_async:
523
- await_(cast("AsyncMigrationCommands[Any]", self._impl).revision, raise_sync_error=False)(message)
441
+ await_(cast("AsyncMigrationCommands[Any]", self._impl).revision, raise_sync_error=False)(message, file_type)
524
442
  else:
525
- cast("SyncMigrationCommands[Any]", self._impl).revision(message)
443
+ cast("SyncMigrationCommands[Any]", self._impl).revision(message, file_type)