thoth-dbmanager 0.4.2__tar.gz → 0.4.3__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.
Files changed (60) hide show
  1. {thoth_dbmanager-0.4.2/thoth_dbmanager.egg-info → thoth_dbmanager-0.4.3}/PKG-INFO +13 -11
  2. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/pyproject.toml +18 -17
  3. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_sqlite_manager.py +132 -68
  4. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/__init__.py +1 -7
  5. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/dynamic_imports.py +36 -44
  6. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3/thoth_dbmanager.egg-info}/PKG-INFO +13 -11
  7. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager.egg-info/SOURCES.txt +0 -2
  8. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager.egg-info/requires.txt +12 -12
  9. thoth_dbmanager-0.4.2/thoth_dbmanager/adapters/qdrant.py +0 -189
  10. thoth_dbmanager-0.4.2/thoth_dbmanager/plugins/qdrant.py +0 -41
  11. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/LICENSE +0 -0
  12. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/MANIFEST.in +0 -0
  13. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/README.md +0 -0
  14. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/setup.cfg +0 -0
  15. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_integration_new_architecture.py +0 -0
  16. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_lsh_query.py +0 -0
  17. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_new_architecture.py +0 -0
  18. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_parameter_validation.py +0 -0
  19. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_db_manager_base.py +0 -0
  20. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_informix_manager.py +0 -0
  21. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_mariadb_manager.py +0 -0
  22. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_mysql_manager.py +0 -0
  23. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_oracle_manager.py +0 -0
  24. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_pg_manager.py +0 -0
  25. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_sqlserver_manager.py +0 -0
  26. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/tests/test_thoth_supabase_manager.py +0 -0
  27. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/ThothDbManager.py +0 -0
  28. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/adapters/__init__.py +0 -0
  29. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/adapters/mariadb.py +0 -0
  30. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/adapters/mysql.py +0 -0
  31. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/adapters/oracle.py +0 -0
  32. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/adapters/postgresql.py +0 -0
  33. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/adapters/sqlite.py +0 -0
  34. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/adapters/sqlserver.py +0 -0
  35. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/adapters/supabase.py +0 -0
  36. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/core/__init__.py +0 -0
  37. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/core/factory.py +0 -0
  38. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/core/interfaces.py +0 -0
  39. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/core/registry.py +0 -0
  40. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/documents.py +0 -0
  41. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/helpers/__init__.py +0 -0
  42. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/helpers/multi_db_generator.py +0 -0
  43. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/helpers/preprocess_values.py +0 -0
  44. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/helpers/schema.py +0 -0
  45. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/helpers/search.py +0 -0
  46. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/lsh/__init__.py +0 -0
  47. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/lsh/core.py +0 -0
  48. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/lsh/factory.py +0 -0
  49. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/lsh/manager.py +0 -0
  50. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/lsh/storage.py +0 -0
  51. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/plugins/__init__.py +0 -0
  52. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/plugins/mariadb.py +0 -0
  53. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/plugins/mysql.py +0 -0
  54. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/plugins/oracle.py +0 -0
  55. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/plugins/postgresql.py +0 -0
  56. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/plugins/sqlite.py +0 -0
  57. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/plugins/sqlserver.py +0 -0
  58. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager/plugins/supabase.py +0 -0
  59. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager.egg-info/dependency_links.txt +0 -0
  60. {thoth_dbmanager-0.4.2 → thoth_dbmanager-0.4.3}/thoth_dbmanager.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thoth_dbmanager
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: A Python library for managing SQL databases with support for multiple database types, LSH-based similarity search, and a modern plugin architecture.
5
5
  Author-email: Marco Pancotti <mp@tylconsulting.it>
6
6
  License: MIT
@@ -28,6 +28,8 @@ Requires-Dist: datasketch>=1.5.0
28
28
  Requires-Dist: tqdm>=4.60.0
29
29
  Requires-Dist: SQLAlchemy>=1.4.0
30
30
  Requires-Dist: pydantic>=2.0.0
31
+ Requires-Dist: pandas>=1.3.0
32
+ Requires-Dist: requests>=2.25.0
31
33
  Provides-Extra: postgresql
32
34
  Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgresql"
33
35
  Provides-Extra: mysql
@@ -36,28 +38,24 @@ Provides-Extra: mariadb
36
38
  Requires-Dist: mariadb>=1.1.0; extra == "mariadb"
37
39
  Provides-Extra: sqlserver
38
40
  Requires-Dist: pyodbc>=4.0.0; extra == "sqlserver"
41
+ Requires-Dist: pymssql>=2.3.0; extra == "sqlserver"
39
42
  Provides-Extra: oracle
40
43
  Requires-Dist: cx_Oracle>=8.3.0; extra == "oracle"
41
- Provides-Extra: informix
42
- Requires-Dist: informixdb>=2.2.0; extra == "informix"
44
+ Requires-Dist: oracledb>=3.0.0; extra == "oracle"
43
45
  Provides-Extra: supabase
44
46
  Requires-Dist: supabase>=2.0.0; extra == "supabase"
45
- Requires-Dist: postgrest-py>=0.16.0; extra == "supabase"
46
- Requires-Dist: gotrue-py>=2.0.0; extra == "supabase"
47
+ Requires-Dist: postgrest-py>=0.10.0; extra == "supabase"
48
+ Requires-Dist: gotrue-py>=1.0.0; extra == "supabase"
47
49
  Provides-Extra: sqlite
48
- Provides-Extra: qdrant
49
- Requires-Dist: qdrant-client>=1.7.0; extra == "qdrant"
50
50
  Provides-Extra: all
51
51
  Requires-Dist: psycopg2-binary>=2.9.0; extra == "all"
52
52
  Requires-Dist: mysql-connector-python>=8.0.0; extra == "all"
