_dbi_mssql - Private database interface for MS SQL Server

Purpose:

This module contains the library’s Microsoft SQL Server database methods and attribute accessors; which are a specialised version of the _dbi_base._DBIBase class methods.

Platform:

Linux/Windows | Python 3.10+

Developer:

J Berendt, J Preston

Email:

support@s3dev.uk

Comments:

n/a

Example:

For class-specific usage examples, please refer to the docstring for the following classes:

class _DBIMSSQL(connstr: str)[source]

Bases: _DBIBase

This private class holds the methods and properties which are used for accessing Microsoft SQL Server databases.

Note

This class is not designed to be interacted with directly.

Rather, please use the database.DBInterface class instead, as the proper interface class has an automatic switch for database interfaces, based on the sqlalchemy.Engine object which is created from the connection string.

Parameters:

connstr (str) – The database-specific SQLAlchemy connection string.

Example Use:

This low-level generalised class is designed to be inherited by the calling/wrapping class as:

>>> from dbilib.database import DBInterface

class MyDB(DBInterface):

    def __init__(self, connstr: str):
        super().__init__(connstr=('mssql+pyodbc://'
                                  '<user>:<pwd>@<host>:<port>/'
                                  '<db_name>'))

‘Userless’ trusted connections can be used in the connection string as follows:

>>> from dbilib.database import DBInterface

class MyDB(DBInterface):

    def __init__(self, connstr: str):
        super().__init__(connstr=('mssql+pyodbc'
                                  '://:@<host>:<port>/'
                                  '<dbname>'
                                  '?driver=<driver-name>'
                                  '&trusted_connection=yes'))
backup(table_name: str, verbose: bool = True) ExitCode[source]

Backup the given table to the backup database.

Parameters:
  • table_name (str) – Name of the table to be backed up.

  • verbose (bool, optional) – Display helpful text indicating the status of the backup. Defaults to True.

Important

The backup database (which is implicitly determined by the engine’s database name) must exist, or this method will fail. See below for the backup database’s naming convention.

Due to MSSQL’s (interesting) handling of the CREATE DATABASE statement, the database cannot be created by this method for you; sorry. Cheers MS!

Note

The backup database name is derived by prepending '__bak__' to the database name.

This obfuscation was done intentionally to help prevent a user from click-selecting the wrong database (in SSMS) by accident.

Returns:

The exit code enumerator object associated to the status of the backup process.

Return type:

ExitCode

call_procedure(proc: str, *, params: dict | tuple = None, paramnames: list | tuple = None, raw: bool = True, return_status: bool = False) pd.DataFrame | tuple[pd.DataFrame | tuple, bool][source]

Call a stored procedure, and return as a DataFrame.

Parameters:
  • proc (str) – Name of the stored procedure to call.

  • params (dict) – A dictionary containing the parameter values to be used as key/value pairs, with the keys being the parameter names, and values being the data values passed into the procedure.

  • paramnames (list|tuple, optional) – An iterable object containing the procedure’s parameter names, in order. Defaults to None. If not provided, these are collected from the database via the get_parameter_names() method. For efficiency, the parameter names should be passed if making repeated calls to the procedure.

  • raw (bool, optional) – Return the data in ‘raw’ (tuple) format rather than as a formatted DataFrame. Defaults to True for efficiency.

  • return_status (bool, optional) – Return the method’s success status. Defaults to False.

Returns:

If the return_status argument is True, a tuple of the data and the method’s return status is returned as:

(data, status)

Otherwise, only the data is returned.

Return type:

pd.DataFrame | tuple[pd.DataFrame | tuple, bool]

call_procedure_update(proc: str, *, data: dict, paramnames: list | tuple = None, return_id: bool = False) bool | tuple[source]

Call an update or insert stored procedure.

Note

Results are not returned from this call, only a boolean status flag and the optional last row ID, which must be provided by the USP if desired.

If results are desired, please use the call_procedure() method.

