sqlspec 0.25.0__py3-none-any.whl → 0.26.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 (84) hide show
  1. sqlspec/_serialization.py +223 -21
  2. sqlspec/_sql.py +12 -50
  3. sqlspec/_typing.py +9 -0
  4. sqlspec/adapters/adbc/config.py +8 -1
  5. sqlspec/adapters/adbc/data_dictionary.py +290 -0
  6. sqlspec/adapters/adbc/driver.py +127 -18
  7. sqlspec/adapters/adbc/type_converter.py +159 -0
  8. sqlspec/adapters/aiosqlite/config.py +3 -0
  9. sqlspec/adapters/aiosqlite/data_dictionary.py +117 -0
  10. sqlspec/adapters/aiosqlite/driver.py +17 -3
  11. sqlspec/adapters/asyncmy/_types.py +1 -1
  12. sqlspec/adapters/asyncmy/config.py +11 -8
  13. sqlspec/adapters/asyncmy/data_dictionary.py +122 -0
  14. sqlspec/adapters/asyncmy/driver.py +31 -7
  15. sqlspec/adapters/asyncpg/config.py +3 -0
  16. sqlspec/adapters/asyncpg/data_dictionary.py +134 -0
  17. sqlspec/adapters/asyncpg/driver.py +19 -4
  18. sqlspec/adapters/bigquery/config.py +3 -0
  19. sqlspec/adapters/bigquery/data_dictionary.py +109 -0
  20. sqlspec/adapters/bigquery/driver.py +21 -3
  21. sqlspec/adapters/bigquery/type_converter.py +93 -0
  22. sqlspec/adapters/duckdb/_types.py +1 -1
  23. sqlspec/adapters/duckdb/config.py +2 -0
  24. sqlspec/adapters/duckdb/data_dictionary.py +124 -0
  25. sqlspec/adapters/duckdb/driver.py +32 -5
  26. sqlspec/adapters/duckdb/pool.py +1 -1
  27. sqlspec/adapters/duckdb/type_converter.py +103 -0
  28. sqlspec/adapters/oracledb/config.py +6 -0
  29. sqlspec/adapters/oracledb/data_dictionary.py +442 -0
  30. sqlspec/adapters/oracledb/driver.py +63 -9
  31. sqlspec/adapters/oracledb/migrations.py +51 -67
  32. sqlspec/adapters/oracledb/type_converter.py +132 -0
  33. sqlspec/adapters/psqlpy/config.py +3 -0
  34. sqlspec/adapters/psqlpy/data_dictionary.py +133 -0
  35. sqlspec/adapters/psqlpy/driver.py +23 -179
  36. sqlspec/adapters/psqlpy/type_converter.py +73 -0
  37. sqlspec/adapters/psycopg/config.py +6 -0
  38. sqlspec/adapters/psycopg/data_dictionary.py +257 -0
  39. sqlspec/adapters/psycopg/driver.py +40 -5
  40. sqlspec/adapters/sqlite/config.py +3 -0
  41. sqlspec/adapters/sqlite/data_dictionary.py +117 -0
  42. sqlspec/adapters/sqlite/driver.py +18 -3
  43. sqlspec/adapters/sqlite/pool.py +13 -4
  44. sqlspec/builder/_base.py +82 -42
  45. sqlspec/builder/_column.py +57 -24
  46. sqlspec/builder/_ddl.py +84 -34
  47. sqlspec/builder/_insert.py +30 -52
  48. sqlspec/builder/_parsing_utils.py +104 -8
  49. sqlspec/builder/_select.py +147 -2
  50. sqlspec/builder/mixins/_cte_and_set_ops.py +1 -2
  51. sqlspec/builder/mixins/_join_operations.py +14 -30
  52. sqlspec/builder/mixins/_merge_operations.py +167 -61
  53. sqlspec/builder/mixins/_order_limit_operations.py +3 -10
  54. sqlspec/builder/mixins/_select_operations.py +3 -9
  55. sqlspec/builder/mixins/_update_operations.py +3 -22
  56. sqlspec/builder/mixins/_where_clause.py +4 -10
  57. sqlspec/cli.py +246 -140
  58. sqlspec/config.py +33 -19
  59. sqlspec/core/cache.py +2 -2
  60. sqlspec/core/compiler.py +56 -1
  61. sqlspec/core/parameters.py +7 -3
  62. sqlspec/core/statement.py +5 -0
  63. sqlspec/core/type_conversion.py +234 -0
  64. sqlspec/driver/__init__.py +6 -3
  65. sqlspec/driver/_async.py +106 -3
  66. sqlspec/driver/_common.py +156 -4
  67. sqlspec/driver/_sync.py +106 -3
  68. sqlspec/exceptions.py +5 -0
  69. sqlspec/migrations/__init__.py +4 -3
  70. sqlspec/migrations/base.py +153 -14
  71. sqlspec/migrations/commands.py +34 -96
  72. sqlspec/migrations/context.py +145 -0
  73. sqlspec/migrations/loaders.py +25 -8
  74. sqlspec/migrations/runner.py +352 -82
  75. sqlspec/typing.py +2 -0
  76. sqlspec/utils/config_resolver.py +153 -0
  77. sqlspec/utils/serializers.py +50 -2
  78. {sqlspec-0.25.0.dist-info → sqlspec-0.26.0.dist-info}/METADATA +1 -1
  79. sqlspec-0.26.0.dist-info/RECORD +157 -0
  80. sqlspec-0.25.0.dist-info/RECORD +0 -139
  81. {sqlspec-0.25.0.dist-info → sqlspec-0.26.0.dist-info}/WHEEL +0 -0
  82. {sqlspec-0.25.0.dist-info → sqlspec-0.26.0.dist-info}/entry_points.txt +0 -0
  83. {sqlspec-0.25.0.dist-info → sqlspec-0.26.0.dist-info}/licenses/LICENSE +0 -0
  84. {sqlspec-0.25.0.dist-info → sqlspec-0.26.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/driver/_async.py CHANGED
@@ -1,10 +1,10 @@
1
1
  """Asynchronous driver protocol implementation."""
2
2
 
3
3
  from abc import abstractmethod
4
- from typing import TYPE_CHECKING, Any, Final, NoReturn, Optional, Union, cast, overload
4
+ from typing import TYPE_CHECKING, Any, Final, NoReturn, Optional, TypeVar, Union, cast, overload
5
5
 
6
6
  from sqlspec.core import SQL, Statement
7
- from sqlspec.driver._common import CommonDriverAttributesMixin, ExecutionResult
7
+ from sqlspec.driver._common import CommonDriverAttributesMixin, DataDictionaryMixin, ExecutionResult, VersionInfo
8
8
  from sqlspec.driver.mixins import SQLTranslatorMixin, ToSchemaMixin
9
9
  from sqlspec.exceptions import NotFoundError
10
10
  from sqlspec.utils.logging import get_logger
@@ -21,17 +21,28 @@ if TYPE_CHECKING:
21
21
  _LOGGER_NAME: Final[str] = "sqlspec"
22
22
  logger = get_logger(_LOGGER_NAME)
23
23
 
24
- __all__ = ("AsyncDriverAdapterBase",)
24
+ __all__ = ("AsyncDataDictionaryBase", "AsyncDriverAdapterBase", "AsyncDriverT")
25
25
 
26
26
 
27
27
  EMPTY_FILTERS: Final["list[StatementFilter]"] = []
28
28
 
29
+ AsyncDriverT = TypeVar("AsyncDriverT", bound="AsyncDriverAdapterBase")
30
+
29
31
 
30
32
  class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, ToSchemaMixin):
