Skip to content

Commit dcab3d1

Browse files
feat: Add database adapter interface for multi-backend support (Phase 2)
Implement the adapter pattern to abstract database-specific logic and enable PostgreSQL support alongside MySQL. This is Phase 2 of the PostgreSQL support implementation plan (POSTGRES_SUPPORT.md). New modules: - src/datajoint/adapters/base.py: DatabaseAdapter abstract base class defining the complete interface for database operations (connection management, SQL generation, type mapping, error translation, introspection) - src/datajoint/adapters/mysql.py: MySQLAdapter implementation with extracted MySQL-specific logic (backtick quoting, ON DUPLICATE KEY UPDATE, SHOW commands, information_schema queries) - src/datajoint/adapters/postgres.py: PostgreSQLAdapter implementation with PostgreSQL-specific SQL dialect (double-quote quoting, ON CONFLICT, INTERVAL syntax, enum type management) - src/datajoint/adapters/__init__.py: Adapter registry with get_adapter() factory function Dependencies: - Added optional PostgreSQL dependency: psycopg2-binary>=2.9.0 (install with: pip install 'datajoint[postgres]') Tests: - tests/unit/test_adapters.py: Comprehensive unit tests for both adapters (24 tests for MySQL, 21 tests for PostgreSQL when psycopg2 available) - All tests pass or properly skip when dependencies unavailable - Pre-commit hooks pass (ruff, mypy, codespell) Key features: - Complete abstraction of database-specific SQL generation - Type mapping between DataJoint core types and backend SQL types - Error translation from backend errors to DataJoint exceptions - Introspection query generation for schema, tables, columns, keys - PostgreSQL enum type lifecycle management (CREATE TYPE/DROP TYPE) - No changes to existing DataJoint code (adapters are standalone) Phase 2 Status: ✅ Complete Next phases: Configuration updates, connection refactoring, SQL generation integration, testing with actual databases. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent f4b0258 commit dcab3d1

File tree

6 files changed

+2826
-0
lines changed

6 files changed

+2826
-0
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ test = [
9898
s3 = ["s3fs>=2023.1.0"]
9999
gcs = ["gcsfs>=2023.1.0"]
100100
azure = ["adlfs>=2023.1.0"]
101+
postgres = ["psycopg2-binary>=2.9.0"]
101102
polars = ["polars>=0.20.0"]
102103
arrow = ["pyarrow>=14.0.0"]
103104
test = [

src/datajoint/adapters/__init__.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Database adapter registry for DataJoint.
3+
4+
This module provides the adapter factory function and exports all adapters.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from .base import DatabaseAdapter
10+
from .mysql import MySQLAdapter
11+
from .postgres import PostgreSQLAdapter
12+
13+
__all__ = ["DatabaseAdapter", "MySQLAdapter", "PostgreSQLAdapter", "get_adapter"]
14+
15+
# Adapter registry mapping backend names to adapter classes
16+
ADAPTERS: dict[str, type[DatabaseAdapter]] = {
17+
"mysql": MySQLAdapter,
18+
"postgresql": PostgreSQLAdapter,
19+
"postgres": PostgreSQLAdapter, # Alias for postgresql
20+
}
21+
22+
23+
def get_adapter(backend: str) -> DatabaseAdapter:
24+
"""
25+
Get adapter instance for the specified database backend.
26+
27+
Parameters
28+
----------
29+
backend : str
30+
Backend name: 'mysql', 'postgresql', or 'postgres'.
31+
32+
Returns
33+
-------
34+
DatabaseAdapter
35+
Adapter instance for the specified backend.
36+
37+
Raises
38+
------
39+
ValueError
40+
If the backend is not supported.
41+
42+
Examples
43+
--------
44+
>>> from datajoint.adapters import get_adapter
45+
>>> mysql_adapter = get_adapter('mysql')
46+
>>> postgres_adapter = get_adapter('postgresql')
47+
"""
48+
backend_lower = backend.lower()
49+
50+
if backend_lower not in ADAPTERS:
51+
supported = sorted(set(ADAPTERS.keys()))
52+
raise ValueError(f"Unknown database backend: {backend}. " f"Supported backends: {', '.join(supported)}")
53+
54+
return ADAPTERS[backend_lower]()

0 commit comments

Comments
 (0)