Source code for etlhelper.db_helpers.db_helper
"""
Database helper classes using Factory Pattern
"""
import logging
import os
from abc import (
ABCMeta,
abstractmethod,
)
from etlhelper.exceptions import ETLHelperConnectionError
logger = logging.getLogger('etlhelper')
[docs]
class DbHelper(metaclass=ABCMeta):
"""
Abstract Base Class for DBHelpers
"""
sql_exceptions = None
connect_exceptions = None
table_info_query = None
# The following are used to help create parameterized queries. Although
# paramstyle is required by DBAPI2, most drivers support both a named and
# positional style.
paramstyle = None
named_paramstyle = None
positional_paramstyle = None
@abstractmethod
def __init__(self):
self.sql_exceptions = tuple()
self.connect_exceptions = tuple()
self.required_params = set()
self.paramstyle = ''
self.missing_driver_msg = ''
# This is overridden with real connect method when DbHelper class is
# successfully initialised if driver is installed
self._connect_func = self._raise_missing_driver_error_on_connect
def connect(self, db_params, password_variable=None, **kwargs):
"""
Return a connection (as appropriate), configured for
the database with the password obtained from environment variable. These
connection classes provide Python's dbapi interface (see PEP 249). The
dbapi interface interacts with data row-by-row and is used for low-level
functions.
:param db_params: DbParams object or similar with appropriate attributes
:param password_variable: str, name of environment variable with password
:param kwargs: connection specific keyword arguments e.g. encoding
:return: Connection object
"""
# Prepare connection string
conn_str = self.get_connection_string(db_params, password_variable)
# Create connection
try:
conn = self._connect_func(conn_str, **kwargs)
except self.connect_exceptions as exc:
msg = f"Error connecting to {db_params} via dbapi: {exc}"
raise ETLHelperConnectionError(msg)
return conn
@staticmethod
def get_password(password_variable):
"""
Read password from environment variable.
:param password_variable: str, name of environment variable with password
:return: str, password
:raises ETLHelperDbParamsError: Exception when parameter not defined
"""
if not password_variable:
msg = "Name of password environment variable e.g. ORACLE_PASSWORD is required"
logger.error(msg)
raise ETLHelperConnectionError(msg)
try:
return os.environ[password_variable]
except KeyError:
msg = f"Password environment variable ({password_variable}) is not set"
logger.error(msg)
raise ETLHelperConnectionError(msg)
@staticmethod
@abstractmethod
def get_connection_string(db_params, password_variable):
"""
:returns: str
"""
return
@staticmethod
def executemany(cursor, query, chunk):
"""
Call executemany method appropriate to database. Overridden for PostgreSQL.
:param cursor: Open database cursor.
:param query: str, SQL query
:param chunk: list, Rows of parameters.
"""
cursor.executemany(query, chunk)
@staticmethod
def cursor(conn):
"""
Return a cursor on the connection. Overridded for SQLite.
:param conn: Open database connection.
"""
return conn.cursor()
def _raise_missing_driver_error_on_connect(self, *args, **kwargs):
"""
Raise an exception with helpful message if user tries to connect without driver installed.
This function replaces a connect function, so *args and **kwargs are collected to allow
it to accept whatever would be passed to that function.
"""
raise ETLHelperConnectionError(self.missing_driver_msg)