31
33
  """Base class for asynchronous database drivers."""
32
34
 
33
35
  __slots__ = ()
34
36
 
37
+ @property
38
+ @abstractmethod
39
+ def data_dictionary(self) -> "AsyncDataDictionaryBase":
40
+ """Get the data dictionary for this driver.
41
+
42
+ Returns:
43
+ Data dictionary instance for metadata queries
44
+ """
45
+
35
46
  async def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> "SQLResult":
36
47
  """Central execution dispatcher using the Template Method Pattern.
37
48
 
@@ -487,3 +498,95 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
487
498
  def _raise_cannot_extract_value_from_row_type(self, type_name: str) -> NoReturn:
488
499
  msg = f"Cannot extract value from row type {type_name}"
489
500
  raise TypeError(msg)
501
+
502
+
503
+ class AsyncDataDictionaryBase(DataDictionaryMixin):
504
+ """Base class for asynchronous data dictionary implementations."""
505
+
506
+ @abstractmethod
507
+ async def get_version(self, driver: "AsyncDriverAdapterBase") -> "Optional[VersionInfo]":
508
+ """Get database version information.
509
+
510
+ Args:
511
+ driver: Async database driver instance
512
+
513
+ Returns:
514
+ Version information or None if detection fails
515
+ """
516
+
517
+ @abstractmethod
518
+ async def get_feature_flag(self, driver: "AsyncDriverAdapterBase", feature: str) -> bool:
519
+ """Check if database supports a specific feature.
520
+
521
+ Args:
522
+ driver: Async database driver instance
523
+ feature: Feature name to check
524
+
525
+ Returns:
526
+ True if feature is supported, False otherwise
527
+ """
528
+
529
+ @abstractmethod
530
+ async def get_optimal_type(self, driver: "AsyncDriverAdapterBase", type_category: str) -> str:
531
+ """Get optimal database type for a category.
532
+
533
+ Args:
534
+ driver: Async database driver instance
535
+ type_category: Type category (e.g., 'json', 'uuid', 'boolean')
536
+
537
+ Returns:
538
+ Database-specific type name
539
+ """
540
+
541
+ async def get_tables(self, driver: "AsyncDriverAdapterBase", schema: "Optional[str]" = None) -> "list[str]":
542
+ """Get list of tables in schema.
543
+
544
+ Args:
545
+ driver: Async database driver instance
546
+ schema: Schema name (None for default)
547
+
548
+ Returns:
549
+ List of table names
550
+ """
551
+ _ = driver, schema
552
+ return []
553
+
554
+ async def get_columns(
555
+ self, driver: "AsyncDriverAdapterBase", table: str, schema: "Optional[str]" = None
556
+ ) -> "list[dict[str, Any]]":
557
+ """Get column information for a table.
558
+
559
+ Args:
560
+ driver: Async database driver instance
561
+ table: Table name
562
+ schema: Schema name (None for default)
563
+
564
+ Returns:
565
+ List of column metadata dictionaries
566
+ """
567
+ _ = driver, table, schema
568
+ return []
569
+
570
+ async def get_indexes(
571
+ self, driver: "AsyncDriverAdapterBase", table: str, schema: "Optional[str]" = None
572
+ ) -> "list[dict[str, Any]]":
573
+ """Get index information for a table.
574
+
575
+ Args:
576
+ driver: Async database driver instance
577
+ table: Table name
578
+ schema: Schema name (None for default)
579
+
580
+ Returns:
581
+ List of index metadata dictionaries
582
+ """
583
+ _ = driver, table, schema
584
+ return []
585
+
586
+ def list_available_features(self) -> "list[str]":
587
+ """List all features that can be checked via get_feature_flag.
588
+
589
+ Returns:
590
+ List of feature names this data dictionary supports
591
+ """
592
+ return self.get_default_features()
sqlspec/driver/_common.py CHANGED
@@ -1,6 +1,8 @@
1
1
  """Common driver attributes and utilities."""
2
2
 
3
- from typing import TYPE_CHECKING, Any, Final, NamedTuple, Optional, Union, cast
3
+ import re
4
+ from contextlib import suppress
5
+ from typing import TYPE_CHECKING, Any, Final, NamedTuple, Optional, TypeVar, Union, cast
4
6
 
5
7
  from mypy_extensions import trait
6
8
  from sqlglot import exp
@@ -25,13 +27,158 @@ __all__ = (
25
27
  "EXEC_ROWCOUNT_OVERRIDE",
26
28
  "EXEC_SPECIAL_DATA",
27
29
  "CommonDriverAttributesMixin",
30
+ "DataDictionaryMixin",
28
31
  "ExecutionResult",
29
32
  "ScriptExecutionResult",
33
+ "VersionInfo",
30
34
  )
31
35
 
32
36
 
33
37
  logger = get_logger("driver")
34
38
 
39
+ DriverT = TypeVar("DriverT")
40
+ VERSION_GROUPS_MIN_FOR_MINOR = 1
41
+ VERSION_GROUPS_MIN_FOR_PATCH = 2
42
+
43
+
44
+ class VersionInfo:
45
+ """Database version information."""
46
+
47
+ def __init__(self, major: int, minor: int = 0, patch: int = 0) -> None:
48
+ """Initialize version info.
49
+
50
+ Args:
51
+ major: Major version number
52
+ minor: Minor version number
53
+ patch: Patch version number
54
+ """
55
+ self.major = major
56
+ self.minor = minor
57
+ self.patch = patch
58
+
59
+ @property
60
+ def version_tuple(self) -> "tuple[int, int, int]":
61
+ """Get version as tuple for comparison."""
62
+ return (self.major, self.minor, self.patch)
63
+
64
+ def __str__(self) -> str:
65
+ """String representation of version info."""
66
+ return f"{self.major}.{self.minor}.{self.patch}"
67
+
68
+ def __repr__(self) -> str:
69
+ """Detailed string representation."""
70
+ return f"VersionInfo({self.major}, {self.minor}, {self.patch})"
71
+
72
+ def __eq__(self, other: object) -> bool:
73
+ """Check version equality."""
74
+ if not isinstance(other, VersionInfo):
75
+ return NotImplemented
76
+ return self.version_tuple == other.version_tuple
77
+
78
+ def __lt__(self, other: "VersionInfo") -> bool:
79
+ """Check if this version is less than another."""
80
+ return self.version_tuple < other.version_tuple
81
+
82
+ def __le__(self, other: "VersionInfo") -> bool:
83
+ """Check if this version is less than or equal to another."""
84
+ return self.version_tuple <= other.version_tuple
85
+
86
+ def __gt__(self, other: "VersionInfo") -> bool:
87
+ """Check if this version is greater than another."""
88
+ return self.version_tuple > other.version_tuple
89
+
90
+ def __ge__(self, other: "VersionInfo") -> bool:
91
+ """Check if this version is greater than or equal to another."""
92
+ return self.version_tuple >= other.version_tuple
93
+
94
+ def __hash__(self) -> int:
95
+ """Make VersionInfo hashable based on version tuple."""
96
+ return hash(self.version_tuple)
97
+
98
+
99
+ @trait
100
+ class DataDictionaryMixin:
101
+ """Mixin providing common data dictionary functionality."""
102
+
103
+ def parse_version_string(self, version_str: str) -> "Optional[VersionInfo]":
104
+ """Parse version string into VersionInfo.
105
+
106
+ Args:
107
+ version_str: Raw version string from database
108
+
109
+ Returns:
110
+ VersionInfo instance or None if parsing fails
111
+ """
112
+ # Try common version patterns
113
+ patterns = [
114
+ r"(\d+)\.(\d+)\.(\d+)", # x.y.z
115
+ r"(\d+)\.(\d+)", # x.y
116
+ r"(\d+)", # x
117
+ ]
118
+
119
+ for pattern in patterns:
120
+ match = re.search(pattern, version_str)
121
+ if match:
122
+ groups = match.groups()
123
+
124
+ major = int(groups[0])
125
+ minor = int(groups[1]) if len(groups) > VERSION_GROUPS_MIN_FOR_MINOR else 0
126
+ patch = int(groups[2]) if len(groups) > VERSION_GROUPS_MIN_FOR_PATCH else 0
127
+ return VersionInfo(major, minor, patch)
128
+
129
+ return None
130
+
131
+ def detect_version_with_queries(self, driver: Any, queries: "list[str]") -> "Optional[VersionInfo]":
132
+ """Try multiple version queries to detect database version.
133
+
134
+ Args:
135
+ driver: Database driver instance
136
+ queries: List of SQL queries to try
137
+
138
+ Returns:
139
+ Version information or None if detection fails
140
+ """
141
+ for query in queries:
142
+ with suppress(Exception):
143
+ result = driver.execute(query)
144
+ if result.data:
145
+ version_str = str(result.data[0])
146
+ if isinstance(result.data[0], dict):
147
+ version_str = str(next(iter(result.data[0].values())))
148
+ elif isinstance(result.data[0], (list, tuple)):
149
+ version_str = str(result.data[0][0])
150
+
151
+ parsed_version = self.parse_version_string(version_str)
152
+ if parsed_version:
153
+ logger.debug("Detected database version: %s", parsed_version)
154
+ return parsed_version
155
+
156
+ logger.warning("Could not detect database version")
157
+ return None
158
+
159
+ def get_default_type_mapping(self) -> "dict[str, str]":
160
+ """Get default type mappings for common categories.
161
+
162
+ Returns:
163
+ Dictionary mapping type categories to generic SQL types
164
+ """
165
+ return {
166
+ "json": "TEXT",
167
+ "uuid": "VARCHAR(36)",
168
+ "boolean": "INTEGER",
169
+ "timestamp": "TIMESTAMP",
170
+ "text": "TEXT",
171
+ "blob": "BLOB",
172
+ }
173
+
174
+ def get_default_features(self) -> "list[str]":
175
+ """Get default feature flags supported by most databases.
176
+
177
+ Returns:
178
+ List of commonly supported feature names
179
+ """
180
+ return ["supports_transactions", "supports_prepared_statements"]
181
+
35
182
 
36
183
  class ScriptExecutionResult(NamedTuple):
37
184
  """Result from script execution with statement count information."""
@@ -270,7 +417,11 @@ class CommonDriverAttributesMixin:
270
417
  ]
271
418
 
272
419
  def prepare_driver_parameters(
273
- self, parameters: Any, statement_config: "StatementConfig", is_many: bool = False
420
+ self,
421
+ parameters: Any,
422
+ statement_config: "StatementConfig",
423
+ is_many: bool = False,
424
+ prepared_statement: Optional[Any] = None, # pyright: ignore[reportUnusedParameter]
274
425
  ) -> Any:
275
426
  """Prepare parameters for database driver consumption.
