sqlalchemy-jdbcapi 2.0.0.post2__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 (36) hide show
  1. sqlalchemy_jdbcapi/__init__.py +128 -0
  2. sqlalchemy_jdbcapi/_version.py +34 -0
  3. sqlalchemy_jdbcapi/dialects/__init__.py +30 -0
  4. sqlalchemy_jdbcapi/dialects/base.py +879 -0
  5. sqlalchemy_jdbcapi/dialects/db2.py +134 -0
  6. sqlalchemy_jdbcapi/dialects/mssql.py +117 -0
  7. sqlalchemy_jdbcapi/dialects/mysql.py +152 -0
  8. sqlalchemy_jdbcapi/dialects/oceanbase.py +218 -0
  9. sqlalchemy_jdbcapi/dialects/odbc_base.py +389 -0
  10. sqlalchemy_jdbcapi/dialects/odbc_mssql.py +69 -0
  11. sqlalchemy_jdbcapi/dialects/odbc_mysql.py +101 -0
  12. sqlalchemy_jdbcapi/dialects/odbc_oracle.py +80 -0
  13. sqlalchemy_jdbcapi/dialects/odbc_postgresql.py +63 -0
  14. sqlalchemy_jdbcapi/dialects/oracle.py +180 -0
  15. sqlalchemy_jdbcapi/dialects/postgresql.py +110 -0
  16. sqlalchemy_jdbcapi/dialects/sqlite.py +141 -0
  17. sqlalchemy_jdbcapi/jdbc/__init__.py +98 -0
  18. sqlalchemy_jdbcapi/jdbc/connection.py +244 -0
  19. sqlalchemy_jdbcapi/jdbc/cursor.py +329 -0
  20. sqlalchemy_jdbcapi/jdbc/dataframe.py +198 -0
  21. sqlalchemy_jdbcapi/jdbc/driver_manager.py +353 -0
  22. sqlalchemy_jdbcapi/jdbc/exceptions.py +53 -0
  23. sqlalchemy_jdbcapi/jdbc/jvm.py +176 -0
  24. sqlalchemy_jdbcapi/jdbc/type_converter.py +292 -0
  25. sqlalchemy_jdbcapi/jdbc/types.py +72 -0
  26. sqlalchemy_jdbcapi/odbc/__init__.py +46 -0
  27. sqlalchemy_jdbcapi/odbc/connection.py +136 -0
  28. sqlalchemy_jdbcapi/odbc/exceptions.py +48 -0
  29. sqlalchemy_jdbcapi/py.typed +2 -0
  30. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/METADATA +825 -0
  31. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/RECORD +36 -0
  32. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/WHEEL +5 -0
  33. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/entry_points.txt +20 -0
  34. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/licenses/AUTHORS +7 -0
  35. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/licenses/LICENSE +13 -0
  36. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,180 @@
