Source code for sqlalchemy_jdbcapi.dialects.gbase

"""
GBase 8s JDBC dialect for SQLAlchemy.

Provides support for GBase 8s (South Technology Group) database,
which is compatible with Informix and supports PostgreSQL-like features.
"""

from __future__ import annotations

import logging
import re
from typing import Any

from sqlalchemy import exc, sql
from sqlalchemy.engine import Connection, Dialect

from .base import BaseJDBCDialect, JDBCDriverConfig

logger = logging.getLogger(__name__)


[docs] class GBase8sDialect(BaseJDBCDialect, Dialect): # type: ignore """ GBase 8s dialect using JDBC driver. GBase 8s is an enterprise-grade database system from South Technology Group, based on Informix architecture with PostgreSQL-compatible features. Supports GBase 8s-specific features including: - Sequences - Identity columns - Stored procedures - Full-text search - JSON support (GBase 8s 8.8+) - Spatial data types Connection URL format: jdbcapi+gbase8s://user:password@host:9088/database jdbcapi+gbase://user:password@host:9088/database (alias) """ name = "gbase8s" driver = "jdbcapi" # GBase 8s capabilities supports_native_boolean = False # Uses SMALLINT for boolean supports_sequences = True supports_identity_columns = True supports_native_enum = False supports_multivalues_insert = True supports_statement_cache = True # Isolation levels supported by GBase 8s _isolation_lookup = { "SERIALIZABLE": "SERIALIZABLE", "READ_UNCOMMITTED": "READ UNCOMMITTED", "READ_COMMITTED": "READ COMMITTED", "REPEATABLE_READ": "REPEATABLE READ", }
[docs] @classmethod def get_driver_config(cls) -> JDBCDriverConfig: """Get GBase 8s JDBC driver configuration.""" return JDBCDriverConfig( driver_class="com.gbasedbt.jdbc.Driver", jdbc_url_template="jdbc:gbasedbt-sqli://{host}:{port}/{database}", default_port=9088, supports_transactions=True, supports_schemas=True, supports_sequences=True, )
[docs] def initialize(self, connection: Connection) -> None: """Initialize GBase 8s connection.""" if not hasattr(self, "_server_version_info"): self._server_version_info = self._get_server_version_info(connection) logger.debug("Initialized GBase 8s JDBC dialect")
def _get_server_version_info(self, connection: Connection) -> tuple[int, ...]: """ Get GBase 8s server version. Returns: Tuple of version numbers (e.g., (8, 8, 1)) """ try: # GBase 8s version query result = connection.execute( sql.text( "SELECT DBINFO('version', 'full') FROM systables WHERE tabid = 1" ) ).scalar() if result: # Parse version from string like: # "GBase 8s Server Version 8.8.1.0" match = re.search(r"(\d+)\.(\d+)\.(\d+)", str(result)) if match: major = int(match.group(1)) minor = int(match.group(2)) patch = int(match.group(3)) return (major, minor, patch) except exc.DBAPIError as e: logger.warning(f"Failed to get GBase 8s server version: {e}") # Fallback: try alternative query try: result = connection.execute( sql.text( "SELECT FIRST 1 DBINFO('version', 'major') || '.' || DBINFO('version', 'minor') FROM systables" ) ).scalar() if result: match = re.search(r"(\d+)\.(\d+)", str(result)) if match: return (int(match.group(1)), int(match.group(2)), 0) except exc.DBAPIError: pass # Default fallback return (8, 8, 0)
[docs] def do_ping(self, dbapi_connection: Any) -> bool: """Check if GBase 8s connection is alive.""" try: cursor = dbapi_connection.cursor() cursor.execute("SELECT 1 FROM systables WHERE tabid = 1") cursor.close() return True except Exception as e: logger.debug(f"GBase 8s ping failed: {e}") return False
[docs] def get_isolation_level(self, dbapi_connection: Any) -> str: """Get current transaction isolation level.""" try: cursor = dbapi_connection.cursor() cursor.execute("SELECT DBINFO('isolation') FROM systables WHERE tabid = 1") result = cursor.fetchone() cursor.close() if result: return str(result[0]) except Exception as e: logger.debug(f"Failed to get isolation level: {e}") return "READ COMMITTED"
[docs] def set_isolation_level(self, dbapi_connection: Any, level: str) -> None: """Set transaction isolation level.""" if level in self._isolation_lookup: level = self._isolation_lookup[level] # Validate isolation level to prevent SQL injection valid_levels = { "SERIALIZABLE", "READ UNCOMMITTED", "READ COMMITTED", "REPEATABLE READ", } if level.upper() not in valid_levels: logger.warning(f"Invalid isolation level: {level}") return try: cursor = dbapi_connection.cursor() # Use validated level - safe from SQL injection cursor.execute(f"SET ISOLATION TO {level}") cursor.close() except Exception as e: logger.warning(f"Failed to set isolation level: {e}")
[docs] class GBaseDialect(GBase8sDialect): """Alias for GBase8sDialect for convenience.""" name = "gbase"
# Export the dialect dialect = GBase8sDialect