276
427
 
@@ -281,6 +432,7 @@ class CommonDriverAttributesMixin:
281
432
  parameters: Parameters in any format (dict, list, tuple, scalar, TypedParameter)
282
433
  statement_config: Statement configuration for parameter style detection
283
434
  is_many: If True, handle as executemany parameter sequence
435
+ prepared_statement: Optional prepared statement containing metadata for parameter processing
284
436
 
285
437
  Returns:
286
438
  Parameters with TypedParameter objects unwrapped to primitive values
@@ -422,7 +574,7 @@ class CommonDriverAttributesMixin:
422
574
  compiled_sql, execution_parameters = prepared_statement.compile()
423
575
 
424
576
  prepared_parameters = self.prepare_driver_parameters(
425
- execution_parameters, statement_config, is_many=statement.is_many
577
+ execution_parameters, statement_config, is_many=statement.is_many, prepared_statement=statement
426
578
  )
427
579
 
428
580
  if statement_config.parameter_config.output_transformer:
@@ -438,7 +590,7 @@ class CommonDriverAttributesMixin:
438
590
  if isinstance(prepared_parameters, list)
439
591
  else (
440
592
  prepared_parameters
441
- if prepared_parameters is None
593
+ if prepared_parameters is None or isinstance(prepared_parameters, dict)
442
594
  else (
443
595
  tuple(prepared_parameters)
444
596
  if not isinstance(prepared_parameters, tuple)
sqlspec/driver/_sync.py CHANGED
@@ -1,10 +1,10 @@
1
1
  """Synchronous driver protocol implementation."""
2
2
 
3
3
  from abc import abstractmethod
4
- from typing import TYPE_CHECKING, Any, Final, NoReturn, Optional, Union, cast, overload
4
+ from typing import TYPE_CHECKING, Any, Final, NoReturn, Optional, TypeVar, Union, cast, overload
5
5
 
6
6
  from sqlspec.core import SQL
7
- from sqlspec.driver._common import CommonDriverAttributesMixin, ExecutionResult
7
+ from sqlspec.driver._common import CommonDriverAttributesMixin, DataDictionaryMixin, ExecutionResult, VersionInfo
8
8
  from sqlspec.driver.mixins import SQLTranslatorMixin, ToSchemaMixin
9
9
  from sqlspec.exceptions import NotFoundError
10
10
  from sqlspec.utils.logging import get_logger
@@ -21,17 +21,28 @@ if TYPE_CHECKING:
21
21
  _LOGGER_NAME: Final[str] = "sqlspec"
22
22
  logger = get_logger(_LOGGER_NAME)
23
23
 
24
- __all__ = ("SyncDriverAdapterBase",)
24
+ __all__ = ("SyncDataDictionaryBase", "SyncDriverAdapterBase", "SyncDriverT")
25
25
 
26
26
 
27
27
  EMPTY_FILTERS: Final["list[StatementFilter]"] = []
28
28
 
29
+ SyncDriverT = TypeVar("SyncDriverT", bound="SyncDriverAdapterBase")
30
+
29
31
 
30
32
  class SyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, ToSchemaMixin):
31
33
  """Base class for synchronous database drivers."""
32
34
 
33
35
  __slots__ = ()
34
36
 
37
+ @property
38
+ @abstractmethod
39
+ def data_dictionary(self) -> "SyncDataDictionaryBase":
40
+ """Get the data dictionary for this driver.
41
+
42
+ Returns:
43
+ Data dictionary instance for metadata queries
44
+ """
45
+
35
46
  def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> "SQLResult":
36
47
  """Central execution dispatcher using the Template Method Pattern.
37
48
 
@@ -488,3 +499,95 @@ class SyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, ToS
488
499
  def _raise_cannot_extract_value_from_row_type(self, type_name: str) -> NoReturn:
489
500
  msg = f"Cannot extract value from row type {type_name}"
490
501
  raise TypeError(msg)
502
+
503
+
504
+ class SyncDataDictionaryBase(DataDictionaryMixin):
505
+ """Base class for synchronous data dictionary implementations."""
506
+
507
+ @abstractmethod
508
+ def get_version(self, driver: "SyncDriverAdapterBase") -> "Optional[VersionInfo]":
509
+ """Get database version information.
510
+
511
+ Args:
512
+ driver: Sync database driver instance
513
+
514
+ Returns:
515
+ Version information or None if detection fails
516
+ """
517
+
518
+ @abstractmethod
519
+ def get_feature_flag(self, driver: "SyncDriverAdapterBase", feature: str) -> bool:
520
+ """Check if database supports a specific feature.
521
+
522
+ Args:
523
+ driver: Sync database driver instance
524
+ feature: Feature name to check
525
+
526
+ Returns:
527
+ True if feature is supported, False otherwise
528
+ """
529
+
530
+ @abstractmethod
531
+ def get_optimal_type(self, driver: "SyncDriverAdapterBase", type_category: str) -> str:
532
+ """Get optimal database type for a category.
533
+
534
+ Args:
535
+ driver: Sync database driver instance
536
+ type_category: Type category (e.g., 'json', 'uuid', 'boolean')
537
+
538
+ Returns:
539
+ Database-specific type name
540
+ """
541
+
542
+ def get_tables(self, driver: "SyncDriverAdapterBase", schema: "Optional[str]" = None) -> "list[str]":
543
+ """Get list of tables in schema.
544
+
545
+ Args:
546
+ driver: Sync database driver instance
547
+ schema: Schema name (None for default)
548
+
549
+ Returns:
550
+ List of table names
551
+ """
552
+ _ = driver, schema
553
+ return []
554
+
555
+ def get_columns(
556
+ self, driver: "SyncDriverAdapterBase", table: str, schema: "Optional[str]" = None
557
+ ) -> "list[dict[str, Any]]":
558
+ """Get column information for a table.
559
+
560
+ Args:
561
+ driver: Sync database driver instance
562
+ table: Table name
563
+ schema: Schema name (None for default)
564
+
565
+ Returns:
566
+ List of column metadata dictionaries
567
+ """
568
+ _ = driver, table, schema
569
+ return []
570
+
571
+ def get_indexes(
572
+ self, driver: "SyncDriverAdapterBase", table: str, schema: "Optional[str]" = None
573
+ ) -> "list[dict[str, Any]]":
574
+ """Get index information for a table.
575
+
576
+ Args:
577
+ driver: Sync database driver instance
578
+ table: Table name
579
+ schema: Schema name (None for default)
580
+
581
+ Returns:
582
+ List of index metadata dictionaries
583
+ """
584
+ _ = driver, table, schema
585
+ return []
586
+
587
+ def list_available_features(self) -> "list[str]":
588
+ """List all features that can be checked via get_feature_flag.
589
+
590
+ Returns:
591
+ List of feature names this data dictionary supports
592
+ """
593
+ return self.get_default_features()
sqlspec/exceptions.py CHANGED
@@ -3,6 +3,7 @@ from contextlib import contextmanager
3
3
  from typing import Any, Optional, Union
4
4
 
5
5
  __all__ = (
6
+ "ConfigResolverError",
6
7
  "FileNotFoundInStorageError",
7
8
  "ImproperConfigurationError",
8
9
  "IntegrityError",
@@ -69,6 +70,10 @@ class BackendNotRegisteredError(SQLSpecError):
69
70
  super().__init__(f"Storage backend '{backend_key}' is not registered. Please register it before use.")
70
71
 
71
72
 
73
+ class ConfigResolverError(SQLSpecError):
74
+ """Exception raised when config resolution fails."""
75
+
76
+
72
77
  class SQLParsingError(SQLSpecError):
73
78
  """Issues parsing SQL statements."""
74
79
 
@@ -4,7 +4,7 @@ A native migration system for SQLSpec that leverages the SQLFileLoader
4
4
  and driver system for database versioning.
5
5
  """
6
6
 
7
- from sqlspec.migrations.commands import AsyncMigrationCommands, MigrationCommands, SyncMigrationCommands
7
+ from sqlspec.migrations.commands import AsyncMigrationCommands, SyncMigrationCommands, create_migration_commands
8
8
  from sqlspec.migrations.loaders import (
9
9
  BaseMigrationLoader,
10
10
  MigrationLoadError,
@@ -12,7 +12,7 @@ from sqlspec.migrations.loaders import (
12
12
  SQLFileLoader,
13
13
  get_migration_loader,
14
14
  )
15
- from sqlspec.migrations.runner import AsyncMigrationRunner, SyncMigrationRunner
15
+ from sqlspec.migrations.runner import AsyncMigrationRunner, SyncMigrationRunner, create_migration_runner
16
16
  from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
17
17
  from sqlspec.migrations.utils import create_migration_file, drop_all, get_author
18
18
 
@@ -21,14 +21,15 @@ __all__ = (
21
21
  "AsyncMigrationRunner",
22
22
  "AsyncMigrationTracker",
23
23
  "BaseMigrationLoader",
24
- "MigrationCommands",
25
24
  "MigrationLoadError",
26
25
  "PythonFileLoader",
27
26
  "SQLFileLoader",
28
27
  "SyncMigrationCommands",
29
28
  "SyncMigrationRunner",
30
29
  "SyncMigrationTracker",
30
+ "create_migration_commands",
31
31
  "create_migration_file",
32
+ "create_migration_runner",
32
33
  "drop_all",
33
34
  "get_author",
34
35
  "get_migration_loader",