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.
Files changed (43) hide show
  1. thoth_dbmanager/ThothDbManager.py +459 -0
  2. thoth_dbmanager/__init__.py +136 -0
  3. thoth_dbmanager/adapters/__init__.py +21 -0
  4. thoth_dbmanager/adapters/mariadb.py +165 -0
  5. thoth_dbmanager/adapters/mysql.py +165 -0
  6. thoth_dbmanager/adapters/oracle.py +554 -0
  7. thoth_dbmanager/adapters/postgresql.py +444 -0
  8. thoth_dbmanager/adapters/qdrant.py +189 -0
  9. thoth_dbmanager/adapters/sqlite.py +385 -0
  10. thoth_dbmanager/adapters/sqlserver.py +583 -0
  11. thoth_dbmanager/adapters/supabase.py +249 -0
  12. thoth_dbmanager/core/__init__.py +13 -0
  13. thoth_dbmanager/core/factory.py +272 -0
  14. thoth_dbmanager/core/interfaces.py +271 -0
  15. thoth_dbmanager/core/registry.py +220 -0
  16. thoth_dbmanager/documents.py +155 -0
  17. thoth_dbmanager/dynamic_imports.py +250 -0
  18. thoth_dbmanager/helpers/__init__.py +0 -0
  19. thoth_dbmanager/helpers/multi_db_generator.py +508 -0
  20. thoth_dbmanager/helpers/preprocess_values.py +159 -0
  21. thoth_dbmanager/helpers/schema.py +376 -0
  22. thoth_dbmanager/helpers/search.py +117 -0
  23. thoth_dbmanager/lsh/__init__.py +21 -0
  24. thoth_dbmanager/lsh/core.py +182 -0
  25. thoth_dbmanager/lsh/factory.py +76 -0
  26. thoth_dbmanager/lsh/manager.py +170 -0
  27. thoth_dbmanager/lsh/storage.py +96 -0
  28. thoth_dbmanager/plugins/__init__.py +23 -0
  29. thoth_dbmanager/plugins/mariadb.py +436 -0
  30. thoth_dbmanager/plugins/mysql.py +408 -0
  31. thoth_dbmanager/plugins/oracle.py +150 -0
  32. thoth_dbmanager/plugins/postgresql.py +145 -0
  33. thoth_dbmanager/plugins/qdrant.py +41 -0
  34. thoth_dbmanager/plugins/sqlite.py +170 -0
  35. thoth_dbmanager/plugins/sqlserver.py +149 -0
  36. thoth_dbmanager/plugins/supabase.py +224 -0
  37. {thoth_dbmanager-0.4.0.dist-info → thoth_dbmanager-0.4.2.dist-info}/METADATA +9 -6
  38. thoth_dbmanager-0.4.2.dist-info/RECORD +41 -0
  39. thoth_dbmanager-0.4.2.dist-info/top_level.txt +1 -0
  40. thoth_dbmanager-0.4.0.dist-info/RECORD +0 -5
  41. thoth_dbmanager-0.4.0.dist-info/top_level.txt +0 -1
  42. {thoth_dbmanager-0.4.0.dist-info → thoth_dbmanager-0.4.2.dist-info}/WHEEL +0 -0
  43. {thoth_dbmanager-0.4.0.dist-info → thoth_dbmanager-0.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,145 @@
1
+ """
2
+ PostgreSQL plugin implementation.
3
+ """
4
+ import logging
5
+ from typing import Any, Dict, List
6
+ from pathlib import Path
7
+
8
+ from ..core.interfaces import DbPlugin, DbAdapter
9
+ from ..core.registry import register_plugin
10
+ from ..adapters.postgresql import PostgreSQLAdapter
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @register_plugin("postgresql")
16
+ class PostgreSQLPlugin(DbPlugin):
17
+ """
18
+ PostgreSQL database plugin implementation.
19
+ """
20
+
21
+ plugin_name = "PostgreSQL Plugin"
22
+ plugin_version = "1.0.0"
23
+ supported_db_types = ["postgresql", "postgres"]
24
+ required_dependencies = ["psycopg2-binary", "SQLAlchemy"]
25
+
26
+ def __init__(self, db_root_path: str, db_mode: str = "dev", **kwargs):
27
+ super().__init__(db_root_path, db_mode, **kwargs)
28
+ self.db_id = None
29
+ self.db_directory_path = None
30
+
31
+ # LSH manager integration (for backward compatibility)
32
+ self._lsh_manager = None
33
+
34
+ def create_adapter(self, **kwargs) -> DbAdapter:
35
+ """Create and return a PostgreSQL adapter instance"""
36
+ return PostgreSQLAdapter(kwargs)
37
+
38
+ def validate_connection_params(self, **kwargs) -> bool:
39
+ """Validate connection parameters for PostgreSQL"""
40
+ required_params = ['host', 'port', 'database', 'user', 'password']
41
+
42
+ for param in required_params:
43
+ if param not in kwargs:
44
+ logger.error(f"Missing required parameter: {param}")
45
+ return False
46
+
47
+ # Validate types
48
+ try:
49
+ port = int(kwargs['port'])
50
+ if port <= 0 or port > 65535:
51
+ logger.error(f"Invalid port number: {port}")
52
+ return False
53
+ except (ValueError, TypeError):
54
+ logger.error(f"Port must be a valid integer: {kwargs.get('port')}")
55
+ return False
56
+
57
+ # Validate required string parameters are not empty
58
+ string_params = ['host', 'database', 'user', 'password']
59
+ for param in string_params:
60
+ if not kwargs.get(param) or not isinstance(kwargs[param], str):
61
+ logger.error(f"Parameter {param} must be a non-empty string")
62
+ return False
63
+
64
+ return True
65
+
66
+ def initialize(self, **kwargs) -> None:
67
+ """Initialize the PostgreSQL plugin"""
68
+ super().initialize(**kwargs)
69
+
70
+ # Set up database directory path (for LSH and other features)
71
+ if 'database' in kwargs:
72
+ self.db_id = kwargs['database']
73
+ self._setup_directory_path(self.db_id)
74
+
75
+ logger.info(f"PostgreSQL plugin initialized for database: {self.db_id}")
76
+
77
+ def _setup_directory_path(self, db_id: str) -> None:
78
+ """Set up the database directory path"""
79
+ if isinstance(self.db_root_path, str):
80
+ self.db_root_path = Path(self.db_root_path)
81
+
82
+ self.db_directory_path = Path(self.db_root_path) / f"{self.db_mode}_databases" / db_id
83
+ self.db_id = db_id
84
+
85
+ # Reset LSH manager when directory path changes
86
+ self._lsh_manager = None
87
+
88
+ @property
89
+ def lsh_manager(self):
90
+ """Lazy load LSH manager for backward compatibility"""
91
+ if self._lsh_manager is None and self.db_directory_path:
92
+ from ..lsh.manager import LshManager
93
+ self._lsh_manager = LshManager(self.db_directory_path)
94
+ return self._lsh_manager
95
+
96
+ # LSH integration methods for backward compatibility
97
+ def set_lsh(self) -> str:
98
+ """Set LSH for backward compatibility"""
99
+ try:
100
+ if self.lsh_manager and self.lsh_manager.load_lsh():
101
+ return "success"
102
+ else:
103
+ return "error"
104
+ except Exception as e:
105
+ logger.error(f"Error loading LSH: {e}")
106
+ return "error"
107
+
108
+ def query_lsh(self, keyword: str, signature_size: int = 30, n_gram: int = 3, top_n: int = 10) -> Dict[str, Dict[str, List[str]]]:
109
+ """Query LSH for backward compatibility"""
110
+ if self.lsh_manager:
111
+ try:
112
+ return self.lsh_manager.query(
113
+ keyword=keyword,
114
+ signature_size=signature_size,
115
+ n_gram=n_gram,
116
+ top_n=top_n
117
+ )
118
+ except Exception as e:
119
+ logger.error(f"LSH query failed: {e}")
120
+ raise Exception(f"Error querying LSH for {self.db_id}: {e}")
121
+ else:
122
+ raise Exception(f"LSH not available for {self.db_id}")
123
+
124
+ def get_connection_info(self) -> Dict[str, Any]:
125
+ """Get connection information"""
126
+ base_info = super().get_plugin_info()
127
+
128
+ if self.adapter:
129
+ adapter_info = self.adapter.get_connection_info()
130
+ base_info.update(adapter_info)
131
+
132
+ base_info.update({
133
+ "db_id": self.db_id,
134
+ "db_directory_path": str(self.db_directory_path) if self.db_directory_path else None,
135
+ "lsh_available": self.lsh_manager is not None
136
+ })
137
+
138
+ return base_info
139
+
140
+ def get_example_data(self, table_name: str, number_of_rows: int = 30) -> Dict[str, List[Any]]:
141
+ """Get example data through adapter"""
142
+ if self.adapter:
143
+ return self.adapter.get_example_data(table_name, number_of_rows)
144
+ else:
145
+ raise RuntimeError("Plugin not initialized")
@@ -0,0 +1,41 @@
1
+ """
2
+ Qdrant plugin for Thoth SQL Database Manager.
3
+ """
4
+
5
+ from typing import List
6
+ from ..core.interfaces import DbPlugin
7
+ from ..adapters.qdrant import QdrantAdapter
8
+
9
+
10
+ class QdrantPlugin(DbPlugin):
11
+ """Plugin for Qdrant vector database."""
12
+
13
+ plugin_name = "qdrant"
14
+ plugin_version = "1.0.0"
15
+ supported_db_types = ["qdrant"]
16
+ required_dependencies = ["qdrant-client"]
17
+
18
+ def create_adapter(self, **kwargs) -> QdrantAdapter:
19
+ """Create and return a QdrantAdapter instance."""
20
+ return QdrantAdapter(**kwargs)
21
+
22
+ def validate_connection_params(self, **kwargs) -> bool:
23
+ """Validate Qdrant connection parameters."""
24
+ required_params = ['host']
25
+
26
+ for param in required_params:
27
+ if param not in kwargs:
28
+ return False
29
+
30
+ # Validate host format
31
+ host = kwargs.get('host')
32
+ if not isinstance(host, str) or not host.strip():
33
+ return False
34
+
35
+ # Validate port if provided
36
+ port = kwargs.get('port')
37
+ if port is not None:
38
+ if not isinstance(port, int) or port <= 0 or port > 65535:
39
+ return False
40
+
41
+ return True
@@ -0,0 +1,170 @@
1
+ """
2
+ SQLite plugin implementation.
3
+ """
4
+ import logging
5
+ from typing import Any, Dict, List
6
+ from pathlib import Path
7
+
8
+ from ..core.interfaces import DbPlugin, DbAdapter
9
+ from ..core.registry import register_plugin
10
+ from ..adapters.sqlite import SQLiteAdapter
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @register_plugin("sqlite")
16
+ class SQLitePlugin(DbPlugin):
17
+ """
18
+ SQLite database plugin implementation.
19
+ """
20
+
21
+ plugin_name = "SQLite Plugin"
22
+ plugin_version = "1.0.0"
23
+ supported_db_types = ["sqlite", "sqlite3"]
24
+ required_dependencies = ["SQLAlchemy"]
25
+
26
+ def __init__(self, db_root_path: str, db_mode: str = "dev", **kwargs):
27
+ super().__init__(db_root_path, db_mode, **kwargs)
28
+ self.db_id = None
29
+ self.db_directory_path = None
30
+ self.database_path = None
31
+
32
+ # LSH manager integration (for backward compatibility)
33
+ self._lsh_manager = None
34
+
35
+ def create_adapter(self, **kwargs) -> DbAdapter:
36
+ """Create and return a SQLite adapter instance"""
37
+ return SQLiteAdapter(kwargs)
38
+
39
+ def validate_connection_params(self, **kwargs) -> bool:
40
+ """Validate connection parameters for SQLite"""
41
+ # For SQLite, we need either database_path or database_name
42
+ database_path = kwargs.get('database_path')
43
+ database_name = kwargs.get('database_name')
44
+
45
+ if not database_path and not database_name:
46
+ logger.error("Either 'database_path' or 'database_name' is required for SQLite")
47
+ return False
48
+
49
+ if database_path:
50
+ # Validate that the path is a string
51
+ if not isinstance(database_path, str):
52
+ logger.error("database_path must be a string")
53
+ return False
54
+
55
+ # Check if parent directory exists or can be created
56
+ try:
57
+ db_path = Path(database_path)
58
+ db_path.parent.mkdir(parents=True, exist_ok=True)
59
+ except Exception as e:
60
+ logger.error(f"Cannot create directory for database path {database_path}: {e}")
61
+ return False
62
+
63
+ if database_name:
64
+ if not isinstance(database_name, str) or not database_name.strip():
65
+ logger.error("database_name must be a non-empty string")
66
+ return False
67
+
68
+ return True
69
+
70
+ def initialize(self, **kwargs) -> None:
71
+ """Initialize the SQLite plugin"""
72
+ # Handle database path resolution
73
+ database_path = kwargs.get('database_path')
74
+ database_name = kwargs.get('database_name')
75
+
76
+ if not database_path and database_name:
77
+ # Create database path from name and root path
78
+ db_root = Path(self.db_root_path)
79
+ db_dir = db_root / f"{self.db_mode}_databases" / database_name
80
+ db_dir.mkdir(parents=True, exist_ok=True)
81
+ database_path = str(db_dir / f"{database_name}.db")
82
+ kwargs['database_path'] = database_path
83
+
84
+ # Set database path for adapter
85
+ self.database_path = database_path
86
+
87
+ # Initialize with updated kwargs
88
+ super().initialize(**kwargs)
89
+
90
+ # Set up database directory path and ID
91
+ if database_name:
92
+ self.db_id = database_name
93
+ else:
94
+ # Extract database name from path
95
+ self.db_id = Path(database_path).stem
96
+
97
+ self._setup_directory_path(self.db_id)
98
+
99
+ logger.info(f"SQLite plugin initialized for database: {self.db_id} at {self.database_path}")
100
+
101
+ def _setup_directory_path(self, db_id: str) -> None:
102
+ """Set up the database directory path"""
103
+ if isinstance(self.db_root_path, str):
104
+ self.db_root_path = Path(self.db_root_path)
105
+
106
+ self.db_directory_path = Path(self.db_root_path) / f"{self.db_mode}_databases" / db_id
107
+ self.db_id = db_id
108
+
109
+ # Reset LSH manager when directory path changes
110
+ self._lsh_manager = None
111
+
112
+ @property
113
+ def lsh_manager(self):
114
+ """Lazy load LSH manager for backward compatibility"""
115
+ if self._lsh_manager is None and self.db_directory_path:
116
+ from ..lsh.manager import LshManager
117
+ self._lsh_manager = LshManager(self.db_directory_path)
118
+ return self._lsh_manager
119
+
120
+ # LSH integration methods for backward compatibility
121
+ def set_lsh(self) -> str:
122
+ """Set LSH for backward compatibility"""
123
+ try:
124
+ if self.lsh_manager and self.lsh_manager.load_lsh():
125
+ return "success"
126
+ else:
127
+ return "error"
128
+ except Exception as e:
129
+ logger.error(f"Error loading LSH: {e}")
130
+ return "error"
131
+
132
+ def query_lsh(self, keyword: str, signature_size: int = 30, n_gram: int = 3, top_n: int = 10) -> Dict[str, Dict[str, List[str]]]:
133
+ """Query LSH for backward compatibility"""
134
+ if self.lsh_manager:
135
+ try:
136
+ return self.lsh_manager.query(
137
+ keyword=keyword,
138
+ signature_size=signature_size,
139
+ n_gram=n_gram,
140
+ top_n=top_n
141
+ )
142
+ except Exception as e:
143
+ logger.error(f"LSH query failed: {e}")
144
+ raise Exception(f"Error querying LSH for {self.db_id}: {e}")
145
+ else:
146
+ raise Exception(f"LSH not available for {self.db_id}")
147
+
148
+ def get_connection_info(self) -> Dict[str, Any]:
149
+ """Get connection information"""
150
+ base_info = super().get_plugin_info()
151
+
152
+ if self.adapter:
153
+ adapter_info = self.adapter.get_connection_info()
154
+ base_info.update(adapter_info)
155
+
156
+ base_info.update({
157
+ "db_id": self.db_id,
158
+ "database_path": self.database_path,
159
+ "db_directory_path": str(self.db_directory_path) if self.db_directory_path else None,
160
+ "lsh_available": self.lsh_manager is not None
161
+ })
162
+
163
+ return base_info
164
+
165
+ def get_example_data(self, table_name: str, number_of_rows: int = 30) -> Dict[str, List[Any]]:
166
+ """Get example data through adapter"""
167
+ if self.adapter:
168
+ return self.adapter.get_example_data(table_name, number_of_rows)
169
+ else:
170
+ raise RuntimeError("Plugin not initialized")
@@ -0,0 +1,149 @@
1
+ """
2
+ SQL Server plugin implementation.
3
+ """
4
+ import logging
5
+ from typing import Any, Dict, List
6
+ from pathlib import Path
7
+
8
+ from ..core.interfaces import DbPlugin, DbAdapter
9
+ from ..core.registry import register_plugin
10
+ from ..adapters.sqlserver import SQLServerAdapter
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @register_plugin("sqlserver")
16
+ class SQLServerPlugin(DbPlugin):
17
+ """
18
+ SQL Server database plugin implementation.
19
+ """
20
+
21
+ plugin_name = "SQL Server Plugin"
22
+ plugin_version = "1.0.0"
23
+ supported_db_types = ["sqlserver", "mssql"]
24
+ required_dependencies = ["pyodbc", "SQLAlchemy"]
25
+
26
+ def __init__(self, db_root_path: str, db_mode: str = "dev", **kwargs):
27
+ super().__init__(db_root_path, db_mode, **kwargs)
28
+ self.db_id = None
29
+ self.db_directory_path = None
30
+
31
+ # LSH manager integration (for backward compatibility)
32
+ self._lsh_manager = None
33
+
34
+ def create_adapter(self, **kwargs) -> DbAdapter:
35
+ """Create and return a SQL Server adapter instance"""
36
+ return SQLServerAdapter(kwargs)
37
+
38
+ def validate_connection_params(self, **kwargs) -> bool:
39
+ """Validate connection parameters for SQL Server"""
40
+ required_params = ['host', 'port', 'database', 'user', 'password']
41
+
42
+ for param in required_params:
43
+ if param not in kwargs:
44
+ logger.error(f"Missing required parameter: {param}")
45
+ return False
46
+
47
+ # Validate types
48
+ try:
49
+ port = int(kwargs['port'])
50
+ if port <= 0 or port > 65535:
51
+ logger.error(f"Invalid port number: {port}")
52
+ return False
53
+ except (ValueError, TypeError):
54
+ logger.error(f"Port must be a valid integer: {kwargs.get('port')}")
55
+ return False
56
+
57
+ # Validate required string parameters are not empty
58
+ string_params = ['host', 'database', 'user', 'password']
59
+ for param in string_params:
60
+ if not kwargs.get(param) or not isinstance(kwargs[param], str):
61
+ logger.error(f"Parameter {param} must be a non-empty string")
62
+ return False
63
+
64
+ return True
65
+
66
+ def initialize(self, **kwargs) -> None:
67
+ """Initialize the SQL Server plugin"""
68
+ super().initialize(**kwargs)
69
+
70
+ # Set up database directory path (for LSH and other features)
71
+ if 'database' in kwargs:
72
+ self.db_id = kwargs['database']
73
+ self._setup_directory_path(self.db_id)
74
+
75
+ logger.info(f"SQL Server plugin initialized for database: {self.db_id}")
76
+
77
+ def _setup_directory_path(self, db_id: str) -> None:
78
+ """Set up directory path for database-specific files"""
79
+ if self.db_root_path:
80
+ self.db_directory_path = Path(self.db_root_path) / "sqlserver" / db_id
81
+ self.db_directory_path.mkdir(parents=True, exist_ok=True)
82
+
83
+ @property
84
+ def lsh_manager(self):
85
+ """Get LSH manager (for backward compatibility)"""
86
+ return self._lsh_manager
87
+
88
+ def get_connection_info(self) -> Dict[str, Any]:
89
+ """Get connection information"""
90
+ base_info = super().get_plugin_info()
91
+
92
+ if self.adapter:
93
+ adapter_info = self.adapter.get_connection_info()
94
+ base_info.update(adapter_info)
95
+
96
+ base_info.update({
97
+ "db_id": self.db_id,
98
+ "db_directory_path": str(self.db_directory_path) if self.db_directory_path else None,
99
+ "lsh_available": self.lsh_manager is not None
100
+ })
101
+
102
+ return base_info
103
+
104
+ def get_example_data(self, table_name: str, number_of_rows: int = 30) -> Dict[str, List[Any]]:
105
+ """Get example data through adapter"""
106
+ if self.adapter:
107
+ return self.adapter.get_example_data(table_name, number_of_rows)
108
+ else:
109
+ raise RuntimeError("Plugin not initialized")
110
+
111
+ def validate_connection_string(self, connection_string: str) -> bool:
112
+ """Validate SQL Server connection string format."""
113
+ return "mssql+pyodbc://" in connection_string
114
+
115
+ def get_database_info(self) -> Dict[str, Any]:
116
+ """Get SQL Server database information."""
117
+ return {
118
+ "name": self.name,
119
+ "display_name": self.display_name,
120
+ "description": self.description,
121
+ "version": self.version,
122
+ "features": [
123
+ "transactions",
124
+ "foreign_keys",
125
+ "indexes",
126
+ "views",
127
+ "stored_procedures",
128
+ "triggers",
129
+ "computed_columns",
130
+ "partitioning"
131
+ ],
132
+ "data_types": [
133
+ "INT", "BIGINT", "DECIMAL", "NUMERIC", "FLOAT", "REAL",
134
+ "VARCHAR", "NVARCHAR", "TEXT", "NTEXT", "BINARY", "VARBINARY",
135
+ "DATE", "TIME", "DATETIME", "DATETIME2", "SMALLDATETIME",
136
+ "BIT", "UNIQUEIDENTIFIER", "XML"
137
+ ]
138
+ }
139
+
140
+ def get_sample_connection_config(self) -> Dict[str, Any]:
141
+ """Get sample connection configuration."""
142
+ return {
143
+ "host": "localhost",
144
+ "port": 1433,
145
+ "database": "mydatabase",
146
+ "username": "user",
147
+ "password": "password",
148
+ "driver": "ODBC Driver 17 for SQL Server"
149
+ }