53
53
  Requires-Dist: mariadb>=1.1.0; extra == "all"
54
54
  Requires-Dist: pyodbc>=4.0.0; extra == "all"
55
+ Requires-Dist: pymssql>=2.3.0; extra == "all"
55
56
  Requires-Dist: cx_Oracle>=8.3.0; extra == "all"
56
- Requires-Dist: informixdb>=2.2.0; extra == "all"
57
+ Requires-Dist: oracledb>=3.0.0; extra == "all"
57
58
  Requires-Dist: supabase>=2.0.0; extra == "all"
58
- Requires-Dist: postgrest-py>=0.16.0; extra == "all"
59
- Requires-Dist: gotrue-py>=2.0.0; extra == "all"
60
- Requires-Dist: qdrant-client>=1.7.0; extra == "all"
61
59
  Provides-Extra: dev
62
60
  Requires-Dist: pytest>=7.0.0; extra == "dev"
63
61
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -65,6 +63,10 @@ Requires-Dist: black>=22.0.0; extra == "dev"
65
63
  Requires-Dist: flake8>=5.0.0; extra == "dev"
66
64
  Requires-Dist: mypy>=1.0.0; extra == "dev"
67
65
  Requires-Dist: pre-commit>=3.0.0; extra == "dev"
66
+ Requires-Dist: build>=1.0.0; extra == "dev"
67
+ Requires-Dist: twine>=4.0.0; extra == "dev"
68
+ Requires-Dist: psutil>=5.8.0; extra == "dev"
69
+ Requires-Dist: docker>=6.0.0; extra == "dev"
68
70
  Provides-Extra: test-postgresql
69
71
  Requires-Dist: pytest>=7.0.0; extra == "test-postgresql"
70
72
  Requires-Dist: psycopg2-binary>=2.9.0; extra == "test-postgresql"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "thoth_dbmanager"
7
- version = "0.4.2"
7
+ version = "0.4.3"
8
8
  authors = [
9
9
  { name="Marco Pancotti", email="mp@tylconsulting.it" },
10
10
  ]