1
+ """
2
+ Oracle JDBC dialect for SQLAlchemy.
3
+
4
+ Provides full Oracle Database support through JDBC, compatible with SQLAlchemy 2.0+.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import re
11
+ from typing import Any
12
+
13
+ from sqlalchemy import exc, sql, util
14
+ from sqlalchemy.dialects.oracle.base import OracleDialect
15
+ from sqlalchemy.engine import Connection
16
+
17
+ from .base import BaseJDBCDialect, JDBCDriverConfig
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class OracleDialect(BaseJDBCDialect, OracleDialect): # type: ignore
23
+ """
24
+ Oracle Database dialect using JDBC driver.
25
+
26
+ Supports Oracle-specific features including:
27
+ - Sequences
28
+ - Synonyms
29
+ - Database links
30
+ - Packages
31
+ - Custom types
32
+
33
+ Connection URL formats:
34
+ jdbcapi+oracle://user:password@host:1521/database
35
+ jdbcapi+oracle://user:password@host:1521/SID
36
+ jdbcapi+oraclejdbc://user:password@host:1521/service_name # Alias
37
+
38
+ For TNS connections:
39
+ jdbcapi+oracle://user:password@tnsname
40
+ """
41
+
42
+ name = "oracle"
43
+ driver = "jdbcapi"
44
+
45
+ # Oracle-specific capabilities
46
+ supports_sequences = True
47
+ supports_native_boolean = False # Oracle < 23c doesn't have native boolean
48
+ supports_identity_columns = True # Oracle 12c+
49
+
50
+ # Override column specifications for JDBC type handling
51
+ colspecs = util.update_copy(
52
+ OracleDialect.colspecs, # type: ignore
53
+ {
54
+ # Add JDBC-specific type mappings here if needed
55
+ },
56
+ )
57
+
58
+ @classmethod
59
+ def get_driver_config(cls) -> JDBCDriverConfig:
60
+ """Get Oracle JDBC driver configuration."""
61
+ return JDBCDriverConfig(
62
+ driver_class="oracle.jdbc.OracleDriver",
63
+ jdbc_url_template="jdbc:oracle:thin:@{host}:{port}/{database}",
64
+ default_port=1521,
65
+ supports_transactions=True,
66
+ supports_schemas=True,
67
+ supports_sequences=True,
68
+ )
69
+
70
+ def create_connect_args(self, url: Any) -> tuple[list[Any], dict[str, Any]]:
71
+ """
72
+ Create connection arguments from SQLAlchemy URL.
73
+
74
+ Handles various Oracle connection formats including TNS names.
75
+ """
76
+ config = self.get_driver_config()
77
+
78
+ # Check if this is a TNS name (no port specified)
79
+ if url.port is None and url.host and "/" not in url.host:
80
+ # TNS name format
81
+ jdbc_url = f"jdbc:oracle:thin:@{url.host}"
82
+ if url.database:
83
+ jdbc_url = f"{jdbc_url}/{url.database}"
84
+ else:
85
+ # Standard format
86
+ jdbc_url = config.format_jdbc_url(
87
+ host=url.host or "localhost",
88
+ port=url.port,
89
+ database=url.database,
90
+ query_params=dict(url.query) if url.query else None,
91
+ )
92
+
93
+ logger.debug(f"Creating Oracle connection to: {jdbc_url}")
94
+
95
+ # Build driver arguments
96
+ driver_args: dict[str, Any] = {}
97
+
98
+ if url.username:
99
+ driver_args["user"] = url.username
100
+ if url.password:
101
+ driver_args["password"] = url.password
102
+
103
+ # Add query parameters as connection properties
104
+ if url.query:
105
+ driver_args.update(url.query)
106
+
107
+ kwargs = {
108
+ "jclassname": config.driver_class,
109
+ "url": jdbc_url,
110
+ "driver_args": driver_args if driver_args else None,
111
+ }
112
+
113
+ return ([], kwargs)
114
+
115
+ def initialize(self, connection: Connection) -> None:
116
+ """Initialize Oracle connection."""
117
+ super().initialize(connection)
118
+ logger.debug("Initialized Oracle JDBC dialect")
119
+
120
+ def _get_server_version_info(self, connection: Connection) -> tuple[int, ...]:
121
+ """
122
+ Get Oracle server version.
123
+
124
+ Returns:
125
+ Tuple of version numbers (e.g., (19, 3, 0))
126
+ """
127
+ try:
128
+ banner = connection.execute(
129
+ sql.text("SELECT BANNER FROM v$version")
130
+ ).scalar()
131
+
132
+ if banner:
133
+ # Parse version from string like:
134
+ # "Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production"
135
+ match = re.search(r"Release ([\d\.]+)", banner)
136
+ if match:
137
+ version_str = match.group(1)
138
+ parts = version_str.split(".")
139
+ return tuple(int(p) for p in parts[:3])
140
+
141
+ except exc.DBAPIError as e:
142
+ logger.warning(f"Failed to get Oracle server version: {e}")
143
+
144
+ # Default fallback
145
+ return (11, 0, 0)
146
+
147
+ @property
148
+ def _is_oracle_8(self) -> bool:
149
+ """Check if connected to Oracle 8 (legacy support)."""
150
+ return getattr(self, "_server_version_info", (11, 0, 0))[0] < 9
151
+
152
+ def _check_max_identifier_length(self, connection: Connection) -> int | None:
153
+ """
154
+ Get maximum identifier length for this Oracle version.
155
+
156
+ Oracle 12.2+ supports 128 characters, earlier versions support 30.
157
+ """
158
+ version = getattr(self, "_server_version_info", (11, 0, 0))
159
+ if version >= (12, 2):
160
+ return 128
161
+ return 30
162
+
163
+ def do_ping(self, dbapi_connection: Any) -> bool:
164
+ """
165
+ Check if Oracle connection is alive.
166
+
167
+ Uses Oracle's dual table for efficiency.
168
+ """
169
+ try:
170
+ cursor = dbapi_connection.cursor()
171
+ cursor.execute("SELECT 1 FROM DUAL")
172
+ cursor.close()
173
+ return True
174
+ except Exception as e:
175
+ logger.debug(f"Oracle ping failed: {e}")
176
+ return False
177
+
178
+
179
+ # Export the dialect
180
+ dialect = OracleDialect
@@ -0,0 +1,110 @@
1
+ """
2
+ PostgreSQL JDBC dialect for SQLAlchemy.
3
+
4
+ Provides full PostgreSQL support through JDBC, compatible with SQLAlchemy 2.0+.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import re
11
+ from typing import Any
12
+
13
+ from sqlalchemy import sql
14
+ from sqlalchemy.dialects.postgresql.base import PGDialect
15
+ from sqlalchemy.engine import Connection
16
+
17
+ from .base import BaseJDBCDialect, JDBCDriverConfig
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class PostgreSQLDialect(BaseJDBCDialect, PGDialect):
23
+ """
24
+ PostgreSQL dialect using JDBC driver.
25
+
26
+ Supports all PostgreSQL-specific features including:
27
+ - Arrays
28
+ - JSONB
29
+ - UUID
30
+ - Full-text search
31
+ - Custom types
32
+
33
+ Connection URL format:
34
+ jdbcapi+postgresql://user:password@host:5432/database
35
+ jdbcapi+pgjdbc://user:password@host:5432/database # Alias
36
+ """
37
+
38
+ name = "postgresql"
39
+ driver = "jdbcapi"
40
+
41
+ # PostgreSQL-specific capabilities
42
+ supports_native_enum = True
43
+ supports_sequences = True
44
+ supports_native_boolean = True
45
+ supports_smallserial = True
46
+
47
+ # Column specifications inherited from PGDialect
48
+ # Can be extended in the future for JDBC-specific type mappings if needed
49
+
50
+ @classmethod
51
+ def get_driver_config(cls) -> JDBCDriverConfig:
52
+ """Get PostgreSQL JDBC driver configuration."""
53
+ return JDBCDriverConfig(
54
+ driver_class="org.postgresql.Driver",
55
+ jdbc_url_template="jdbc:postgresql://{host}:{port}/{database}",
56
+ default_port=5432,
57
+ supports_transactions=True,
58
+ supports_schemas=True,
59
+ supports_sequences=True,
60
+ )
61
+
62
+ def initialize(self, connection: Connection) -> None:
63
+ """Initialize PostgreSQL connection."""
64
+ super().initialize(connection)
65
+
66
+ # Set up PostgreSQL-specific settings
67
+ logger.debug("Initialized PostgreSQL JDBC dialect")
68
+
69
+ def _get_server_version_info(self, connection: Connection) -> tuple[int, ...]:
70
+ """
71
+ Get PostgreSQL server version.
72
+
73
+ Returns:
74
+ Tuple of version numbers (e.g., (14, 5, 0))
75
+ """
76
+ try:
77
+ result = connection.execute(sql.text("SELECT version()")).scalar()
78
+ if result:
79
+ # Parse version from string like:
80
+ # "PostgreSQL 14.5 on x86_64-pc-linux-gnu..."
81
+ match = re.search(r"PostgreSQL (\d+)\.(\d+)(?:\.(\d+))?", result)
82
+ if match:
83
+ major = int(match.group(1))
84
+ minor = int(match.group(2))
85
+ patch = int(match.group(3)) if match.group(3) else 0
86
+ return (major, minor, patch)
87
+ except Exception as e:
88
+ logger.warning(f"Failed to get server version: {e}")
89
+
90
+ # Default fallback
91
+ return (9, 0, 0)
92
+
93
+ def do_ping(self, dbapi_connection: Any) -> bool:
94
+ """
95
+ Check if PostgreSQL connection is alive.
96
+
97
+ Uses a simple SELECT query for efficiency.
98
+ """
99
+ try:
100
+ cursor = dbapi_connection.cursor()
101
+ cursor.execute("SELECT 1")
102
+ cursor.close()
103
+ return True
104
+ except Exception as e:
105
+ logger.debug(f"PostgreSQL ping failed: {e}")
106
+ return False
107
+
108
+
109
+ # Export the dialect
110
+ dialect = PostgreSQLDialect
@@ -0,0 +1,141 @@
1
+ """
2
+ SQLite JDBC dialect for SQLAlchemy.
3
+
4
+ Provides SQLite support through JDBC (mainly for testing or Java interop).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from typing import Any
11
+
12
+ from sqlalchemy import exc, sql
13
+ from sqlalchemy.dialects.sqlite.base import SQLiteDialect as BaseSQLiteDialect
14
+ from sqlalchemy.engine import Connection
15
+
16
+ from .base import BaseJDBCDialect, JDBCDriverConfig
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class SQLiteDialect(BaseJDBCDialect, BaseSQLiteDialect):
22
+ """
23
+ SQLite dialect using JDBC driver.
24
+
25
+ Note: For production use, the native sqlite3 dialect is recommended.
26
+ This JDBC dialect is primarily useful for:
27
+ - Testing JDBC infrastructure
28
+ - Java application integration
29
+ - Environments where native sqlite3 is not available
30
+
31
+ Connection URL format:
32
+ jdbcapi+sqlite:///path/to/database.db
33
+ jdbcapi+sqlite:///:memory: # In-memory database
34
+ """
35
+
36
+ name = "sqlite"
37
+ driver = "jdbcapi"
38
+
39
+ # SQLite capabilities
40
+ supports_native_boolean = False
41
+ supports_sequences = False
42
+ supports_native_enum = False
43
+ supports_native_decimal = False
44
+
45
+ @classmethod
46
+ def import_dbapi(cls) -> Any:
47
+ """
48
+ Import JDBC module.
49
+
50
+ SQLAlchemy's SQLite dialect expects sqlite_version_info attribute,
51
+ so we add it as a wrapper.
52
+ """
53
+ from sqlalchemy_jdbcapi import jdbc
54
+
55
+ # Add sqlite_version_info if not present (required by SQLAlchemy's SQLite dialect)
56
+ if not hasattr(jdbc, "sqlite_version_info"):
57
+ # Use a reasonable default version
58
+ jdbc.sqlite_version_info = (3, 40, 0)
59
+
60
+ return jdbc
61
+
62
+ @classmethod
63
+ def get_driver_config(cls) -> JDBCDriverConfig:
64
+ """Get SQLite JDBC driver configuration."""
65
+ return JDBCDriverConfig(
66
+ driver_class="org.sqlite.JDBC",
67
+ jdbc_url_template="jdbc:sqlite:{database}",
68
+ default_port=0, # Not applicable for SQLite
69
+ supports_transactions=True,
70
+ supports_schemas=False, # SQLite has limited schema support
71
+ supports_sequences=False,
72
+ )
73
+
74
+ def create_connect_args(self, url: Any) -> tuple[list[Any], dict[str, Any]]:
75
+ """
76
+ Create connection arguments for SQLite.
77
+
78
+ Handles file paths and in-memory databases.
79
+ """
80
+ config = self.get_driver_config()
81
+
82
+ # Build JDBC URL
83
+ if url.database:
84
+ if url.database == ":memory:":
85
+ jdbc_url = "jdbc:sqlite::memory:"
86
+ else:
87
+ jdbc_url = f"jdbc:sqlite:{url.database}"
88
+ else:
89
+ # Default to in-memory
90
+ jdbc_url = "jdbc:sqlite::memory:"
91
+
92
+ logger.debug(f"Creating SQLite connection to: {jdbc_url}")
93
+
94
+ kwargs = {
95
+ "jclassname": config.driver_class,
96
+ "url": jdbc_url,
97
+ "driver_args": None,
98
+ }
99
+
100
+ return ([], kwargs)
101
+
102
+ def initialize(self, connection: Connection) -> None:
103
+ """Initialize SQLite connection."""
104
+ super().initialize(connection)
105
+ logger.debug("Initialized SQLite JDBC dialect")
106
+
107
+ def _get_server_version_info(self, connection: Connection) -> tuple[int, ...]:
108
+ """
109
+ Get SQLite version.
110
+
111
+ Returns:
112
+ Tuple of version numbers (e.g., (3, 40, 1))
113
+ """
114
+ try:
115
+ result = connection.execute(sql.text("SELECT sqlite_version()")).scalar()
116
+
117
+ if result:
118
+ # Parse version from string like: "3.40.1"
119
+ parts = result.split(".")
120
+ return tuple(int(p) for p in parts[:3])
121
+
122
+ except exc.DBAPIError as e:
123
+ logger.warning(f"Failed to get SQLite version: {e}")
124
+
125
+ # Default fallback
126
+ return (3, 30, 0)
127
+
128
+ def do_ping(self, dbapi_connection: Any) -> bool:
129
+ """Check if SQLite connection is alive."""
130
+ try:
131
+ cursor = dbapi_connection.cursor()
132
+ cursor.execute("SELECT 1")
133
+ cursor.close()
134
+ return True
135
+ except Exception as e:
136
+ logger.debug(f"SQLite ping failed: {e}")
137
+ return False
138
+
139
+
140
+ # Export the dialect
141
+ dialect = SQLiteDialect
@@ -0,0 +1,98 @@
1
+ """
2
+ JDBC Bridge Layer for SQLAlchemy.
3
+
4
+ This module provides a Python DB-API 2.0 compliant interface to JDBC drivers
5
+ using JPype for JVM interaction. This replaces the unmaintained JayDeBeApi library
6
+ with a modern, type-safe, and feature-rich implementation.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from .connection import Connection, connect
12
+ from .cursor import Cursor
13
+ from .driver_manager import (
14
+ RECOMMENDED_JDBC_DRIVERS,
15
+ JDBCDriver,
16
+ clear_driver_cache,
17
+ download_driver,
18
+ get_driver_cache_dir,
19
+ get_driver_path,
20
+ list_cached_drivers,
21
+ )
22
+ from .exceptions import (
23
+ DatabaseError,
24
+ DataError,
25
+ Error,
26
+ IntegrityError,
27
+ InterfaceError,
28
+ InternalError,
29
+ NotSupportedError,
30
+ OperationalError,
31
+ ProgrammingError,
32
+ Warning,
33
+ )
34
+ from .jvm import get_classpath, is_jvm_started, shutdown_jvm, start_jvm
35
+ from .types import (
36
+ BINARY,
37
+ DATETIME,
38
+ NUMBER,
39
+ ROWID,
40
+ STRING,
41
+ Binary,
42
+ Date,
43
+ DateFromTicks,
44
+ Time,
45
+ TimeFromTicks,
46
+ Timestamp,
47
+ TimestampFromTicks,
48
+ )
49
+
50
+ __all__ = [
51
+ # Core functions
52
+ "connect",
53
+ # JVM management
54
+ "start_jvm",
55
+ "is_jvm_started",
56
+ "shutdown_jvm",
57
+ "get_classpath",
58
+ # Driver management
59
+ "JDBCDriver",
60
+ "RECOMMENDED_JDBC_DRIVERS",
61
+ "download_driver",
62
+ "get_driver_path",
63
+ "get_driver_cache_dir",
64
+ "list_cached_drivers",
65
+ "clear_driver_cache",
66
+ # Classes
67
+ "Connection",
68
+ "Cursor",
69
+ # Exceptions
70
+ "Error",
71
+ "Warning",
72
+ "InterfaceError",
73
+ "DatabaseError",
74
+ "InternalError",
75
+ "OperationalError",
76
+ "ProgrammingError",
77
+ "IntegrityError",
78
+ "DataError",
79
+ "NotSupportedError",
80
+ # Types
81
+ "Date",
82
+ "Time",
83
+ "Timestamp",
84
+ "DateFromTicks",
85
+ "TimeFromTicks",
86
+ "TimestampFromTicks",
87
+ "Binary",
88
+ "STRING",
89
+ "BINARY",
90
+ "NUMBER",
91
+ "DATETIME",
92
+ "ROWID",
93
+ ]
94
+
95
+ # DB-API 2.0 module attributes
96
+ apilevel = "2.0"
97
+ threadsafety = 1 # Threads may share the module, but not connections
98
+ paramstyle = "qmark" # Question mark style, e.g. ...WHERE name=?