sqlspec 0.14.0__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 +12 -0
  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 -248
  10. sqlspec/adapters/adbc/driver.py +462 -353
  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.0.dist-info → sqlspec-0.15.0.dist-info}/METADATA +7 -2
  120. sqlspec-0.15.0.dist-info/RECORD +134 -0
  121. sqlspec-0.15.0.dist-info/entry_points.txt +2 -0
  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 -996
  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 -115
  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.0.dist-info/RECORD +0 -143
  154. sqlspec-0.14.0.dist-info/entry_points.txt +0 -2
  155. /sqlspec/{statement/builder → builder}/mixins/_delete_operations.py +0 -0
  156. {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/WHEEL +0 -0
  157. {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/licenses/LICENSE +0 -0
  158. {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/licenses/NOTICE +0 -0
@@ -5,6 +5,13 @@ and driver architecture for database versioning.
5
5
  """
6
6
 
7
7
  from sqlspec.migrations.commands import AsyncMigrationCommands, MigrationCommands, SyncMigrationCommands
8
+ from sqlspec.migrations.loaders import (
9
+ BaseMigrationLoader,
10
+ MigrationLoadError,
11
+ PythonFileLoader,
12
+ SQLFileLoader,
13
+ get_migration_loader,
14
+ )
8
15
  from sqlspec.migrations.runner import AsyncMigrationRunner, SyncMigrationRunner
9
16
  from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
10
17
  from sqlspec.migrations.utils import create_migration_file, drop_all, get_author
@@ -13,11 +20,16 @@ __all__ = (
13
20
  "AsyncMigrationCommands",
14
21
  "AsyncMigrationRunner",
15
22
  "AsyncMigrationTracker",
23
+ "BaseMigrationLoader",
16
24
  "MigrationCommands",
25
+ "MigrationLoadError",
26
+ "PythonFileLoader",
27
+ "SQLFileLoader",
17
28
  "SyncMigrationCommands",
18
29
  "SyncMigrationRunner",
19
30
  "SyncMigrationTracker",
20
31
  "create_migration_file",
21
32
  "drop_all",
22
33
  "get_author",
34
+ "get_migration_loader",
23
35
  )
@@ -3,20 +3,24 @@
3
3
  This module provides abstract base classes for migration components.
4
4
  """
5
5
 
6
+ import operator
6
7
  from abc import ABC, abstractmethod
7
8
  from pathlib import Path
8
9
  from typing import Any, Generic, Optional, TypeVar
9
10
 
11
+ from sqlspec._sql import sql
12
+ from sqlspec.builder._ddl import CreateTable
13
+ from sqlspec.core.statement import SQL
10
14
  from sqlspec.loader import SQLFileLoader
11
- from sqlspec.statement.sql import SQL
15
+ from sqlspec.migrations.loaders import get_migration_loader
12
16
  from sqlspec.utils.logging import get_logger
17
+ from sqlspec.utils.sync_tools import run_
13
18
 
14
19
  __all__ = ("BaseMigrationCommands", "BaseMigrationRunner", "BaseMigrationTracker")
15
20
 
16
21
 
17
22
  logger = get_logger("migrations.base")
18
23
 
19
- # Type variables for generic driver and config types
20
24
  DriverT = TypeVar("DriverT")
21
25
  ConfigT = TypeVar("ConfigT")
22
26
 
@@ -38,18 +42,23 @@ class BaseMigrationTracker(ABC, Generic[DriverT]):
38
42
  Returns:
39
43
  SQL object for table creation.
40
44
  """
41
- return SQL(
42
- f"""
43
- CREATE TABLE IF NOT EXISTS {self.version_table} (
44
- version_num VARCHAR(32) PRIMARY KEY,
45
- description TEXT,
46
- applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
47
- execution_time_ms INTEGER,
48
- checksum VARCHAR(64),
49
- applied_by VARCHAR(255)
50
- )
51
- """
52
- )
45
+ builder = CreateTable(self.version_table)
46
+ if not hasattr(builder, "_columns"):
47
+ builder._columns = []
48
+ if not hasattr(builder, "_constraints"):
49
+ builder._constraints = []
50
+ if not hasattr(builder, "_table_options"):
51
+ builder._table_options = {}
52
+
53
+ return (
54
+ builder.if_not_exists()
55
+ .column("version_num", "VARCHAR(32)", primary_key=True)
56
+ .column("description", "TEXT")
57
+ .column("applied_at", "TIMESTAMP", not_null=True, default="CURRENT_TIMESTAMP")
58
+ .column("execution_time_ms", "INTEGER")
59
+ .column("checksum", "VARCHAR(64)")
60
+ .column("applied_by", "VARCHAR(255)")
61
+ ).to_statement()
53
62
 
54
63
  def _get_current_version_sql(self) -> SQL:
55
64
  """Get SQL for retrieving current version.
@@ -57,7 +66,10 @@ class BaseMigrationTracker(ABC, Generic[DriverT]):
57
66
  Returns:
58
67
  SQL object for version query.
59
68
  """
60
- return SQL(f"SELECT version_num FROM {self.version_table} ORDER BY version_num DESC LIMIT 1")
69
+
70
+ return (
71
+ sql.select("version_num").from_(self.version_table).order_by("version_num DESC").limit(1)
72
+ ).to_statement()
61
73
 
62
74
  def _get_applied_migrations_sql(self) -> SQL:
63
75
  """Get SQL for retrieving all applied migrations.
@@ -65,7 +77,8 @@ class BaseMigrationTracker(ABC, Generic[DriverT]):
65
77
  Returns:
66
78
  SQL object for migrations query.
67
79
  """
68
- return SQL(f"SELECT * FROM {self.version_table} ORDER BY version_num")
80
+
81
+ return (sql.select("*").from_(self.version_table).order_by("version_num")).to_statement()
69
82
 
70
83
  def _get_record_migration_sql(
71
84
  self, version: str, description: str, execution_time_ms: int, checksum: str, applied_by: str
@@ -82,14 +95,12 @@ class BaseMigrationTracker(ABC, Generic[DriverT]):
82
95
  Returns:
83
96
  SQL object for insert.
84
97
  """
85
- return SQL(
86
- f"INSERT INTO {self.version_table} (version_num, description, execution_time_ms, checksum, applied_by) VALUES (?, ?, ?, ?, ?)",
87
- version,
88
- description,
89
- execution_time_ms,
90
- checksum,
91
- applied_by,
92
- )
98
+
99
+ return (
100
+ sql.insert(self.version_table)
101
+ .columns("version_num", "description", "execution_time_ms", "checksum", "applied_by")
102
+ .values(version, description, execution_time_ms, checksum, applied_by)
103
+ ).to_statement()
93
104
 
94
105
  def _get_remove_migration_sql(self, version: str) -> SQL:
95
106
  """Get SQL for removing a migration record.
@@ -100,7 +111,8 @@ class BaseMigrationTracker(ABC, Generic[DriverT]):
100
111
  Returns:
101
112
  SQL object for delete.
102
113
  """
103
- return SQL(f"DELETE FROM {self.version_table} WHERE version_num = ?", version)
114
+
115
+ return (sql.delete().from_(self.version_table).where(sql.version_num == version)).to_statement()
104
116
 
105
117
  @abstractmethod
106
118
  def ensure_tracking_table(self, driver: DriverT) -> Any:
@@ -141,9 +153,10 @@ class BaseMigrationRunner(ABC, Generic[DriverT]):
141
153
  """
142
154
  self.migrations_path = migrations_path
143
155
  self.loader = SQLFileLoader()
156
+ self.project_root: Optional[Path] = None
144
157
 
145
158
  def _extract_version(self, filename: str) -> Optional[str]:
146
- """Extract version from filename (e.g., '0001_initial.sql' -> '0001').
159
+ """Extract version from filename.
147
160
 
148
161
  Args:
149
162
  filename: The migration filename.
@@ -152,9 +165,7 @@ class BaseMigrationRunner(ABC, Generic[DriverT]):
152
165
  The extracted version string or None.
153
166
  """
154
167
  parts = filename.split("_", 1)
155
- if parts and parts[0].isdigit():
156
- return parts[0].zfill(4)
157
- return None
168
+ return parts[0].zfill(4) if parts and parts[0].isdigit() else None
158
169
 
159
170
  def _calculate_checksum(self, content: str) -> str:
160
171
  """Calculate MD5 checksum of migration content.
@@ -163,14 +174,14 @@ class BaseMigrationRunner(ABC, Generic[DriverT]):
163
174
  content: The migration file content.
164
175
 
165
176
  Returns:
166
- The MD5 checksum hex string.
177
+ MD5 checksum hex string.
167
178
  """
168
179
  import hashlib
169
180
 
170
181
  return hashlib.md5(content.encode()).hexdigest() # noqa: S324
171
182
 
172
183
  def _get_migration_files_sync(self) -> "list[tuple[str, Path]]":
173
- """Get all migration files sorted by version (sync version).
184
+ """Get all migration files sorted by version.
174
185
 
175
186
  Returns:
176
187
  List of tuples containing (version, file_path).
@@ -179,14 +190,15 @@ class BaseMigrationRunner(ABC, Generic[DriverT]):
179
190
  return []
180
191
 
181
192
  migrations = []
182
- for file_path in self.migrations_path.glob("*.sql"):
183
- if file_path.name.startswith("."):
184
- continue
185
- version = self._extract_version(file_path.name)
186
- if version:
187
- migrations.append((version, file_path))
193
+ for pattern in ["*.sql", "*.py"]:
194
+ for file_path in self.migrations_path.glob(pattern):
195
+ if file_path.name.startswith("."):
196
+ continue
197
+ version = self._extract_version(file_path.name)
198
+ if version:
199
+ migrations.append((version, file_path))
188
200
 
189
- return sorted(migrations, key=lambda x: x[0])
201
+ return sorted(migrations, key=operator.itemgetter(0))
190
202
 
191
203
  def _load_migration_metadata(self, file_path: Path) -> "dict[str, Any]":
192
204
  """Load migration metadata from file.
@@ -195,32 +207,37 @@ class BaseMigrationRunner(ABC, Generic[DriverT]):
195
207
  file_path: Path to the migration file.
196
208
 
197
209
  Returns:
198
- Dictionary containing migration metadata.
210
+ Migration metadata dictionary.
199
211
  """
200
- self.loader.clear_cache()
201
- self.loader.load_sql(file_path)
202
212
 
203
- # Read raw content for checksum
204
- content = file_path.read_text()
213
+ loader = get_migration_loader(file_path, self.migrations_path, self.project_root)
214
+ loader.validate_migration_file(file_path)
215
+ content = file_path.read_text(encoding="utf-8")
205
216
  checksum = self._calculate_checksum(content)
206
-
207
- # Extract metadata
208
217
  version = self._extract_version(file_path.name)
209
218
  description = file_path.stem.split("_", 1)[1] if "_" in file_path.stem else ""
210
219
 
211
- # Query names use versioned pattern
212
- up_query = f"migrate-{version}-up"
213
- down_query = f"migrate-{version}-down"
220
+ has_upgrade, has_downgrade = True, False
221
+
222
+ if file_path.suffix == ".sql":
223
+ up_query, down_query = f"migrate-{version}-up", f"migrate-{version}-down"
224
+ self.loader.clear_cache()
225
+ self.loader.load_sql(file_path)
226
+ has_upgrade, has_downgrade = self.loader.has_query(up_query), self.loader.has_query(down_query)
227
+ else:
228
+ try:
229
+ has_downgrade = bool(run_(loader.get_down_sql)(file_path))
230
+ except Exception:
231
+ has_downgrade = False
214
232
 
215
233
  return {
216
234
  "version": version,
217
235
  "description": description,
218
236
  "file_path": file_path,
219
237
  "checksum": checksum,
220
- "up_query": up_query,
221
- "down_query": down_query,
222
- "has_upgrade": self.loader.has_query(up_query),
223
- "has_downgrade": self.loader.has_query(down_query),
238
+ "has_upgrade": has_upgrade,
239
+ "has_downgrade": has_downgrade,
240
+ "loader": loader,
224
241
  }
225
242
 
226
243
  def _get_migration_sql(self, migration: "dict[str, Any]", direction: str) -> Optional[SQL]:
@@ -233,17 +250,29 @@ class BaseMigrationRunner(ABC, Generic[DriverT]):
233
250
  Returns:
234
251
  SQL object for the migration.
235
252
  """
236
- query_key = f"{direction}_query"
237
- has_key = f"has_{direction}grade"
238
-
239
- if not migration.get(has_key):
253
+ if not migration.get(f"has_{direction}grade"):
240
254
  if direction == "down":
241
255
  logger.warning("Migration %s has no downgrade query", migration["version"])
242
256
  return None
243
257
  msg = f"Migration {migration['version']} has no upgrade query"
244
258
  raise ValueError(msg)
245
259
 
246
- return self.loader.get_sql(migration[query_key])
260
+ file_path, loader = migration["file_path"], migration["loader"]
261
+
262
+ try:
263
+ method = loader.get_up_sql if direction == "up" else loader.get_down_sql
264
+ sql_statements = run_(method)(file_path)
265
+
266
+ except Exception as e:
267
+ if direction == "down":
268
+ logger.warning("Failed to load downgrade for migration %s: %s", migration["version"], e)
269
+ return None
270
+ msg = f"Failed to load upgrade for migration {migration['version']}: {e}"
271
+ raise ValueError(msg) from e
272
+ else:
273
+ if sql_statements:
274
+ return SQL(sql_statements[0])
275
+ return None
247
276
 
248
277
  @abstractmethod
249
278
  def get_migration_files(self) -> Any:
@@ -281,20 +310,17 @@ class BaseMigrationCommands(ABC, Generic[ConfigT, DriverT]):
281
310
  config: The SQLSpec configuration.
282
311
  """
283
312
  self.config = config
284
-
285
- # Get migration settings from config
286
- migration_config = getattr(self.config, "migration_config", {})
287
- if migration_config is None:
288
- migration_config = {}
313
+ migration_config = getattr(self.config, "migration_config", {}) or {}
289
314
 
290
315
  self.version_table = migration_config.get("version_table_name", "sqlspec_migrations")
291
316
  self.migrations_path = Path(migration_config.get("script_location", "migrations"))
317
+ self.project_root = Path(migration_config["project_root"]) if "project_root" in migration_config else None
292
318
 
293
319
  def _get_init_readme_content(self) -> str:
294
- """Get the README content for migration directory initialization.
320
+ """Get README content for migration directory initialization.
295
321
 
296
322
  Returns:
297
- The README markdown content.
323
+ README markdown content.
298
324
  """
299
325
  return """# SQLSpec Migrations
300
326
 
@@ -334,7 +360,7 @@ This naming ensures proper sorting and avoids conflicts when loading multiple fi
334
360
  """
335
361
 
336
362
  def init_directory(self, directory: str, package: bool = True) -> None:
337
- """Initialize migration directory structure (sync implementation).
363
+ """Initialize migration directory structure.
338
364
 
339
365
  Args:
340
366
  directory: Directory to initialize migrations in.
@@ -350,11 +376,9 @@ This naming ensures proper sorting and avoids conflicts when loading multiple fi
350
376
  if package:
351
377
  (migrations_dir / "__init__.py").touch()
352
378
 
353
- # Create README
354
379
  readme = migrations_dir / "README.md"
355
380
  readme.write_text(self._get_init_readme_content())
356
381
 
357
- # Create .gitkeep for empty directory
358
382
  (migrations_dir / ".gitkeep").touch()
359
383
 
360
384
  console.print(f"[green]Initialized migrations in {directory}[/]")
@@ -385,6 +409,6 @@ This naming ensures proper sorting and avoids conflicts when loading multiple fi
385
409
  ...
386
410
 
387
411
  @abstractmethod
388
- def revision(self, message: str) -> Any:
412
+ def revision(self, message: str, file_type: str = "sql") -> Any:
389
413
  """Create a new migration file."""
390
414
  ...