datus-db-core 0.1.0__tar.gz

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.
@@ -0,0 +1,140 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ *.manifest
32
+ *.spec
33
+
34
+ # Installer logs
35
+ pip-log.txt
36
+ pip-delete-this-directory.txt
37
+
38
+ # Unit test / coverage reports
39
+ htmlcov/
40
+ .tox/
41
+ .nox/
42
+ .coverage
43
+ .coverage.*
44
+ .cache
45
+ nosetests.xml
46
+ coverage.xml
47
+ *.cover
48
+ *.py,cover
49
+ .hypothesis/
50
+ .pytest_cache/
51
+
52
+ # Translations
53
+ *.mo
54
+ *.pot
55
+
56
+ # Django stuff:
57
+ *.log
58
+ local_settings.py
59
+ db.sqlite3
60
+ db.sqlite3-journal
61
+
62
+ # Flask stuff:
63
+ instance/
64
+ .webassets-cache
65
+
66
+ # Scrapy stuff:
67
+ .scrapy
68
+
69
+ # Sphinx documentation
70
+ docs/_build/
71
+
72
+ # PyBuilder
73
+ target/
74
+
75
+ # Jupyter Notebook
76
+ .ipynb_checkpoints
77
+
78
+ # IPython
79
+ profile_default/
80
+ ipython_config.py
81
+
82
+ # pyenv
83
+ .python-version
84
+
85
+ # pipenv
86
+ Pipfile.lock
87
+
88
+ # uv
89
+ uv.lock
90
+
91
+ # PEP 582
92
+ __pypackages__/
93
+
94
+ # Celery stuff
95
+ celerybeat-schedule
96
+ celerybeat.pid
97
+
98
+ # SageMath parsed files
99
+ *.sage.py
100
+
101
+ # Environments
102
+ .env
103
+ .venv
104
+ env/
105
+ venv/
106
+ ENV/
107
+ env.bak/
108
+ venv.bak/
109
+
110
+ # Spyder project settings
111
+ .spyderproject
112
+ .spyproject
113
+
114
+ # Rope project settings
115
+ .ropeproject
116
+
117
+ # mkdocs documentation
118
+ /site
119
+
120
+ # mypy
121
+ .mypy_cache/
122
+ .dmypy.json
123
+ dmypy.json
124
+
125
+ # Pyre type checker
126
+ .pyre/
127
+
128
+ # IDEs
129
+ .vscode/
130
+ .idea/
131
+ *.swp
132
+ *.swo
133
+ *~
134
+
135
+ # OS
136
+ .DS_Store
137
+ Thumbs.db
138
+
139
+
140
+ .omc
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: datus-db-core
3
+ Version: 0.1.0
4
+ Summary: Core database abstractions for Datus adapters
5
+ Project-URL: Homepage, https://github.com/Datus-ai/datus-db-adapters
6
+ Project-URL: Repository, https://github.com/Datus-ai/datus-db-adapters
7
+ Project-URL: Issues, https://github.com/Datus-ai/datus-db-adapters/issues
8
+ Author-email: DatusAI <support@datus.ai>
9
+ License: Apache-2.0
10
+ Keywords: adapter,core,database,datus
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: pydantic>=2.0.0
18
+ Requires-Dist: sqlglot>=20.0.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ # datus-db-core
22
+
23
+ Core database abstractions for Datus adapters. Provides `BaseSqlConnector`, `ConnectorRegistry`, SQL parsing utilities, and shared models used by all database adapter packages.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install datus-db-core
29
+ ```
@@ -0,0 +1,9 @@
1
+ # datus-db-core
2
+
3
+ Core database abstractions for Datus adapters. Provides `BaseSqlConnector`, `ConnectorRegistry`, SQL parsing utilities, and shared models used by all database adapter packages.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install datus-db-core
9
+ ```
@@ -0,0 +1,36 @@
1
+ # Copyright 2025-present DatusAI, Inc.
2
+ # Licensed under the Apache License, Version 2.0.
3
+ # See http://www.apache.org/licenses/LICENSE-2.0 for details.
4
+
5
+ from datus_db_core.base import BaseSqlConnector, _to_sql_literal, list_to_in_str
6
+ from datus_db_core.config import ConnectionConfig
7
+ from datus_db_core.constants import SQLType
8
+ from datus_db_core.exceptions import DatusException, ErrorCode
9
+ from datus_db_core.logging import get_logger
10
+ from datus_db_core.mixins import CatalogSupportMixin, MaterializedViewSupportMixin, SchemaNamespaceMixin
11
+ from datus_db_core.models import TABLE_TYPE, ExecuteSQLInput, ExecuteSQLResult
12
+ from datus_db_core.registry import AdapterMetadata, ConnectorRegistry, connector_registry
13
+ from datus_db_core.sql_utils import metadata_identifier, parse_context_switch, parse_sql_type
14
+
15
+ __all__ = [
16
+ "BaseSqlConnector",
17
+ "list_to_in_str",
18
+ "_to_sql_literal",
19
+ "ConnectionConfig",
20
+ "SQLType",
21
+ "DatusException",
22
+ "ErrorCode",
23
+ "get_logger",
24
+ "CatalogSupportMixin",
25
+ "MaterializedViewSupportMixin",
26
+ "SchemaNamespaceMixin",
27
+ "TABLE_TYPE",
28
+ "ExecuteSQLInput",
29
+ "ExecuteSQLResult",
30
+ "AdapterMetadata",
31
+ "ConnectorRegistry",
32
+ "connector_registry",
33
+ "metadata_identifier",
34
+ "parse_context_switch",
35
+ "parse_sql_type",
36
+ ]
@@ -0,0 +1,300 @@
1
+ # Copyright 2025-present DatusAI, Inc.
2
+ # Licensed under the Apache License, Version 2.0.
3
+ # See http://www.apache.org/licenses/LICENSE-2.0 for details.
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Any, Dict, Iterator, List, Literal, Optional, Set, Tuple
7
+
8
+ from datus_db_core.config import ConnectionConfig
9
+ from datus_db_core.constants import SQLType
10
+ from datus_db_core.logging import get_logger
11
+ from datus_db_core.models import TABLE_TYPE, ExecuteSQLInput, ExecuteSQLResult
12
+ from datus_db_core.sql_utils import metadata_identifier, parse_sql_type
13
+
14
+ logger = get_logger(__name__)
15
+
16
+
17
+ class BaseSqlConnector(ABC):
18
+ def __init__(self, config: ConnectionConfig, dialect: str):
19
+ self.config = config
20
+ self.timeout_seconds = config.timeout_seconds
21
+ self.connection: Any = None
22
+ self.dialect = dialect
23
+ self.catalog_name = ""
24
+ self.database_name = ""
25
+ self.schema_name = ""
26
+
27
+ def close(self):
28
+ if self.connection:
29
+ self.connection.close()
30
+ self.connection = None
31
+
32
+ def connect(self):
33
+ return
34
+
35
+ def __enter__(self):
36
+ self.connect()
37
+ return self
38
+
39
+ def __exit__(self, exc_type, exc_val, exc_tb):
40
+ if exc_type:
41
+ try:
42
+ self._safe_rollback()
43
+ except Exception as e:
44
+ logger.warning(f"Failed to rollback during cleanup: {e}")
45
+ try:
46
+ self.close()
47
+ except Exception as e:
48
+ logger.warning(f"Failed to close connection during cleanup: {e}")
49
+ return False
50
+
51
+ def _safe_rollback(self):
52
+ if hasattr(self, "connection") and self.connection:
53
+ try:
54
+ if hasattr(self.connection, "rollback"):
55
+ self.connection.rollback()
56
+ except Exception:
57
+ pass
58
+
59
+ def execute(
60
+ self,
61
+ input_params: Any,
62
+ result_format: Optional[Literal["csv", "arrow", "pandas", "list"]] = None,
63
+ ) -> ExecuteSQLResult:
64
+ self.validate_input(input_params)
65
+ if isinstance(input_params, dict):
66
+ input_params = ExecuteSQLInput(**input_params)
67
+ sql_query = input_params.sql_query.strip()
68
+ if result_format is None:
69
+ result_format = getattr(input_params, "result_format", "csv") or "csv"
70
+ try:
71
+ sql_type = parse_sql_type(sql_query, self.dialect)
72
+ if sql_type == SQLType.INSERT:
73
+ result = self.execute_insert(sql_query)
74
+ elif sql_type in (SQLType.UPDATE, SQLType.MERGE):
75
+ result = self.execute_update(sql_query)
76
+ elif sql_type == SQLType.DELETE:
77
+ result = self.execute_delete(sql_query)
78
+ elif sql_type == SQLType.CONTENT_SET:
79
+ result = self.execute_content_set(sql_query)
80
+ elif sql_type == SQLType.DDL:
81
+ result = self.execute_ddl(sql_query)
82
+ elif sql_type == SQLType.SELECT:
83
+ result = self.execute_query(sql_query, result_format)
84
+ elif sql_type == SQLType.METADATA_SHOW:
85
+ result = self.execute_query(sql_query, result_format)
86
+ elif sql_type == SQLType.EXPLAIN:
87
+ result = self.execute_explain(sql_query, result_format)
88
+ else:
89
+ return ExecuteSQLResult(
90
+ success=False,
91
+ error="Unknown type of SQL",
92
+ sql_query=sql_query,
93
+ sql_return="",
94
+ row_count=0,
95
+ result_format=result_format,
96
+ )
97
+
98
+ return result
99
+ except Exception as e:
100
+ logger.error(f"Executing SQL query failed: {e}")
101
+ return ExecuteSQLResult(
102
+ success=False,
103
+ error=str(e),
104
+ sql_query=sql_query,
105
+ sql_return="",
106
+ row_count=0,
107
+ result_format=result_format,
108
+ )
109
+
110
+ @abstractmethod
111
+ def execute_insert(self, sql: str) -> ExecuteSQLResult:
112
+ raise NotImplementedError()
113
+
114
+ @abstractmethod
115
+ def execute_update(self, sql: str) -> ExecuteSQLResult:
116
+ raise NotImplementedError()
117
+
118
+ @abstractmethod
119
+ def execute_delete(self, sql: str) -> ExecuteSQLResult:
120
+ raise NotImplementedError()
121
+
122
+ def validate_input(self, input_params: Any):
123
+ if isinstance(input_params, dict):
124
+ if "sql_query" not in input_params:
125
+ raise ValueError("'sql_query' parameter is required")
126
+ if not isinstance(input_params["sql_query"], str):
127
+ raise ValueError("'sql_query' must be a string")
128
+ else:
129
+ if not hasattr(input_params, "sql_query"):
130
+ raise ValueError("'sql_query' parameter is required")
131
+ if not isinstance(input_params.sql_query, str):
132
+ raise ValueError("'sql_query' must be a string")
133
+
134
+ def execute_arrow(self, sql: str) -> ExecuteSQLResult:
135
+ raise NotImplementedError()
136
+
137
+ @abstractmethod
138
+ def execute_query(
139
+ self, sql: str, result_format: Literal["csv", "arrow", "pandas", "list"] = "csv"
140
+ ) -> ExecuteSQLResult:
141
+ raise NotImplementedError()
142
+
143
+ def execute_explain(
144
+ self, sql: str, result_format: Literal["csv", "arrow", "pandas", "list"] = "csv"
145
+ ) -> ExecuteSQLResult:
146
+ return self.execute_query(sql, result_format)
147
+
148
+ @abstractmethod
149
+ def execute_pandas(self, sql: str) -> ExecuteSQLResult:
150
+ raise NotImplementedError()
151
+
152
+ @abstractmethod
153
+ def execute_ddl(self, sql: str) -> ExecuteSQLResult:
154
+ raise NotImplementedError()
155
+
156
+ @abstractmethod
157
+ def execute_csv(self, sql: str) -> ExecuteSQLResult:
158
+ raise NotImplementedError()
159
+
160
+ @abstractmethod
161
+ def get_databases(self, catalog_name: str = "", include_sys: bool = False) -> List[str]:
162
+ raise NotImplementedError()
163
+
164
+ @abstractmethod
165
+ def get_tables(self, catalog_name: str = "", database_name: str = "", schema_name: str = "") -> List[str]:
166
+ raise NotImplementedError()
167
+
168
+ def get_views(self, catalog_name: str = "", database_name: str = "", schema_name: str = "") -> List[str]:
169
+ return []
170
+
171
+ def _sys_databases(self) -> Set[str]:
172
+ return set()
173
+
174
+ def _sys_schemas(self) -> Set[str]:
175
+ return set()
176
+
177
+ def execute_csv_iterator(
178
+ self, query: str, max_rows: int = 100, with_header: bool = True
179
+ ) -> Iterator[Tuple[str, ...]]:
180
+ raise NotImplementedError()
181
+
182
+ @abstractmethod
183
+ def test_connection(self):
184
+ raise NotImplementedError()
185
+
186
+ def get_type(self) -> str:
187
+ return self.dialect
188
+
189
+ @abstractmethod
190
+ def execute_queries(self, queries: List[str]) -> List[Any]:
191
+ raise NotImplementedError()
192
+
193
+ def get_tables_with_ddl(
194
+ self,
195
+ catalog_name: str = "",
196
+ database_name: str = "",
197
+ schema_name: str = "",
198
+ tables: Optional[List[str]] = None,
199
+ ) -> List[Dict[str, str]]:
200
+ raise NotImplementedError()
201
+
202
+ def _reset_filter_tables(
203
+ self,
204
+ tables: Optional[List[str]] = None,
205
+ catalog_name: str = "",
206
+ database_name: str = "",
207
+ schema_name: str = "",
208
+ ) -> List[str]:
209
+ filter_tables = []
210
+ if tables:
211
+ catalog_name = catalog_name or self.catalog_name
212
+ database_name = database_name or self.database_name
213
+ schema_name = schema_name or self.schema_name
214
+ for table_name in tables:
215
+ filter_tables.append(
216
+ self.full_name(
217
+ table_name=table_name,
218
+ catalog_name=catalog_name,
219
+ database_name=database_name,
220
+ schema_name=schema_name,
221
+ )
222
+ )
223
+ return filter_tables
224
+
225
+ def get_views_with_ddl(
226
+ self, catalog_name: str = "", database_name: str = "", schema_name: str = ""
227
+ ) -> List[Dict[str, str]]:
228
+ raise NotImplementedError()
229
+
230
+ def switch_context(self, catalog_name: str = "", database_name: str = "", schema_name: str = ""):
231
+ self.connect()
232
+ self.do_switch_context(catalog_name=catalog_name, database_name=database_name, schema_name=schema_name)
233
+ if catalog_name:
234
+ self.catalog_name = catalog_name
235
+ if database_name:
236
+ self.database_name = database_name
237
+ if schema_name:
238
+ self.schema_name = schema_name
239
+
240
+ def do_switch_context(self, catalog_name: str = "", database_name: str = "", schema_name: str = ""):
241
+ return None
242
+
243
+ def get_schema(
244
+ self, catalog_name: str = "", database_name: str = "", schema_name: str = "", table_name: str = ""
245
+ ) -> List[Dict[str, str]]:
246
+ raise NotImplementedError()
247
+
248
+ def get_sample_rows(
249
+ self,
250
+ tables: Optional[List[str]] = None,
251
+ top_n: int = 5,
252
+ catalog_name: str = "",
253
+ database_name: str = "",
254
+ schema_name: str = "",
255
+ table_type: TABLE_TYPE = "table",
256
+ ) -> List[Dict[str, Any]]:
257
+ raise NotImplementedError()
258
+
259
+ def full_name(
260
+ self, catalog_name: str = "", database_name: str = "", schema_name: str = "", table_name: str = ""
261
+ ) -> str:
262
+ raise NotImplementedError()
263
+
264
+ def identifier(
265
+ self, catalog_name: str = "", database_name: str = "", schema_name: str = "", table_name: str = ""
266
+ ) -> str:
267
+ return metadata_identifier(
268
+ dialect=self.dialect,
269
+ catalog_name=catalog_name,
270
+ database_name=database_name,
271
+ schema_name=schema_name,
272
+ table_name=table_name,
273
+ )
274
+
275
+ @abstractmethod
276
+ def execute_content_set(self, sql_query: str) -> ExecuteSQLResult:
277
+ raise NotImplementedError()
278
+
279
+
280
+ def list_to_in_str(prefix: str, values: Optional[List[str]] = None) -> str:
281
+ if not values:
282
+ return ""
283
+ value_str = ",".join(_to_sql_literal(v, around_with_quotes=True) for v in values)
284
+ return f"{prefix} ({value_str})"
285
+
286
+
287
+ def _escape_sql_string_standard(value: str) -> str:
288
+ return value.replace("'", "''")
289
+
290
+
291
+ def _to_sql_literal(value: Optional[str], around_with_quotes: bool = False) -> str:
292
+ if value is None:
293
+ return "NULL"
294
+ if not value:
295
+ return "" if not around_with_quotes else "''"
296
+ replace_value = _escape_sql_string_standard(value)
297
+ if not around_with_quotes:
298
+ return replace_value
299
+ else:
300
+ return f"'{replace_value}'"
@@ -0,0 +1,13 @@
1
+ # Copyright 2025-present DatusAI, Inc.
2
+ # Licensed under the Apache License, Version 2.0.
3
+ # See http://www.apache.org/licenses/LICENSE-2.0 for details.
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
7
+
8
+ class ConnectionConfig(BaseModel):
9
+ """Base connection configuration for all database connectors."""
10
+
11
+ model_config = ConfigDict(extra="forbid")
12
+
13
+ timeout_seconds: int = Field(default=30, description="Connection timeout in seconds")
@@ -0,0 +1,20 @@
1
+ # Copyright 2025-present DatusAI, Inc.
2
+ # Licensed under the Apache License, Version 2.0.
3
+ # See http://www.apache.org/licenses/LICENSE-2.0 for details.
4
+
5
+ from enum import Enum
6
+
7
+
8
+ class SQLType(str, Enum):
9
+ """SQL statement types."""
10
+
11
+ SELECT = "select"
12
+ INSERT = "insert"
13
+ UPDATE = "update"
14
+ DELETE = "delete"
15
+ MERGE = "merge"
16
+ DDL = "ddl"
17
+ METADATA_SHOW = "metadata"
18
+ EXPLAIN = "explain"
19
+ CONTENT_SET = "context_set"
20
+ UNKNOWN = "unknown"
@@ -0,0 +1,74 @@
1
+ # Copyright 2025-present DatusAI, Inc.
2
+ # Licensed under the Apache License, Version 2.0.
3
+ # See http://www.apache.org/licenses/LICENSE-2.0 for details.
4
+
5
+ from enum import Enum
6
+ from typing import Any, Optional
7
+
8
+
9
+ class ErrorCode(Enum):
10
+ """Error codes for Datus database exceptions."""
11
+
12
+ # Common errors
13
+ COMMON_UNKNOWN = ("100000", "Unknown error occurred")
14
+ COMMON_FIELD_INVALID = (
15
+ "100001",
16
+ "Unexpected value of {field_name}, expected value: {expected_values}, your value: {your_value}",
17
+ )
18
+ COMMON_INVALID_PARAMETER = ("100002", "Invalid parameter: {error_message}")
19
+ COMMON_FIELD_REQUIRED = ("100003", "Missing required field: {field_name}")
20
+ COMMON_UNSUPPORTED = ("100004", "Unsupported value `{your_value}` for field `{field_name}`")
21
+ COMMON_CONFIG_ERROR = ("100006", "Configuration error: {config_error}")
22
+ COMMON_MISSING_DEPENDENCY = ("100007", "Missing required dependency: {dependency}")
23
+
24
+ # Database errors
25
+ DB_FAILED = ("500000", "Database operation failed. Error details: {error_message}")
26
+ DB_CONNECTION_FAILED = ("500001", "Failed to establish connection to database. Error details: {error_message}")
27
+ DB_CONNECTION_TIMEOUT = ("500002", "Connection to database timed out. Error details: {error_message}")
28
+ DB_AUTHENTICATION_FAILED = (
29
+ "500003",
30
+ "Authentication failed for database. Please check your credentials. Error details: {error_message}",
31
+ )
32
+ DB_PERMISSION_DENIED = (
33
+ "500004",
34
+ "Permission denied when performing '{operation}' on database. Error details: {error_message}",
35
+ )
36
+ DB_EXECUTION_SYNTAX_ERROR = ("500005", "Invalid SQL syntax in query. Error details: {error_message}")
37
+ DB_EXECUTION_ERROR = ("500006", "Failed to execute query on database. Error details: {error_message}")
38
+ DB_EXECUTION_TIMEOUT = ("500007", "Query execution timed out on database. Error details: {error_message}")
39
+ DB_QUERY_METADATA_FAILED = ("500008", "Failed to retrieve metadata for query. Error details: {error_message}")
40
+ DB_CONSTRAINT_VIOLATION = ("500011", "Database constraint violation occurred. Error details: {error_message}")
41
+ DB_TRANSACTION_FAILED = ("500009", "Database transaction failed. Error details: {error_message}")
42
+ DB_TABLE_NOT_EXISTS = ("500010", "Table {table_name} does not exist.")
43
+
44
+ def __init__(self, code: str, desc: str):
45
+ self.code = code
46
+ self.desc = desc
47
+
48
+
49
+ class DatusException(Exception):
50
+ """Datus custom exception with error code."""
51
+
52
+ def __init__(
53
+ self,
54
+ code: ErrorCode,
55
+ message: Optional[str] = None,
56
+ message_args: Optional[dict[str, Any]] = None,
57
+ *args: object,
58
+ ):
59
+ self.code = code
60
+ self.message_args = message_args or {}
61
+ self.message = self.build_msg(message, message_args)
62
+ super().__init__(self.message, *args)
63
+
64
+ def __str__(self):
65
+ return self.message
66
+
67
+ def build_msg(self, message: Optional[str] = None, message_args: Optional[dict[str, Any]] = None) -> str:
68
+ if message:
69
+ final_message = message
70
+ elif message_args:
71
+ final_message = self.code.desc.format(**message_args)
72
+ else:
73
+ final_message = self.code.desc
74
+ return f"error_code={self.code.code}, error_message={final_message}"
@@ -0,0 +1,9 @@
1
+ # Copyright 2025-present DatusAI, Inc.
2
+ # Licensed under the Apache License, Version 2.0.
3
+ # See http://www.apache.org/licenses/LICENSE-2.0 for details.
4
+
5
+ import logging
6
+
7
+
8
+ def get_logger(name: str) -> logging.Logger:
9
+ return logging.getLogger(name)
@@ -0,0 +1,46 @@
1
+ # Copyright 2025-present DatusAI, Inc.
2
+ # Licensed under the Apache License, Version 2.0.
3
+ # See http://www.apache.org/licenses/LICENSE-2.0 for details.
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Dict, List
7
+
8
+
9
+ class CatalogSupportMixin(ABC):
10
+ """Mixin for databases that support catalog namespace."""
11
+
12
+ @abstractmethod
13
+ def get_catalogs(self) -> List[str]:
14
+ raise NotImplementedError
15
+
16
+ @abstractmethod
17
+ def switch_catalog(self, catalog_name: str) -> None:
18
+ raise NotImplementedError
19
+
20
+ @abstractmethod
21
+ def default_catalog(self) -> str:
22
+ raise NotImplementedError
23
+
24
+
25
+ class MaterializedViewSupportMixin(ABC):
26
+ """Mixin for databases that support materialized views."""
27
+
28
+ @abstractmethod
29
+ def get_materialized_views(
30
+ self, catalog_name: str = "", database_name: str = "", schema_name: str = ""
31
+ ) -> List[str]:
32
+ raise NotImplementedError
33
+
34
+ @abstractmethod
35
+ def get_materialized_views_with_ddl(
36
+ self, catalog_name: str = "", database_name: str = "", schema_name: str = ""
37
+ ) -> List[Dict[str, str]]:
38
+ raise NotImplementedError
39
+
40
+
41
+ class SchemaNamespaceMixin(ABC):
42
+ """Mixin for databases that support schema-level namespace."""
43
+
44
+ @abstractmethod
45
+ def get_schemas(self, catalog_name: str = "", database_name: str = "", include_sys: bool = False) -> List[str]:
46
+ raise NotImplementedError