thoth-dbmanager 0.4.0__py3-none-any.whl → 0.4.2__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.
- thoth_dbmanager/ThothDbManager.py +459 -0
- thoth_dbmanager/__init__.py +136 -0
- thoth_dbmanager/adapters/__init__.py +21 -0
- thoth_dbmanager/adapters/mariadb.py +165 -0
- thoth_dbmanager/adapters/mysql.py +165 -0
- thoth_dbmanager/adapters/oracle.py +554 -0
- thoth_dbmanager/adapters/postgresql.py +444 -0
- thoth_dbmanager/adapters/qdrant.py +189 -0
- thoth_dbmanager/adapters/sqlite.py +385 -0
- thoth_dbmanager/adapters/sqlserver.py +583 -0
- thoth_dbmanager/adapters/supabase.py +249 -0
- thoth_dbmanager/core/__init__.py +13 -0
- thoth_dbmanager/core/factory.py +272 -0
- thoth_dbmanager/core/interfaces.py +271 -0
- thoth_dbmanager/core/registry.py +220 -0
- thoth_dbmanager/documents.py +155 -0
- thoth_dbmanager/dynamic_imports.py +250 -0
- thoth_dbmanager/helpers/__init__.py +0 -0
- thoth_dbmanager/helpers/multi_db_generator.py +508 -0
- thoth_dbmanager/helpers/preprocess_values.py +159 -0
- thoth_dbmanager/helpers/schema.py +376 -0
- thoth_dbmanager/helpers/search.py +117 -0
- thoth_dbmanager/lsh/__init__.py +21 -0
- thoth_dbmanager/lsh/core.py +182 -0
- thoth_dbmanager/lsh/factory.py +76 -0
- thoth_dbmanager/lsh/manager.py +170 -0
- thoth_dbmanager/lsh/storage.py +96 -0
- thoth_dbmanager/plugins/__init__.py +23 -0
- thoth_dbmanager/plugins/mariadb.py +436 -0
- thoth_dbmanager/plugins/mysql.py +408 -0
- thoth_dbmanager/plugins/oracle.py +150 -0
- thoth_dbmanager/plugins/postgresql.py +145 -0
- thoth_dbmanager/plugins/qdrant.py +41 -0
- thoth_dbmanager/plugins/sqlite.py +170 -0
- thoth_dbmanager/plugins/sqlserver.py +149 -0
- thoth_dbmanager/plugins/supabase.py +224 -0
- {thoth_dbmanager-0.4.0.dist-info → thoth_dbmanager-0.4.2.dist-info}/METADATA +9 -6
- thoth_dbmanager-0.4.2.dist-info/RECORD +41 -0
- thoth_dbmanager-0.4.2.dist-info/top_level.txt +1 -0
- thoth_dbmanager-0.4.0.dist-info/RECORD +0 -5
- thoth_dbmanager-0.4.0.dist-info/top_level.txt +0 -1
- {thoth_dbmanager-0.4.0.dist-info → thoth_dbmanager-0.4.2.dist-info}/WHEEL +0 -0
- {thoth_dbmanager-0.4.0.dist-info → thoth_dbmanager-0.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,249 @@
|
|
1
|
+
"""
|
2
|
+
Supabase adapter implementation.
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
from typing import Any, Dict, List, Optional, Union
|
6
|
+
import psycopg2
|
7
|
+
from psycopg2.extras import RealDictCursor
|
8
|
+
from sqlalchemy import create_engine, text
|
9
|
+
from sqlalchemy.exc import SQLAlchemyError
|
10
|
+
from urllib.parse import urlparse, parse_qs
|
11
|
+
|
12
|
+
from .postgresql import PostgreSQLAdapter
|
13
|
+
from ..core.interfaces import DbAdapter
|
14
|
+
from ..documents import (
|
15
|
+
TableDocument,
|
16
|
+
ColumnDocument,
|
17
|
+
SchemaDocument,
|
18
|
+
ForeignKeyDocument,
|
19
|
+
IndexDocument
|
20
|
+
)
|
21
|
+
|
22
|
+
logger = logging.getLogger(__name__)
|
23
|
+
|
24
|
+
|
25
|
+
class SupabaseAdapter(PostgreSQLAdapter):
|
26
|
+
"""
|
27
|
+
Supabase database adapter implementation.
|
28
|
+
Extends PostgreSQL adapter with Supabase-specific features.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, connection_params: Dict[str, Any]):
|
32
|
+
super().__init__(connection_params)
|
33
|
+
self.supabase_url = None
|
34
|
+
self.api_key = None
|
35
|
+
self.use_rest_api = False
|
36
|
+
|
37
|
+
def connect(self) -> None:
|
38
|
+
"""Establish Supabase connection with SSL enforcement"""
|
39
|
+
try:
|
40
|
+
# Check if we should use REST API or direct database connection
|
41
|
+
self.use_rest_api = self.connection_params.get('use_rest_api', False)
|
42
|
+
|
43
|
+
if self.use_rest_api:
|
44
|
+
# REST API connection setup
|
45
|
+
self.supabase_url = self.connection_params.get('project_url')
|
46
|
+
self.api_key = self.connection_params.get('api_key')
|
47
|
+
|
48
|
+
if not self.supabase_url or not self.api_key:
|
49
|
+
raise ValueError("project_url and api_key are required for REST API mode")
|
50
|
+
|
51
|
+
logger.info("Supabase REST API connection established")
|
52
|
+
else:
|
53
|
+
# Direct database connection (PostgreSQL with SSL)
|
54
|
+
super().connect()
|
55
|
+
|
56
|
+
# Ensure SSL is enabled for Supabase
|
57
|
+
if hasattr(self, 'engine') and self.engine:
|
58
|
+
# Update connection string to enforce SSL
|
59
|
+
connection_string = self._build_connection_string()
|
60
|
+
if 'sslmode=' not in connection_string:
|
61
|
+
connection_string += '?sslmode=require'
|
62
|
+
|
63
|
+
self.engine = create_engine(connection_string, echo=False)
|
64
|
+
|
65
|
+
# Test connection
|
66
|
+
with self.engine.connect() as conn:
|
67
|
+
conn.execute(text("SELECT 1"))
|
68
|
+
|
69
|
+
logger.info("Supabase database connection established with SSL")
|
70
|
+
|
71
|
+
except Exception as e:
|
72
|
+
logger.error(f"Failed to connect to Supabase: {e}")
|
73
|
+
raise
|
74
|
+
|
75
|
+
def _build_connection_string(self) -> str:
|
76
|
+
"""Build SQLAlchemy connection string with Supabase-specific parameters"""
|
77
|
+
params = self.connection_params
|
78
|
+
|
79
|
+
if self.use_rest_api:
|
80
|
+
return params.get('project_url')
|
81
|
+
|
82
|
+
# Direct database connection
|
83
|
+
host = params.get('host')
|
84
|
+
port = params.get('port', 5432)
|
85
|
+
database = params.get('database')
|
86
|
+
user = params.get('user')
|
87
|
+
password = params.get('password')
|
88
|
+
|
89
|
+
if not all([host, database, user, password]):
|
90
|
+
raise ValueError("Missing required connection parameters: host, database, user, password")
|
91
|
+
|
92
|
+
# Ensure SSL mode for Supabase
|
93
|
+
ssl_mode = params.get('sslmode', 'require')
|
94
|
+
|
95
|
+
connection_string = f"postgresql://{user}:{password}@{host}:{port}/{database}?sslmode={ssl_mode}"
|
96
|
+
|
97
|
+
# Add additional SSL parameters if provided
|
98
|
+
if params.get('sslcert'):
|
99
|
+
connection_string += f"&sslcert={params['sslcert']}"
|
100
|
+
if params.get('sslkey'):
|
101
|
+
connection_string += f"&sslkey={params['sslkey']}"
|
102
|
+
if params.get('sslrootcert'):
|
103
|
+
connection_string += f"&sslrootcert={params['sslrootcert']}"
|
104
|
+
|
105
|
+
return connection_string
|
106
|
+
|
107
|
+
def _get_psycopg2_params(self) -> Dict[str, Any]:
|
108
|
+
"""Get parameters for psycopg2 connection with SSL"""
|
109
|
+
params = super()._get_psycopg2_params()
|
110
|
+
|
111
|
+
# Ensure SSL is enabled for Supabase
|
112
|
+
params['sslmode'] = self.connection_params.get('sslmode', 'require')
|
113
|
+
|
114
|
+
# Add SSL certificates if provided
|
115
|
+
if self.connection_params.get('sslcert'):
|
116
|
+
params['sslcert'] = self.connection_params['sslcert']
|
117
|
+
if self.connection_params.get('sslkey'):
|
118
|
+
params['sslkey'] = self.connection_params['sslkey']
|
119
|
+
if self.connection_params.get('sslrootcert'):
|
120
|
+
params['sslrootcert'] = self.connection_params['sslrootcert']
|
121
|
+
|
122
|
+
return params
|
123
|
+
|
124
|
+
def execute_query(self, query: str, params: Optional[Dict] = None, fetch: Union[str, int] = "all", timeout: int = 60) -> Any:
|
125
|
+
"""Execute SQL queries with Supabase-specific optimizations"""
|
126
|
+
if self.use_rest_api:
|
127
|
+
return self._execute_rest_query(query, params, fetch, timeout)
|
128
|
+
else:
|
129
|
+
return super().execute_query(query, params, fetch, timeout)
|
130
|
+
|
131
|
+
def _execute_rest_query(self, query: str, params: Optional[Dict] = None, fetch: Union[str, int] = "all", timeout: int = 60) -> Any:
|
132
|
+
"""Execute query using Supabase REST API"""
|
133
|
+
try:
|
134
|
+
from supabase import create_client
|
135
|
+
from postgrest.exceptions import APIError
|
136
|
+
|
137
|
+
# Create Supabase client
|
138
|
+
supabase = create_client(self.supabase_url, self.api_key)
|
139
|
+
|
140
|
+
# For REST API, we need to convert SQL to Postgrest queries
|
141
|
+
# This is a simplified implementation - in practice, you'd need a SQL parser
|
142
|
+
if query.strip().upper().startswith('SELECT'):
|
143
|
+
# Extract table name and conditions from query
|
144
|
+
table_name = self._extract_table_name(query)
|
145
|
+
|
146
|
+
# Build Postgrest query
|
147
|
+
result = supabase.table(table_name).select('*').execute()
|
148
|
+
|
149
|
+
if fetch == "all":
|
150
|
+
return result.data
|
151
|
+
elif fetch == "one":
|
152
|
+
return result.data[0] if result.data else None
|
153
|
+
elif isinstance(fetch, int):
|
154
|
+
return result.data[:fetch]
|
155
|
+
else:
|
156
|
+
return result.data
|
157
|
+
else:
|
158
|
+
# For non-SELECT queries, use RPC
|
159
|
+
result = supabase.rpc('execute_sql', {'sql': query}).execute()
|
160
|
+
return result.data
|
161
|
+
|
162
|
+
except ImportError:
|
163
|
+
raise RuntimeError("supabase-py package is required for REST API mode")
|
164
|
+
except APIError as e:
|
165
|
+
logger.error(f"Supabase REST API error: {e}")
|
166
|
+
raise
|
167
|
+
|
168
|
+
def _extract_table_name(self, query: str) -> str:
|
169
|
+
"""Extract table name from SQL query (simplified)"""
|
170
|
+
# This is a basic implementation - in practice, you'd use a proper SQL parser
|
171
|
+
query = query.upper()
|
172
|
+
from_index = query.find('FROM')
|
173
|
+
if from_index != -1:
|
174
|
+
after_from = query[from_index + 4:].strip()
|
175
|
+
# Find first space or end of string
|
176
|
+
space_index = after_from.find(' ')
|
177
|
+
if space_index != -1:
|
178
|
+
return after_from[:space_index].lower()
|
179
|
+
else:
|
180
|
+
return after_from.lower()
|
181
|
+
return "unknown"
|
182
|
+
|
183
|
+
def get_tables_as_documents(self) -> List[TableDocument]:
|
184
|
+
"""Get tables with Supabase schema considerations"""
|
185
|
+
tables = super().get_tables_as_documents()
|
186
|
+
|
187
|
+
# Filter out Supabase system schemas
|
188
|
+
filtered_tables = []
|
189
|
+
for table in tables:
|
190
|
+
if table.schema_name not in ['auth', 'storage', 'realtime', 'supabase_functions']:
|
191
|
+
filtered_tables.append(table)
|
192
|
+
|
193
|
+
return filtered_tables
|
194
|
+
|
195
|
+
def get_columns_as_documents(self, table_name: str) -> List[ColumnDocument]:
|
196
|
+
"""Get columns with Supabase-specific handling"""
|
197
|
+
columns = super().get_columns_as_documents(table_name)
|
198
|
+
|
199
|
+
# Add Supabase-specific metadata
|
200
|
+
for column in columns:
|
201
|
+
if column.column_name in ['created_at', 'updated_at']:
|
202
|
+
column.comment = f"{column.comment} (Supabase auto-timestamp)"
|
203
|
+
elif column.column_name == 'id':
|
204
|
+
column.comment = f"{column.comment} (Supabase auto-increment)"
|
205
|
+
|
206
|
+
return columns
|
207
|
+
|
208
|
+
def get_unique_values(self) -> Dict[str, Dict[str, List[str]]]:
|
209
|
+
"""Get unique values with Supabase schema filtering"""
|
210
|
+
result = super().get_unique_values()
|
211
|
+
|
212
|
+
# Filter out Supabase system tables
|
213
|
+
filtered_result = {}
|
214
|
+
for table_name, columns in result.items():
|
215
|
+
if not table_name.startswith('auth_') and not table_name.startswith('storage_'):
|
216
|
+
filtered_result[table_name] = columns
|
217
|
+
|
218
|
+
return filtered_result
|
219
|
+
|
220
|
+
def get_example_data(self, table_name: str, number_of_rows: int = 30) -> Dict[str, List[Any]]:
|
221
|
+
"""Get example data with Supabase-specific handling"""
|
222
|
+
if self.use_rest_api:
|
223
|
+
return self._get_example_data_rest(table_name, number_of_rows)
|
224
|
+
else:
|
225
|
+
return super().get_example_data(table_name, number_of_rows)
|
226
|
+
|
227
|
+
def _get_example_data_rest(self, table_name: str, number_of_rows: int = 30) -> Dict[str, List[Any]]:
|
228
|
+
"""Get example data using REST API"""
|
229
|
+
try:
|
230
|
+
from supabase import create_client
|
231
|
+
|
232
|
+
supabase = create_client(self.supabase_url, self.api_key)
|
233
|
+
|
234
|
+
# Get data from REST API
|
235
|
+
result = supabase.table(table_name).select('*').limit(number_of_rows).execute()
|
236
|
+
|
237
|
+
# Convert to the expected format
|
238
|
+
example_data = {}
|
239
|
+
if result.data:
|
240
|
+
for key in result.data[0].keys():
|
241
|
+
example_data[key] = [row.get(key) for row in result.data]
|
242
|
+
|
243
|
+
return example_data
|
244
|
+
|
245
|
+
except ImportError:
|
246
|
+
raise RuntimeError("supabase-py package is required for REST API mode")
|
247
|
+
except Exception as e:
|
248
|
+
logger.error(f"Error getting example data via REST API: {e}")
|
249
|
+
return {}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"""
|
2
|
+
Core components for Thoth SQL Database Manager.
|
3
|
+
"""
|
4
|
+
from .interfaces import DbPlugin, DbAdapter
|
5
|
+
from .registry import DbPluginRegistry
|
6
|
+
from .factory import ThothDbFactory
|
7
|
+
|
8
|
+
__all__ = [
|
9
|
+
"DbPlugin",
|
10
|
+
"DbAdapter",
|
11
|
+
"DbPluginRegistry",
|
12
|
+
"ThothDbFactory",
|
13
|
+
]
|
@@ -0,0 +1,272 @@
|
|
1
|
+
"""
|
2
|
+
Factory for creating database manager instances with plugin support.
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
from typing import Any, Dict, List, Optional
|
6
|
+
from .registry import DbPluginRegistry
|
7
|
+
from .interfaces import DbPlugin
|
8
|
+
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
class ThothDbFactory:
|
13
|
+
"""
|
14
|
+
Factory class for creating database manager instances.
|
15
|
+
Provides plugin-based instantiation with backward compatibility.
|
16
|
+
"""
|
17
|
+
|
18
|
+
@staticmethod
|
19
|
+
def create_manager(db_type: str, db_root_path: Optional[str] = None, db_mode: str = "dev", **kwargs) -> DbPlugin:
|
20
|
+
"""
|
21
|
+
Create a database manager instance using the plugin system.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
db_type: Database type identifier (e.g., 'postgresql', 'sqlite')
|
25
|
+
db_root_path: Path to database root directory (can be in kwargs)
|
26
|
+
db_mode: Database mode (dev, prod, etc.)
|
27
|
+
**kwargs: Database-specific connection parameters
|
28
|
+
|
29
|
+
Returns:
|
30
|
+
Database plugin instance
|
31
|
+
|
32
|
+
Raises:
|
33
|
+
ValueError: If database type is not supported
|
34
|
+
RuntimeError: If plugin initialization fails
|
35
|
+
"""
|
36
|
+
try:
|
37
|
+
# Handle db_root_path from kwargs for backward compatibility
|
38
|
+
if db_root_path is None:
|
39
|
+
db_root_path = kwargs.get('db_root_path')
|
40
|
+
if db_root_path is None:
|
41
|
+
raise ValueError("db_root_path is required")
|
42
|
+
|
43
|
+
# Handle db_mode from kwargs for backward compatibility
|
44
|
+
if 'db_mode' in kwargs:
|
45
|
+
db_mode = kwargs.get('db_mode', db_mode)
|
46
|
+
|
47
|
+
# Remove extracted parameters from kwargs to avoid duplicate parameter errors
|
48
|
+
kwargs_clean = kwargs.copy()
|
49
|
+
kwargs_clean.pop('db_root_path', None)
|
50
|
+
kwargs_clean.pop('db_mode', None)
|
51
|
+
|
52
|
+
# Create plugin instance
|
53
|
+
plugin = DbPluginRegistry.create_plugin(
|
54
|
+
db_type=db_type,
|
55
|
+
db_root_path=db_root_path,
|
56
|
+
db_mode=db_mode,
|
57
|
+
**kwargs_clean
|
58
|
+
)
|
59
|
+
|
60
|
+
# Initialize the plugin
|
61
|
+
plugin.initialize(**kwargs_clean)
|
62
|
+
|
63
|
+
logger.info(f"Successfully created {db_type} manager for {db_root_path}")
|
64
|
+
return plugin
|
65
|
+
|
66
|
+
except Exception as e:
|
67
|
+
logger.error(f"Failed to create {db_type} manager: {e}")
|
68
|
+
raise RuntimeError(f"Failed to create {db_type} manager: {e}") from e
|
69
|
+
|
70
|
+
@staticmethod
|
71
|
+
def list_available_databases() -> List[str]:
|
72
|
+
"""
|
73
|
+
List all available database types.
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
List of supported database type identifiers
|
77
|
+
"""
|
78
|
+
return DbPluginRegistry.list_plugins()
|
79
|
+
|
80
|
+
@staticmethod
|
81
|
+
def get_database_info(db_type: Optional[str] = None) -> Dict[str, Any]:
|
82
|
+
"""
|
83
|
+
Get information about available database plugins.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
db_type: Specific database type, or None for all
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
Database plugin information
|
90
|
+
"""
|
91
|
+
return DbPluginRegistry.get_plugin_info(db_type)
|
92
|
+
|
93
|
+
@staticmethod
|
94
|
+
def validate_database_type(db_type: str) -> bool:
|
95
|
+
"""
|
96
|
+
Check if a database type is supported.
|
97
|
+
|
98
|
+
Args:
|
99
|
+
db_type: Database type identifier
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
True if supported, False otherwise
|
103
|
+
"""
|
104
|
+
return db_type in DbPluginRegistry.list_plugins()
|
105
|
+
|
106
|
+
@staticmethod
|
107
|
+
def get_required_parameters(db_type: str) -> Dict[str, Any]:
|
108
|
+
"""
|
109
|
+
Get required connection parameters for a database type.
|
110
|
+
|
111
|
+
Args:
|
112
|
+
db_type: Database type identifier
|
113
|
+
|
114
|
+
Returns:
|
115
|
+
Dictionary describing required parameters
|
116
|
+
"""
|
117
|
+
try:
|
118
|
+
plugin_class = DbPluginRegistry.get_plugin_class(db_type)
|
119
|
+
|
120
|
+
# This would ideally be defined in the plugin class
|
121
|
+
# For now, return common parameters based on database type
|
122
|
+
common_params = {
|
123
|
+
"postgresql": {
|
124
|
+
"required": ["host", "port", "database", "user", "password"],
|
125
|
+
"optional": ["schema", "sslmode", "connect_timeout"]
|
126
|
+
},
|
127
|
+
"supabase": {
|
128
|
+
"required": ["host", "port", "database", "user", "password"],
|
129
|
+
"optional": ["schema", "sslmode", "connect_timeout", "project_url", "api_key", "use_rest_api"]
|
130
|
+
},
|
131
|
+
"sqlite": {
|
132
|
+
"required": ["database_path"],
|
133
|
+
"optional": ["timeout", "check_same_thread"]
|
134
|
+
},
|
135
|
+
"mysql": {
|
136
|
+
"required": ["host", "port", "database", "user", "password"],
|
137
|
+
"optional": ["charset", "autocommit", "connect_timeout"]
|
138
|
+
},
|
139
|
+
"mariadb": {
|
140
|
+
"required": ["host", "port", "database", "user", "password"],
|
141
|
+
"optional": ["charset", "autocommit", "connect_timeout"]
|
142
|
+
},
|
143
|
+
"sqlserver": {
|
144
|
+
"required": ["server", "database", "user", "password"],
|
145
|
+
"optional": ["driver", "trusted_connection", "timeout"]
|
146
|
+
},
|
147
|
+
"oracle": {
|
148
|
+
"required": ["host", "port", "service_name", "user", "password"],
|
149
|
+
"optional": ["encoding", "nencoding", "threaded"]
|
150
|
+
},
|
151
|
+
"informix": {
|
152
|
+
"required": ["server", "database", "host", "user", "password"],
|
153
|
+
"optional": ["protocol", "service", "timeout"]
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
return common_params.get(db_type, {
|
158
|
+
"required": [],
|
159
|
+
"optional": [],
|
160
|
+
"note": f"Parameters for {db_type} not defined. Check plugin documentation."
|
161
|
+
})
|
162
|
+
|
163
|
+
except ValueError:
|
164
|
+
return {
|
165
|
+
"error": f"Database type '{db_type}' not supported",
|
166
|
+
"available_types": DbPluginRegistry.list_plugins()
|
167
|
+
}
|
168
|
+
|
169
|
+
@staticmethod
|
170
|
+
def create_with_validation(db_type: str, db_root_path: str, db_mode: str = "dev", **kwargs) -> DbPlugin:
|
171
|
+
"""
|
172
|
+
Create a database manager with parameter validation.
|
173
|
+
|
174
|
+
Args:
|
175
|
+
db_type: Database type identifier
|
176
|
+
db_root_path: Path to database root directory
|
177
|
+
db_mode: Database mode
|
178
|
+
**kwargs: Connection parameters
|
179
|
+
|
180
|
+
Returns:
|
181
|
+
Database plugin instance
|
182
|
+
|
183
|
+
Raises:
|
184
|
+
ValueError: If parameters are invalid
|
185
|
+
RuntimeError: If creation fails
|
186
|
+
"""
|
187
|
+
# Validate database type
|
188
|
+
if not ThothDbFactory.validate_database_type(db_type):
|
189
|
+
available = ThothDbFactory.list_available_databases()
|
190
|
+
raise ValueError(f"Unsupported database type '{db_type}'. Available: {available}")
|
191
|
+
|
192
|
+
# Get required parameters
|
193
|
+
param_info = ThothDbFactory.get_required_parameters(db_type)
|
194
|
+
|
195
|
+
if "required" in param_info:
|
196
|
+
# Check required parameters
|
197
|
+
missing_params = []
|
198
|
+
for param in param_info["required"]:
|
199
|
+
if param not in kwargs:
|
200
|
+
missing_params.append(param)
|
201
|
+
|
202
|
+
if missing_params:
|
203
|
+
raise ValueError(f"Missing required parameters for {db_type}: {missing_params}")
|
204
|
+
|
205
|
+
# Create the manager
|
206
|
+
return ThothDbFactory.create_manager(db_type, db_root_path, db_mode, **kwargs)
|
207
|
+
|
208
|
+
@staticmethod
|
209
|
+
def create_from_config(config: Dict[str, Any]) -> DbPlugin:
|
210
|
+
"""
|
211
|
+
Create a database manager from a configuration dictionary.
|
212
|
+
|
213
|
+
Args:
|
214
|
+
config: Configuration dictionary containing all parameters
|
215
|
+
|
216
|
+
Returns:
|
217
|
+
Database plugin instance
|
218
|
+
|
219
|
+
Example config:
|
220
|
+
{
|
221
|
+
"db_type": "postgresql",
|
222
|
+
"db_root_path": "/path/to/db",
|
223
|
+
"db_mode": "dev",
|
224
|
+
"host": "localhost",
|
225
|
+
"port": 5432,
|
226
|
+
"database": "mydb",
|
227
|
+
"user": "user",
|
228
|
+
"password": "pass"
|
229
|
+
}
|
230
|
+
"""
|
231
|
+
# Extract factory parameters
|
232
|
+
db_type = config.pop("db_type")
|
233
|
+
db_root_path = config.pop("db_root_path")
|
234
|
+
db_mode = config.pop("db_mode", "dev")
|
235
|
+
|
236
|
+
# Remaining parameters are connection parameters
|
237
|
+
return ThothDbFactory.create_with_validation(
|
238
|
+
db_type=db_type,
|
239
|
+
db_root_path=db_root_path,
|
240
|
+
db_mode=db_mode,
|
241
|
+
**config
|
242
|
+
)
|
243
|
+
|
244
|
+
@staticmethod
|
245
|
+
def get_plugin_status() -> Dict[str, Any]:
|
246
|
+
"""
|
247
|
+
Get status information about all registered plugins.
|
248
|
+
|
249
|
+
Returns:
|
250
|
+
Status information for all plugins
|
251
|
+
"""
|
252
|
+
plugins = DbPluginRegistry.list_plugins()
|
253
|
+
status = {
|
254
|
+
"total_plugins": len(plugins),
|
255
|
+
"available_types": plugins,
|
256
|
+
"plugins": {}
|
257
|
+
}
|
258
|
+
|
259
|
+
for db_type in plugins:
|
260
|
+
try:
|
261
|
+
plugin_info = DbPluginRegistry.get_plugin_info(db_type)
|
262
|
+
status["plugins"][db_type] = {
|
263
|
+
"status": "available",
|
264
|
+
"info": plugin_info
|
265
|
+
}
|
266
|
+
except Exception as e:
|
267
|
+
status["plugins"][db_type] = {
|
268
|
+
"status": "error",
|
269
|
+
"error": str(e)
|
270
|
+
}
|
271
|
+
|
272
|
+
return status
|