Parameters:
  • proc (str) – Name of the stored procedure to call.

  • data (dict) – A dictionary containing the data to be loaded as key/value pairs, with the keys being the parameter names, and values being the data values.

  • paramnames (list|tuple, optional) – An iterable object containing the procedure’s parameter names, in order. Defaults to None. If not provided, these are collected from the database via the get_parameter_names() method. For efficiency, the parameter names should be passed if making repeated calls to the procedure.

  • return_id (bool, optional) – Return the ID of the last inserted row. If a duplicate constraint is encountered, -1 is returned as the row ID. See note above. Defaults to False.

Returns:

If return_id is False, True is returned if the procedure completed successfully, otherwise False. If return_id is True, a tuple containing the ID of the last inserted row (or -1 on duplicate) and the execution success flag are returned as:

(rowid, success_flag)

Return type:

bool | tuple

call_procedure_update_raw(proc: str, *, data: dict, paramnames: list | tuple = None) None[source]

Call an update or insert stored procedure, without error handling.

Warning

This method is unprotected, perhaps use call_procedure_update() instead.

This ‘raw’ method does not contain an error handler. It is (by design) the responsibility of the caller to contain and control the errors.

The purpose of this raw method is to enable the caller method to contain and control the errors which might be generated from a USP call, for example a duplicate key error.

Parameters:
  • proc (str) – Name of the stored procedure to call.

  • data (dict) – A dictionary containing the data to be loaded as key/value pairs, with the keys being the parameter names, and values being the data values.

  • paramnames (list|tuple, optional) – An iterable object containing the procedure’s parameter names, in order. Defaults to None. If not provided, these are collected from the database via the get_parameter_names() method. For efficiency, the parameter names should be passed if making repeated calls to the procedure.

checksum(table_name: str, database_name: str = None) int | None[source]

Calculate a hash (checksum) on the given table.

Parameters:
  • table_name (str) – is to be calculated.

  • database_name (str) – This argument can be used if the table resides in a different database than the one to which the engine object already points. Defaults to None.

This method wraps the CHECKSUM_AGG and BINARY_CHECKSUM MSSQL functions.

Returns:

A signed integer representation of the table’s hash value, if the table exists. Otherwise, None.

Return type:

int | None

database_exists(database_name: str, verbose: bool = False) bool[source]

Using the engine object, test if the given database exists.

Parameters:
  • database_name (str) – Name of the database to test.

  • verbose (bool, optional) – Print a message if the database does not exist. Defaults to False.

Returns:

True if the given database exists, otherwise False.

Return type:

bool

get_parameter_names(proc: str) tuple[source]

Retrieve the parameter names for the given USP.

For portability, this method has been updated to use an embedded query rather than a USP.

Parameters:

proc (str) – Name of the target stored procedure.

Returns:

A tuple of parameter names for the given USP.

Return type:

tuple

table_exists(table_name: str, database_name: str = None, verbose: bool = False) bool[source]

Using the engine object, test if the given table exists.

Parameters:
  • table_name (str) – Name of the table to test.

  • database_name (str) – This argument can be used if the table resides in a different database than the one to which the engine object already points. Defaults to None.

  • verbose (bool, optional) – Print a message if the table does not exist. Defaults to False.

Returns:

True if the given table exists, otherwise False.

Return type:

bool

_backup(table_name: str, bkdb_name: str) bool[source]

Perform the table backup to the backup database.

Parameters:
  • table_name (str) – Name of the table to be backed up.

  • bkdb_name (str) – Name of the backup database.

Returns:

True if the backup was successful, otherwise False. A successful backup is determined by verifying matching table checksum values between the origin and backup tables.

Return type:

bool

static _print_summary(success: bool) None[source]

Print a short end-of-processing summary.

Parameters:

success (bool) – Flag indicating if the backup was successful.

This message is designed to be short and concise, as the backup is designed to be called by other applications, so a short message is preferable.

_create_engine() sqlalchemy.engine.base.Engine