@@ -30,43 +30,44 @@ classifiers = [
30
30
  dependencies = [
31
31
  "datasketch>=1.5.0",
32
32
  "tqdm>=4.60.0",
33
- "SQLAlchemy>=1.4.0",
33
+ "SQLAlchemy>=1.4.0",
34
34
  "pydantic>=2.0.0",
35
+ "pandas>=1.3.0",
36
+ "requests>=2.25.0",
35
37
  ]
36
-
37
38
  [project.optional-dependencies]
38
39
  postgresql = ["psycopg2-binary>=2.9.0"]
39
40
  mysql = ["mysql-connector-python>=8.0.0"]
40
41
  mariadb = ["mariadb>=1.1.0"]
41
- sqlserver = ["pyodbc>=4.0.0"]
42
- oracle = ["cx_Oracle>=8.3.0"]
43
- informix = ["informixdb>=2.2.0"]
44
- supabase = ["supabase>=2.0.0", "postgrest-py>=0.16.0", "gotrue-py>=2.0.0"]
45
- sqlite = [] # Built into Python
46
- qdrant = ["qdrant-client>=1.7.0"]
42
+ sqlserver = ["pyodbc>=4.0.0", "pymssql>=2.3.0"]
43
+ oracle = ["cx_Oracle>=8.3.0", "oracledb>=3.0.0"]
44
+ supabase = ["supabase>=2.0.0", "postgrest-py>=0.10.0", "gotrue-py>=1.0.0"]
45
+ sqlite = []
47
46
 
48
47
  # Convenience groups
49
48
  all = [
50
49
  "psycopg2-binary>=2.9.0",
51
- "mysql-connector-python>=8.0.0",
50
+ "mysql-connector-python>=8.0.0",
52
51
  "mariadb>=1.1.0",
53
52
  "pyodbc>=4.0.0",
53
+ "pymssql>=2.3.0",
54
54
  "cx_Oracle>=8.3.0",
55
- "informixdb>=2.2.0",
55
+ "oracledb>=3.0.0",
56
56
  "supabase>=2.0.0",
57
- "postgrest-py>=0.16.0",
58
- "gotrue-py>=2.0.0",
59
- "qdrant-client>=1.7.0"
60
57
  ]
61
58
 
62
59
  # Development dependencies
63
60
  dev = [
64
- "pytest>=7.0.0",
61
+ "pytest>=7.0.0",
65
62
  "pytest-cov>=4.0.0",
66
- "black>=22.0.0",
63
+ "black>=22.0.0",
67
64
  "flake8>=5.0.0",
68
65
  "mypy>=1.0.0",
69
- "pre-commit>=3.0.0"
66
+ "pre-commit>=3.0.0",
67
+ "build>=1.0.0",
68
+ "twine>=4.0.0",
69
+ "psutil>=5.8.0", # Add this for system resource monitoring
70
+ "docker>=6.0.0", # Add this for Docker management in tests
70
71
  ]
71
72
 
72
73
  # Testing with specific databases
@@ -3,7 +3,7 @@ import os
3
3
  import logging
4
4
  from pathlib import Path
5
5
 
6
- from dbmanager.impl.ThothSqliteManager import ThothSqliteManager
6
+ from thoth_dbmanager import ThothSqliteManager
7
7
 
8
8
  # Configure logging
9
9
  logging.basicConfig(level=logging.INFO,
@@ -47,7 +47,7 @@ class TestThothSqliteManager(unittest.TestCase):
47
47
  try:
48
48
  cls.db_manager = ThothSqliteManager.get_instance(
49
49
  db_id=cls.db_id,
50
- db_root_path="data",
50
+ db_root_path=cls.db_root_path,
51
51
  db_mode=cls.db_mode
52
52
  )
53
53
  logger.info("Successfully connected to california_schools SQLite database")
@@ -59,7 +59,7 @@ class TestThothSqliteManager(unittest.TestCase):
59
59
  """Test that get_instance returns the same instance for same parameters."""
60
60
  second_instance = ThothSqliteManager.get_instance(
61
61
  db_id=self.db_id,
62
- db_root_path="data",
62
+ db_root_path=self.db_root_path,
63
63
  db_mode=self.db_mode
64
64
  )
65
65
 
@@ -71,7 +71,7 @@ class TestThothSqliteManager(unittest.TestCase):
71
71
  # Using a different db_mode should create a new instance
72
72
  different_instance = ThothSqliteManager.get_instance(
73
73
  db_id=self.db_id,
74
- db_root_path="data",
74
+ db_root_path=self.db_root_path,
75
75
  db_mode="different_mode"
76
76
  )
77
77
 
@@ -200,41 +200,58 @@ class TestThothSqliteManager(unittest.TestCase):
200
200
 
201
201
  def test_transaction_rollback(self):
202
202
  """Test transaction rollback on error."""
203
- # First, create a temporary table
204
- create_table_sql = """CREATE TABLE IF NOT EXISTS test_rollback (
203
+ # First, clean up any existing test table
204
+ cleanup_sql = "DROP TABLE IF EXISTS test_rollback"
205
+ try:
206
+ self.db_manager.execute_sql(cleanup_sql, fetch=None)
207
+ except:
208
+ pass # Ignore if table doesn't exist
209
+
210
+ # Create a temporary table
211
+ create_table_sql = """CREATE TABLE test_rollback (
205
212
  id INTEGER PRIMARY KEY,
206
213
  name TEXT
207
214
  )"""
208
215
 
209
216
  try:
210
- self.db_manager.execute_sql(create_table_sql)
217
+ # Create the table (use fetch=None to avoid fetchall() on non-SELECT)
218
+ create_result = self.db_manager.execute_sql(create_table_sql, fetch=None)
219
+ logger.info(f"Table creation result: {create_result}")
211
220
 
212
- # Insert a valid row
221
+ # Insert a valid row (use fetch=None to avoid fetchall() on non-SELECT)
213
222
  insert_sql = """INSERT INTO test_rollback (name) VALUES ('test_value')"""
214
- self.db_manager.execute_sql(insert_sql)
223
+ insert_result = self.db_manager.execute_sql(insert_sql, fetch=None)
224
+ logger.info(f"Insert result: {insert_result}")
215
225
 
216
- # Try an invalid insert that should cause a rollback
226
+ # Try an invalid insert that should cause an error
217
227
  invalid_insert = """INSERT INTO test_rollback (non_existent_column) VALUES ('test')"""
218
228
 
219
229
  with self.assertRaises(Exception):
220
- self.db_manager.execute_sql(invalid_insert)
230
+ self.db_manager.execute_sql(invalid_insert, fetch=None)
221
231
 
222
232
  # Check that the valid insert was committed (SQLite behavior differs from PostgreSQL)
223
- count_sql = """SELECT COUNT(*) as row_count FROM test_rollback"""
224
- result = self.db_manager.execute_sql(count_sql, fetch="one")
233
+ count_sql = """SELECT COUNT(*) as count FROM test_rollback"""
234
+ result = self.db_manager.execute_sql(count_sql)
225
235
 
226
236
  # In SQLite, each statement is its own transaction by default,
227
237
  # so the first INSERT should still be there
228
- self.assertEqual(result['row_count'], 1,
229
- "Valid insert should be committed")
238
+ # Legacy manager returns list of dictionaries
239
+ self.assertIsNotNone(result, "Count result should not be None")
240
+ self.assertIsInstance(result, list, "Result should be a list")
241
+ self.assertEqual(len(result), 1, "Should have one result row")
242
+ self.assertEqual(result[0]['count'], 1, "Valid insert should be committed")
230
243
 
231
- # Clean up - drop the test table
232
- cleanup_sql = "DROP TABLE IF EXISTS test_rollback"
233
- self.db_manager.execute_sql(cleanup_sql)
244
+ logger.info("Transaction rollback test completed successfully")
234
245
 
235
246
  except Exception as e:
236
- logger.warning(f"Transaction test failed: {str(e)}")
237
- self.skipTest(f"Skipping transaction test: {str(e)}")
247
+ logger.error(f"Transaction test failed: {str(e)}")
248
+ raise
249
+ finally:
250
+ # Clean up - drop the test table
251
+ try:
252
+ self.db_manager.execute_sql("DROP TABLE IF EXISTS test_rollback", fetch=None)
253
+ except Exception as cleanup_error:
254
+ logger.warning(f"Cleanup failed: {cleanup_error}")
238
255
 
239
256
  def test_database_file_exists(self):
240
257
  """Test that the database file exists at the expected location."""
@@ -250,69 +267,116 @@ class TestThothSqliteManager(unittest.TestCase):
250
267
 
251
268
  def test_data_types(self):
252
269
  """Test handling of different data types in SQLite."""
253
- # First, get a table to work with
254
- tables_sql = """SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"""
270
+ # Create a test table with various data types
271
+ create_table_sql = """CREATE TABLE IF NOT EXISTS test_data_types (
272
+ id INTEGER PRIMARY KEY,
273
+ text_col TEXT,
274
+ integer_col INTEGER,
275
+ real_col REAL,
276
+ blob_col BLOB,
277
+ null_col TEXT
278
+ )"""
255
279
 
256
- tables = self.db_manager.execute_sql(tables_sql)
257
- if not tables:
258
- self.skipTest("No tables available to test data types")
280
+ try:
281
+ # Create the table
282
+ self.db_manager.execute_sql(create_table_sql, fetch=None)
259
283
 
260
- sample_table = tables[0]['name']
284
+ # Insert test data with various types
285
+ insert_sql = """INSERT INTO test_data_types
286
+ (text_col, integer_col, real_col, blob_col, null_col)
287
+ VALUES ('test_string', 42, 3.14159, 'binary_data', NULL)"""
288
+ self.db_manager.execute_sql(insert_sql, fetch=None)
261
289
 
262
- # Get column information including data types
263
- columns_sql = f"PRAGMA table_info({sample_table})"
290
+ # Get column information including data types
291
+ columns_sql = "PRAGMA table_info(test_data_types)"
292
+ columns = self.db_manager.execute_sql(columns_sql)
264
293
 
265
- columns = self.db_manager.execute_sql(columns_sql)
294
+ # Log column types for the test table
295
+ column_types = {col['name']: col['type'] for col in columns}
296
+ logger.info(f"Column types in test_data_types: {column_types}")
266
297
 
267
- # Log column types for the sample table
268
- column_types = {col['name']: col['type'] for col in columns}
269
- logger.info(f"Column types in {sample_table}: {column_types}")
298
+ # Test a query that returns different data types
299
+ query_sql = "SELECT * FROM test_data_types LIMIT 1"
300
+ result = self.db_manager.execute_sql(query_sql, fetch="one")
270
301
 
271
- # Test a query that returns different data types
272
- query_sql = f"SELECT * FROM {sample_table} LIMIT 1"
302
+ if result:
303
+ # Log the types of values returned
304
+ value_types = {key: type(value).__name__ for key, value in result.items()}
305
+ logger.info(f"Value types in result: {value_types}")
273
306
 
274
- result = self.db_manager.execute_sql(query_sql, fetch="one")
307
+ # Verify that we can access the values
308
+ for key, value in result.items():
309
+ self.assertIsNotNone(key, "Column name should not be None")
310
+ # Value can be None, so we don't assert on that
275
311
 
276
- if result:
277
- # Log the types of values returned
278
- value_types = {key: type(value).__name__ for key, value in result.items()}
279
- logger.info(f"Value types in result: {value_types}")
312
+ # Test specific data type handling
313
+ self.assertEqual(result['text_col'], 'test_string')
314
+ self.assertEqual(result['integer_col'], 42)
315
+ self.assertAlmostEqual(result['real_col'], 3.14159, places=5)
316
+ self.assertIsNone(result['null_col'])
280
317
 
281
- # Verify that we can access the values
282
- for key, value in result.items():
283
- self.assertIsNotNone(key, "Column name should not be None")
284
- # Value can be None, so we don't assert on that
318
+ # Clean up
319
+ self.db_manager.execute_sql("DROP TABLE IF EXISTS test_data_types", fetch=None)
320
+
321
+ except Exception as e:
322
+ logger.error(f"Data types test failed: {str(e)}")
323
+ # Clean up on error
324
+ try:
325
+ self.db_manager.execute_sql("DROP TABLE IF EXISTS test_data_types", fetch=None)
326
+ except:
327
+ pass
328
+ raise
285
329
 
286
330
  def test_large_result_set(self):
287
331
  """Test handling of larger result sets."""
288
- # First, get a table to work with
289
- tables_sql = """SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"""
290
-
291
- tables = self.db_manager.execute_sql(tables_sql)
292
- if not tables:
293
- self.skipTest("No tables available to test large result sets")
294
-
295
- sample_table = tables[0]['name']
296
-
297
- # Get row count for the sample table
298
- count_sql = f"SELECT COUNT(*) as row_count FROM {sample_table}"
299
-
300
- count_result = self.db_manager.execute_sql(count_sql, fetch="one")
301
- row_count = count_result['row_count']
302
-
303
- logger.info(f"Table {sample_table} has {row_count} rows")
304
-
305
- if row_count < 10:
306
- self.skipTest(f"Table {sample_table} has too few rows ({row_count}) to test large result sets")
332
+ # Create a test table with sufficient data
333
+ create_table_sql = """CREATE TABLE IF NOT EXISTS test_large_data (
334
+ id INTEGER PRIMARY KEY,
335
+ name TEXT,
336
+ value INTEGER
337
+ )"""
307
338
 
308
- # Test fetching a larger number of rows
309
- query_sql = f"SELECT * FROM {sample_table} LIMIT 100"
339
+ try:
340
+ self.db_manager.execute_sql(create_table_sql, fetch=None)
341
+
342
+ # Insert multiple rows to create a larger dataset
343
+ # Insert 50 rows of test data
344
+ for i in range(50):
345
+ # Use fetch=None for INSERT statements
346
+ self.db_manager.execute_sql(
347
+ f"INSERT INTO test_large_data (name, value) VALUES ('test_name_{i}', {i})",
348
+ fetch=None
349
+ )
350
+
351
+ # Test fetching all rows
352
+ query_sql = "SELECT * FROM test_large_data"
353
+ result = self.db_manager.execute_sql(query_sql)
354
+
355
+ self.assertIsNotNone(result, "Query result should not be None")
356
+ self.assertIsInstance(result, list, "Result should be a list")
357
+ self.assertEqual(len(result), 50, "Should have 50 rows")
358
+ logger.info(f"Successfully fetched {len(result)} rows from test_large_data")
359
+
360
+ # Test fetching with LIMIT
361
+ limit_query_sql = "SELECT * FROM test_large_data LIMIT 25"
362
+ limit_result = self.db_manager.execute_sql(limit_query_sql)
363
+
364
+ self.assertIsNotNone(limit_result, "Limited query result should not be None")
365
+ self.assertIsInstance(limit_result, list, "Limited result should be a list")
366
+ self.assertEqual(len(limit_result), 25, "Should have 25 rows with LIMIT")
310
367
 
311
- result = self.db_manager.execute_sql(query_sql)
368
+ # Clean up - drop the test table
369
+ cleanup_sql = "DROP TABLE IF EXISTS test_large_data"
370
+ self.db_manager.execute_sql(cleanup_sql, fetch=None)
312
371
 
313
- self.assertIsNotNone(result, "Query result should not be None")
314
- self.assertIsInstance(result, list, "Result should be a list")
315
- logger.info(f"Successfully fetched {len(result)} rows from {sample_table}")
372
+ except Exception as e:
373
+ logger.error(f"Large result set test failed: {str(e)}")
374
+ # Clean up on error
375
+ try:
376
+ self.db_manager.execute_sql("DROP TABLE IF EXISTS test_large_data", fetch=None)
377
+ except:
378
+ pass
379
+ raise
316
380
 
317
381
  @classmethod
318
382
  def tearDownClass(cls):
@@ -43,7 +43,6 @@ from .dynamic_imports import (
43
43
  import_sqlserver,
44
44
  import_oracle,
45
45
  import_mariadb,
46
- import_informix,
47
46
  import_supabase,
48
47
  )
49
48
 
@@ -62,8 +61,6 @@ def __getattr__(name: str):
62
61
  return import_manager('sqlserver')
63
62
  elif name == 'ThothOracleManager':
64
63
  return import_manager('oracle')
65
- elif name == 'ThothInformixManager':
66
- return import_manager('informix')
67
64
  elif name == 'ThothSupabaseManager':
68
65
  return import_manager('supabase')
69
66
 
@@ -79,7 +76,6 @@ __all__ = [
79
76
  "ThothMariaDbManager",
80
77
  "ThothSqlServerManager",
81
78
  "ThothOracleManager",
82
- "ThothInformixManager",
83
79
  "ThothSupabaseManager",
84
80
 
85
81
  # New architecture
@@ -106,7 +102,6 @@ __all__ = [
106
102
  "MariaDBPlugin",
107
103
  "SQLServerPlugin",
108
104
  "OraclePlugin",
109
- "InformixPlugin",
110
105
  "SupabasePlugin",
111
106
 
112
107
  # Adapters
@@ -116,7 +111,6 @@ __all__ = [
116
111
  "MariaDBAdapter",
117
112
  "SQLServerAdapter",
118
113
  "OracleAdapter",
119
- "InformixAdapter",
120
114
  "SupabaseAdapter",
121
115
 
122
116
  # LSH functionality
@@ -133,4 +127,4 @@ __all__ = [
133
127
  "DatabaseImportError",
134
128
  ]
135
129
 
136
- __version__ = "0.4.0"
130
+ __version__ = "0.4.3"
@@ -14,45 +14,32 @@ DATABASE_DEPENDENCIES = {
14
14
  'mariadb': ['mariadb'],
15
15
  'sqlserver': ['pyodbc'],
16
16
  'oracle': ['cx_Oracle'],
17
- 'informix': ['informixdb'],
18
17
  'supabase': ['supabase', 'postgrest', 'gotrue'],
19
18
  'sqlite': [], # Built into Python
20
19
  }
21
20
 
22
- # Mapping of database names to their manager classes
23
- DATABASE_MANAGERS = {
24
- 'postgresql': 'dbmanager.impl.ThothPgManager.ThothPgManager',
25
- 'mysql': 'dbmanager.impl.ThothMySqlManager.ThothMySqlManager',
26
- 'mariadb': 'dbmanager.impl.ThothMariaDbManager.ThothMariaDbManager',
27
- 'sqlserver': 'dbmanager.impl.ThothSqlServerManager.ThothSqlServerManager',
28
- 'oracle': 'dbmanager.impl.ThothOracleManager.ThothOracleManager',
29
- 'informix': 'dbmanager.impl.ThothInformixManager.ThothInformixManager',
30
- 'supabase': 'dbmanager.impl.ThothSupabaseManager.ThothSupabaseManager',
31
- 'sqlite': 'dbmanager.impl.ThothSqliteManager.ThothSqliteManager',
32
- }
21
+ # Note: DATABASE_MANAGERS is no longer used - managers are created via factory pattern
33
22
 
34
23
  # Mapping of database names to their adapter classes
35
24
  DATABASE_ADAPTERS = {
36
- 'postgresql': 'dbmanager.adapters.postgresql.PostgreSQLAdapter',
37
- 'mysql': 'dbmanager.adapters.mysql.MySQLAdapter',
38
- 'mariadb': 'dbmanager.adapters.mariadb.MariaDBAdapter',
39
- 'sqlserver': 'dbmanager.adapters.sqlserver.SQLServerAdapter',
40
- 'oracle': 'dbmanager.adapters.oracle.OracleAdapter',
41
- 'informix': 'dbmanager.adapters.informix.InformixAdapter',
42
- 'supabase': 'dbmanager.adapters.supabase.SupabaseAdapter',
43
- 'sqlite': 'dbmanager.adapters.sqlite.SQLiteAdapter',
25
+ 'postgresql': 'thoth_dbmanager.adapters.postgresql.PostgreSQLAdapter',
26
+ 'mysql': 'thoth_dbmanager.adapters.mysql.MySQLAdapter',
27
+ 'mariadb': 'thoth_dbmanager.adapters.mariadb.MariaDBAdapter',
28
+ 'sqlserver': 'thoth_dbmanager.adapters.sqlserver.SQLServerAdapter',
29
+ 'oracle': 'thoth_dbmanager.adapters.oracle.OracleAdapter',
30
+ 'supabase': 'thoth_dbmanager.adapters.supabase.SupabaseAdapter',
31
+ 'sqlite': 'thoth_dbmanager.adapters.sqlite.SQLiteAdapter',
44
32
  }
45
33
 
46
34
  # Mapping of database names to their plugin classes
47
35
  DATABASE_PLUGINS = {
48
- 'postgresql': 'dbmanager.plugins.postgresql.PostgreSQLPlugin',
49
- 'mysql': 'dbmanager.plugins.mysql.MySQLPlugin',
50
- 'mariadb': 'dbmanager.plugins.mariadb.MariaDBPlugin',
51
- 'sqlserver': 'dbmanager.plugins.sqlserver.SQLServerPlugin',
52
- 'oracle': 'dbmanager.plugins.oracle.OraclePlugin',
53
- 'informix': 'dbmanager.plugins.informix.InformixPlugin',
54
- 'supabase': 'dbmanager.plugins.supabase.SupabasePlugin',
55
- 'sqlite': 'dbmanager.plugins.sqlite.SQLitePlugin',
36
+ 'postgresql': 'thoth_dbmanager.plugins.postgresql.PostgreSQLPlugin',
37
+ 'mysql': 'thoth_dbmanager.plugins.mysql.MySQLPlugin',
38
+ 'mariadb': 'thoth_dbmanager.plugins.mariadb.MariaDBPlugin',
39
+ 'sqlserver': 'thoth_dbmanager.plugins.sqlserver.SQLServerPlugin',
40
+ 'oracle': 'thoth_dbmanager.plugins.oracle.OraclePlugin',
41
+ 'supabase': 'thoth_dbmanager.plugins.supabase.SupabasePlugin',
42
+ 'sqlite': 'thoth_dbmanager.plugins.sqlite.SQLitePlugin',
56
43
  }
57
44
 
58
45
 
@@ -93,30 +80,39 @@ def check_dependencies(database: str) -> List[str]:
93
80
 
94
81
  def import_manager(database: str) -> Any:
95
82
  """
96
- Dynamically import a database manager class.
97
-
83
+ Dynamically import a database manager using the factory pattern.
84
+
98
85
  Args:
99
86
  database: Name of the database
100
-
87
+
101
88
  Returns:
102
- The database manager class
103
-
89
+ The database manager class (factory-created)
90
+
104
91
  Raises:
105
92
  DatabaseImportError: If dependencies are missing
106
93
  ImportError: If the manager class cannot be imported
107
94
  """
108
- if database not in DATABASE_MANAGERS:
95
+ if database not in DATABASE_PLUGINS:
109
96
  raise ValueError(f"Unknown database: {database}")
110
-
97
+
111
98
  # Check dependencies
112
99
  missing_deps = check_dependencies(database)
113
100
  if missing_deps:
114
101
  raise DatabaseImportError(database, missing_deps)
115
-
116
- # Import the manager class
117
- module_path, class_name = DATABASE_MANAGERS[database].rsplit('.', 1)
118
- module = importlib.import_module(module_path)
119
- return getattr(module, class_name)
102
+
103
+ # Import the factory and create a manager class
104
+ from thoth_dbmanager.core.factory import ThothDbFactory
105
+
106
+ # Create a wrapper class that can be instantiated like the old managers
107
+ class DatabaseManagerWrapper:
108
+ def __init__(self, *args, **kwargs):
109
+ # Create manager using factory
110
+ self._manager = ThothDbFactory.create_manager(database, *args, **kwargs)
111
+
112
+ def __getattr__(self, name):
113
+ return getattr(self._manager, name)
114
+
115
+ return DatabaseManagerWrapper
120
116
 
121
117
 
122
118
  def import_adapter(database: str) -> Any:
@@ -241,10 +237,6 @@ def import_mariadb():
241
237
  """Import MariaDB components."""
242
238
  return import_database_components(['mariadb'])['mariadb']
243
239
 
244
- def import_informix():
245
- """Import Informix components."""
246
- return import_database_components(['informix'])['informix']
247
-
248
240
  def import_supabase():
249
241
  """Import Supabase components."""
250
242
  return import_database_components(['supabase'])['supabase']
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thoth_dbmanager
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: A Python library for managing SQL databases with support for multiple database types, LSH-based similarity search, and a modern plugin architecture.
5
5
  Author-email: Marco Pancotti <mp@tylconsulting.it>
6
6
  License: MIT
@@ -28,6 +28,8 @@ Requires-Dist: datasketch>=1.5.0
28
28
  Requires-Dist: tqdm>=4.60.0
29
29
  Requires-Dist: SQLAlchemy>=1.4.0
30
30
  Requires-Dist: pydantic>=2.0.0
31
+ Requires-Dist: pandas>=1.3.0
32
+ Requires-Dist: requests>=2.25.0
31
33
  Provides-Extra: postgresql
32
34
  Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgresql"
33
35
  Provides-Extra: mysql
@@ -36,28 +38,24 @@ Provides-Extra: mariadb
36
38
  Requires-Dist: mariadb>=1.1.0; extra == "mariadb"
37
39
  Provides-Extra: sqlserver
38
40
  Requires-Dist: pyodbc>=4.0.0; extra == "sqlserver"
41
+ Requires-Dist: pymssql>=2.3.0; extra == "sqlserver"
39
42
  Provides-Extra: oracle
40
43
  Requires-Dist: cx_Oracle>=8.3.0; extra == "oracle"
41
- Provides-Extra: informix
42
- Requires-Dist: informixdb>=2.2.0; extra == "informix"
44
+ Requires-Dist: oracledb>=3.0.0; extra == "oracle"
43
45
  Provides-Extra: supabase
44
46
  Requires-Dist: supabase>=2.0.0; extra == "supabase"
45
- Requires-Dist: postgrest-py>=0.16.0; extra == "supabase"
46
- Requires-Dist: gotrue-py>=2.0.0; extra == "supabase"
47
+ Requires-Dist: postgrest-py>=0.10.0; extra == "supabase"
48
+ Requires-Dist: gotrue-py>=1.0.0; extra == "supabase"
47
49
  Provides-Extra: sqlite
48
- Provides-Extra: qdrant
49
- Requires-Dist: qdrant-client>=1.7.0; extra == "qdrant"
50
50
  Provides-Extra: all
51
51
  Requires-Dist: psycopg2-binary>=2.9.0; extra == "all"
52
52
  Requires-Dist: mysql-connector-python>=8.0.0; extra == "all"
53
53
  Requires-Dist: mariadb>=1.1.0; extra == "all"
54
54
  Requires-Dist: pyodbc>=4.0.0; extra == "all"
55
+ Requires-Dist: pymssql>=2.3.0; extra == "all"
55
56
  Requires-Dist: cx_Oracle>=8.3.0; extra == "all"
56
- Requires-Dist: informixdb>=2.2.0; extra == "all"
57
+ Requires-Dist: oracledb>=3.0.0; extra == "all"
57
58
  Requires-Dist: supabase>=2.0.0; extra == "all"
58
- Requires-Dist: postgrest-py>=0.16.0; extra == "all"
59
- Requires-Dist: gotrue-py>=2.0.0; extra == "all"
60
- Requires-Dist: qdrant-client>=1.7.0; extra == "all"
61
59
  Provides-Extra: dev
62
60
  Requires-Dist: pytest>=7.0.0; extra == "dev"
63
61
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -65,6 +63,10 @@ Requires-Dist: black>=22.0.0; extra == "dev"
65
63
  Requires-Dist: flake8>=5.0.0; extra == "dev"
66
64
  Requires-Dist: mypy>=1.0.0; extra == "dev"
67
65
  Requires-Dist: pre-commit>=3.0.0; extra == "dev"
66
+ Requires-Dist: build>=1.0.0; extra == "dev"
67
+ Requires-Dist: twine>=4.0.0; extra == "dev"
68
+ Requires-Dist: psutil>=5.8.0; extra == "dev"
69
+ Requires-Dist: docker>=6.0.0; extra == "dev"
68
70
  Provides-Extra: test-postgresql
69
71
  Requires-Dist: pytest>=7.0.0; extra == "test-postgresql"
70
72
  Requires-Dist: psycopg2-binary>=2.9.0; extra == "test-postgresql"
@@ -29,7 +29,6 @@ thoth_dbmanager/adapters/mariadb.py
29
29
  thoth_dbmanager/adapters/mysql.py
30
30
  thoth_dbmanager/adapters/oracle.py
31
31
  thoth_dbmanager/adapters/postgresql.py
32
- thoth_dbmanager/adapters/qdrant.py
33
32
  thoth_dbmanager/adapters/sqlite.py
34
33
  thoth_dbmanager/adapters/sqlserver.py
35
34
  thoth_dbmanager/adapters/supabase.py
@@ -52,7 +51,6 @@ thoth_dbmanager/plugins/mariadb.py
52
51
  thoth_dbmanager/plugins/mysql.py
53
52
  thoth_dbmanager/plugins/oracle.py
54
53
  thoth_dbmanager/plugins/postgresql.py
55
- thoth_dbmanager/plugins/qdrant.py
56
54
  thoth_dbmanager/plugins/sqlite.py
57
55
  thoth_dbmanager/plugins/sqlserver.py
58
56
  thoth_dbmanager/plugins/supabase.py
@@ -2,18 +2,18 @@ datasketch>=1.5.0
2
2
  tqdm>=4.60.0
3
3
  SQLAlchemy>=1.4.0
4
4
  pydantic>=2.0.0
5
+ pandas>=1.3.0
6
+ requests>=2.25.0
5
7
 
6
8
  [all]
7
9
  psycopg2-binary>=2.9.0
8
10
  mysql-connector-python>=8.0.0
9
11
  mariadb>=1.1.0
10
12
  pyodbc>=4.0.0
13
+ pymssql>=2.3.0
11
14
  cx_Oracle>=8.3.0
12
- informixdb>=2.2.0
15
+ oracledb>=3.0.0
13
16
  supabase>=2.0.0
14
- postgrest-py>=0.16.0
15
- gotrue-py>=2.0.0
16
- qdrant-client>=1.7.0
17
17
 
18
18
  [dev]
19
19
  pytest>=7.0.0
@@ -22,9 +22,10 @@ black>=22.0.0
22
22
  flake8>=5.0.0
23
23
  mypy>=1.0.0
24
24
  pre-commit>=3.0.0
25
-
26
- [informix]
27
- informixdb>=2.2.0
25
+ build>=1.0.0
26
+ twine>=4.0.0
27
+ psutil>=5.8.0
28
+ docker>=6.0.0
28
29
 
29
30
  [mariadb]
30
31
  mariadb>=1.1.0
@@ -34,22 +35,21 @@ mysql-connector-python>=8.0.0
34
35
 
35
36
  [oracle]
36
37
  cx_Oracle>=8.3.0
38
+ oracledb>=3.0.0
37
39
 
38
40
  [postgresql]
39
41
  psycopg2-binary>=2.9.0
40
42
 
41
- [qdrant]
42
- qdrant-client>=1.7.0
43
-
44
43
  [sqlite]
45
44
 
46
45
  [sqlserver]
47
46
  pyodbc>=4.0.0
47
+ pymssql>=2.3.0
48
48
 
49
49
  [supabase]
50
50
  supabase>=2.0.0
51
- postgrest-py>=0.16.0
52
- gotrue-py>=2.0.0
51
+ postgrest-py>=0.10.0
52
+ gotrue-py>=1.0.0
53
53
 
54
54
  [test-mysql]
55
55
  pytest>=7.0.0
@@ -1,189 +0,0 @@
1
- """
2
- Qdrant adapter for Thoth SQL Database Manager.
3
- """
4
-
5
- from typing import Any, Dict, List, Optional, Union
6
- from ..core.interfaces import DbAdapter
7
-
8
-
9
- class QdrantAdapter(DbAdapter):
10
- """
11
- Qdrant vector database adapter implementation.
12
- """
13
-
14
- def __init__(self, **kwargs):
15
- """Initialize Qdrant adapter with connection parameters."""
16
- super().__init__()
17
- self.host = kwargs.get('host', 'localhost')
18
- self.port = kwargs.get('port', 6333)
19
- self.api_key = kwargs.get('api_key')
20
- self.collection_name = kwargs.get('collection_name', 'thoth_documents')
21
- self._client = None
22
-
23
- def connect(self) -> bool:
24
- """Establish connection to Qdrant."""
25
- try:
26
- # Import qdrant_client here to avoid dependency issues
27
- from qdrant_client import QdrantClient
28
-
29
- if self.api_key:
30
- self._client = QdrantClient(
31
- host=self.host,
32
- port=self.port,
33
- api_key=self.api_key
34
- )
35
- else:
36
- self._client = QdrantClient(
37
- host=self.host,
38
- port=self.port
39
- )
40
-
41
- # Test connection
42
- self._client.get_collections()
43
- return True
44
- except Exception as e:
45
- print(f"Failed to connect to Qdrant: {e}")
46
- return False
47
-
48
- def disconnect(self) -> None:
49
- """Disconnect from Qdrant."""
50
- if self._client:
51
- self._client.close()
52
- self._client = None
53
-
54
- def execute_query(self, query: str, params: Optional[Dict] = None,
55
- fetch: Union[str, int] = "all", timeout: int = 60) -> Any:
56
- """
57
- Execute a query against Qdrant.
58
- Note: Qdrant doesn't use SQL, so this adapts the interface.
59
- """
60
- if not self._client:
61
- raise RuntimeError("Not connected to Qdrant")
62
-
63
- # This is a placeholder - adapt based on your specific needs
64
- # Qdrant uses vector search, not SQL queries
65
- return {"message": "Qdrant uses vector search, not SQL queries"}
66
-
67
- def get_tables(self) -> List[Dict[str, str]]:
68
- """Get collections (equivalent to tables in Qdrant)."""
69
- if not self._client:
70
- raise RuntimeError("Not connected to Qdrant")
71
-
72
- try:
73
- collections = self._client.get_collections()
74
- return [
75
- {
76
- "table_name": collection.name,
77
- "table_type": "COLLECTION"
78
- }
79
- for collection in collections.collections
80
- ]
81
- except Exception as e:
82
- print(f"Error getting collections: {e}")
83
- return []
84
-
85
- def get_columns(self, table_name: str) -> List[Dict[str, Any]]:
86
- """Get collection info (equivalent to columns in Qdrant)."""
87
- if not self._client:
88
- raise RuntimeError("Not connected to Qdrant")
89
-
90
- try:
91
- collection_info = self._client.get_collection(table_name)
92
- return [
93
- {
94
- "column_name": "id",
95
- "data_type": "UUID",
96
- "is_nullable": False
97
- },
98
- {
99
- "column_name": "vector",
100
- "data_type": f"VECTOR({collection_info.config.params.vectors.size})",
101
- "is_nullable": False
102
- },
103
- {
104
- "column_name": "payload",
105
- "data_type": "JSON",
106
- "is_nullable": True
107
- }
108
- ]
109
- except Exception as e:
110
- print(f"Error getting collection info: {e}")
111
- return []
112
-
113
- def get_foreign_keys(self) -> List[Dict[str, str]]:
114
- """Get foreign keys (not applicable for Qdrant)."""
115
- return []
116
-
117
- def get_unique_values(self) -> Dict[str, Dict[str, List[str]]]:
118
- """Get unique values from collections."""
119
- if not self._client:
120
- raise RuntimeError("Not connected to Qdrant")
121
-
122
- # This is a simplified implementation
123
- # In practice, you'd need to scroll through points and extract unique payload values
124
- return {}
125
-
126
- def add_documentation(self, doc_type: str, content: Dict[str, Any]) -> str:
127
- """Add documentation to Qdrant collection."""
128
- if not self._client:
129
- raise RuntimeError("Not connected to Qdrant")
130
-
131
- try:
132
- from qdrant_client.models import PointStruct
133
- import uuid
134
-
135
- # Generate a unique ID for the document
136
- doc_id = str(uuid.uuid4())
137
-
138
- # Create a point with the documentation content
139
- point = PointStruct(
140
- id=doc_id,
141
- vector=content.get('vector', [0.0] * 384), # Default vector size
142
- payload={
143
- "doc_type": doc_type,
144
- "content": content
145
- }
146
- )
147
-
148
- # Upsert the point
149
- self._client.upsert(
150
- collection_name=self.collection_name,
151
- points=[point]
152
- )
153
-
154
- return doc_id
155
- except Exception as e:
156
- print(f"Error adding documentation: {e}")
157
- raise
158
-
159
- def delete_collection(self, collection_name: str) -> bool:
160
- """Delete a collection from Qdrant."""
161
- if not self._client:
162
- raise RuntimeError("Not connected to Qdrant")
163
-
164
- try:
165
- self._client.delete_collection(collection_name)
166
- return True
167
- except Exception as e:
168
- print(f"Error deleting collection: {e}")
169
- return False
170
-
171
- def create_collection(self, collection_name: str, vector_size: int = 384) -> bool:
172
- """Create a new collection in Qdrant."""
173
- if not self._client:
174
- raise RuntimeError("Not connected to Qdrant")
175
-
176
- try:
177
- from qdrant_client.models import VectorParams, Distance
178
-
179
- self._client.create_collection(
180
- collection_name=collection_name,
181
- vectors_config=VectorParams(
182
- size=vector_size,
183
- distance=Distance.COSINE
184
- )
185
- )
186
- return True
187
- except Exception as e:
188
- print(f"Error creating collection: {e}")
189
- return False
@@ -1,41 +0,0 @@
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
File without changes