Create a database engine using the provided environment.

Returns:

A sqlalchemy database engine object.

Return type:

sqlalchemy.engine.base.Engine

static _is_dangerous(stmt: str) bool

Perform a dirty security check for injection attempts.

Parameters:

stmt (str) – SQL statement to be potentially executed.

Raises:

SecurityWarning – If there are multiple semi-colons (;) in the statement, or any comment delimiters (--).

Returns:

False if the checks pass.

Return type:

bool

_report_sa_error(msg: str, error: sqlalchemy.exc.SQLAlchemyError)

Report SQLAlchemy error to the terminal.

Parameters:
  • msg (str) – Additional error to be displayed. This message will be automatically prefixed with ‘[DatabaseError]: ‘

  • error (sqlalchemy.exc.SQLAlchemyError) – Caught error object from the try/except block.

static _result_to_df__cursor(result: sqlalchemy.engine.cursor.CursorResult) DataFrame

Convert a CursorResult object to a DataFrame.

If the cursor did not return results, an empty DataFrame containing the column names only, is returned.

Parameters:

result (sqlalchemy.engine.cursor.CursorResult) – Object to be converted.

Returns:

A pandas.DataFrame object containing the cursor’s data.

Return type:

pd.DataFrame

static _result_to_df__stored(result: object) DataFrame

Convert a MySQLCursor.stored_results object to a DataFrame.

Parameters:
  • result (object) – The cursor.stored_results() object from

  • call. (a sqlalchemy or mysql.connector procedure)

Returns:

A DataFrame containing the results from the procedure call.

Return type:

pd.DataFrame

property database_name

Accessor to the database name used by the engine object.

property engine

Accessor to the sqlalchemy.engine.base.Engine object.

execute_query(stmt: str, params: dict = None, *, raw: bool = True, flat: bool = False, commit: bool = True, ignore_unsafe: bool = False) list | DataFrame | None

Execute a query statement.

Important

The following are not allowed to be executed by this method:

  • Statements containing multiple semi-colons (;).

  • Statements containing a comment delimiter (--).

If found, a SecurityWarning will be raised by the _is_dangerous() method.

Parameters:
  • stmt (str) – Statement to be executed. The parameter bindings are to be written in colon format.

  • params (dict, optional) – Parameter key/value bindings as a dictionary, if applicable. Defaults to None.

  • raw (bool, optional) – Return the data in ‘raw’ (tuple) format rather than as a formatted DataFrame. Defaults to True for efficiency.

  • flat (bool, optional) – Flatten the response. This is useful if the expected response is a collection of single elements. If True, this will return a flattened tuple of elements, rather than a list of tuples, as is the default behaviour. Note (1): If true, and the return is a list of multi-value tuples, only the first element in each tuple will be returned. Note (2): This argument should only be used if raw=True. Defaults to False.

  • commit (bool, optional) – Call COMMIT after the transaction is complete. Defaults to True (for backwards compatibility).

  • ignore_unsafe (bool, optional) –

    Bypass the ‘is dangerous’ check and the run query anyway. This may be required if a script contains multiple statements. Defaults to False.

    WARNING: HC SVNT DRACONES

If the query did not return results and the raw argument is False, an empty DataFrame containing the column names only, is returned.

Note

In the SQL query, the bind parameters are specified by name, using the format :bind_name. The params dictionary argument must contain the associated parameter name/value bindings.

Warning

  1. Generally, whatever statement is passed into this method will be executed, and may have destructive implications.

  2. This method contains a commit call, and the option to disable the COMMIT.

If a statement is passed into this method, and the user has the appropriate permissions - the change will be committed.

… HC SVNT DRACONES.

Returns:

If the raw parameter is True, a list of tuples containing values is returned. Otherwise, a pandas.DataFrame object containing the returned data is returned.

If this method is called with a script which does not return results, for example a CREATE script, None is returned; regardless of the value passed to the raw parameter.

Return type:

list | pd.DataFrame | None