#wip(py): SQL Server Driver.
This commit is contained in:
parent
175cc13022
commit
7a71827a9b
@ -1,17 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Self, Any, Sequence
|
||||
from typing import Self, Any, Sequence, Optional
|
||||
from pyodbc import Connection, connect as sql_server_connect, Cursor
|
||||
from re import Match as REMatch
|
||||
from AnP.Abstracts.DatabaseAbstract import DatabaseAbstract
|
||||
from AnP.Interfaces.Application.AnPInterface import AnPInterface
|
||||
from AnP.Models.DatabaseResponseModel import DatabaseResponseModel
|
||||
from AnP.Utils.Common import Common
|
||||
from AnP.Utils.Patterns import RE
|
||||
from AnP.Utils.Checks import Check
|
||||
|
||||
class SQLServerDriver(DatabaseAbstract):
|
||||
|
||||
def __init__(self:Self, anp:AnPInterface, inputs:dict[str, Any|None]|Sequence[Any|None]) -> None:
|
||||
self.anp:AnPInterface = anp
|
||||
self.__host:str = Common.get_value("host", inputs)
|
||||
self.__port:int = Common.get_value("port", inputs)
|
||||
self.__user:str = Common.get_value("user", inputs)
|
||||
self.__connection:Connection|None = None
|
||||
self.__host:str = Common.get_value("host", inputs, "localhost")
|
||||
self.__port:int = Common.get_value("port", inputs, 1433)
|
||||
self.__user:str = Common.get_value("user", inputs, "sa")
|
||||
self.__password:str = Common.get_value("password", inputs)
|
||||
self.__database:str = Common.get_value("database", inputs)
|
||||
self.__database:str = Common.get_value("database", inputs)
|
||||
self.__driver:str = Common.get_value("driver", inputs, "{ODBC Driver 17 for SQL Server}")
|
||||
self.__string_connection:str = Common.string_variables(Common.get_value("string_connection", inputs, "DRIVER={driver};SERVER={host},{port};UID={user};PWD={password};DATABASE={database}"), {
|
||||
"driver" : self.__driver,
|
||||
"host" : self.__host,
|
||||
"port" : self.__port,
|
||||
"user" : self.__user,
|
||||
"password" : self.__password,
|
||||
"database" : self.__database
|
||||
})
|
||||
|
||||
def connect(self:Self) -> bool:
|
||||
if self.__connection is None:
|
||||
try:
|
||||
self.__connection = sql_server_connect(self.__string_connection, autocommit = True)
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "anp_sql_server_driver_connection_exception", {
|
||||
"driver" : self.__driver,
|
||||
"host" : self.__host,
|
||||
"port" : self.__port,
|
||||
"user" : self.__user,
|
||||
"password" : self.__password,
|
||||
"database" : self.__database
|
||||
})
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_disconnected(self:Self) -> bool:
|
||||
return self.__connection is None
|
||||
|
||||
def disconnect(self:Self) -> bool:
|
||||
if self.__connection is not None:
|
||||
try:
|
||||
self.__connection.close()
|
||||
self.__connection = None
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "anp_sql_server_driver_disconnection_exception", {
|
||||
"driver" : self.__driver,
|
||||
"host" : self.__host,
|
||||
"port" : self.__port,
|
||||
"user" : self.__user,
|
||||
"password" : self.__password,
|
||||
"database" : self.__database
|
||||
})
|
||||
return False
|
||||
return True
|
||||
|
||||
def __process(self:Self, query:str, inputs:Optional[dict[str, Any|None]] = None) -> tuple[str, list[str]]:
|
||||
|
||||
variables:list[str] = []
|
||||
|
||||
def callback(matches:REMatch) -> str:
|
||||
|
||||
key:str = matches.group(1)
|
||||
|
||||
if key is not None:
|
||||
if key in inputs:
|
||||
|
||||
value:Any|None = inputs[key]
|
||||
|
||||
return (
|
||||
"null" if value is None else
|
||||
"'" + value + "'" if Check.is_string(value) else
|
||||
("1" if value is True else "0") if Check.is_boolean(value) else
|
||||
# "'" + value.strftime("%Y-%m-%d %H:%M:%S") + "'" if Check.is_date(value) else
|
||||
"'" + value.isoformat(sep = " ") + "'" if Check.is_date(value) else
|
||||
str(value).replace("'", "''"))
|
||||
else:
|
||||
|
||||
variable_name:str = matches.group(2)
|
||||
|
||||
if variable_name is not None and variable_name not in variables:
|
||||
variables.append(variable_name)
|
||||
|
||||
return matches.group(0)
|
||||
|
||||
query = RE.ODBC_STRING_VARIABLE.sub(callback, query.strip())
|
||||
|
||||
return (
|
||||
"\n".join("declare @" + variable + " varchar(max)" for variable in variables) + "\n\n" +
|
||||
query + "\n\n" +
|
||||
"select " + ", ".join("@" + variable + " as [" + variable + "]" for variable in variables)
|
||||
)
|
||||
|
||||
def execute(self:Self, query:str, inputs:Optional[dict[str, Any|None]] = None) -> DatabaseResponseModel:
|
||||
|
||||
response:DatabaseResponseModel = DatabaseResponseModel()
|
||||
|
||||
self._ready()
|
||||
|
||||
if self.connect():
|
||||
|
||||
cursor:Cursor = self.__connection.cursor()
|
||||
variables:list[str]
|
||||
i:int = 0
|
||||
|
||||
query, variables = self.__process(query, inputs)
|
||||
|
||||
try:
|
||||
|
||||
cursor.execute(query)
|
||||
|
||||
while True:
|
||||
if cursor.description is not None:
|
||||
|
||||
columns:list[str] = [column[0] for column in cursor.description]
|
||||
|
||||
response.add(i, [dict(zip(columns, row)) for row in cursor.fetchall()])
|
||||
i += 1
|
||||
|
||||
if not cursor.nextset():
|
||||
break
|
||||
|
||||
self.__connection.commit()
|
||||
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "anp_sql_server_driver_execution_exception", {
|
||||
"driver" : self.__driver,
|
||||
"host" : self.__host,
|
||||
"port" : self.__port,
|
||||
"user" : self.__user,
|
||||
"password" : self.__password,
|
||||
"database" : self.__database,
|
||||
"query" : query
|
||||
})
|
||||
self.__connection.rollback()
|
||||
|
||||
response.set(variables)
|
||||
|
||||
self._unbusy()
|
||||
|
||||
return response
|
||||
@ -28,7 +28,9 @@ class SQLiteDriver(DatabaseAbstract):
|
||||
self.__connection = sqlite_connect(self.__path, check_same_thread = False)
|
||||
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "anp_sqlite_driver_connection_exception")
|
||||
self.anp.exception(exception, "anp_sqlite_driver_connection_exception", {
|
||||
"path" : self.__path
|
||||
})
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -40,10 +42,12 @@ class SQLiteDriver(DatabaseAbstract):
|
||||
try:
|
||||
self.__connection.close()
|
||||
self.__connection = None
|
||||
return True
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "anp_sqlite_driver_disconnection_exception")
|
||||
return False
|
||||
self.anp.exception(exception, "anp_sqlite_driver_disconnection_exception", {
|
||||
"path" : self.__path
|
||||
})
|
||||
return False
|
||||
return True
|
||||
|
||||
def __set_variables(self:Self, query:str, inputs:Optional[dict[str, Any|None]] = None) -> str:
|
||||
|
||||
@ -57,16 +61,16 @@ class SQLiteDriver(DatabaseAbstract):
|
||||
|
||||
return (
|
||||
"null" if value is None else
|
||||
"'" + value.replace("'", "''") + "'" if Check.is_string(value) else
|
||||
"'" + value + "'" if Check.is_string(value) else
|
||||
("1" if value is True else "0") if Check.is_boolean(value) else
|
||||
# "'" + value.strftime("%Y-%m-%d %H:%M:%S") + "'" if Check.is_date(value) else
|
||||
"'" + value.isoformat(sep = " ") + "'" if Check.is_date(value) else
|
||||
str(value))
|
||||
str(value).replace("'", "''"))
|
||||
return matches.group(0)
|
||||
|
||||
return RE.STRING_VARIABLES.sub(callback, query.strip())
|
||||
|
||||
def __process(self:Self, query:str, inputs:Optional[dict[str, Any|None]] = None) -> Any|None:
|
||||
def __process(self:Self, query:str, inputs:Optional[dict[str, Any|None]] = None) -> tuple[list[str], list[str]]:
|
||||
|
||||
sentences:list[str] = []
|
||||
variables:list[str] = []
|
||||
@ -154,6 +158,7 @@ class SQLiteDriver(DatabaseAbstract):
|
||||
|
||||
except Exception as exception:
|
||||
self.anp.exception(exception, "anp_sqlite_driver_execution_exception", {
|
||||
"path" : self.__path,
|
||||
"i" : i,
|
||||
"sentence" : sentence
|
||||
})
|
||||
|
||||
@ -19,4 +19,5 @@ class RE:
|
||||
DOUBLE_NEW_LINE:REPattern = re_compile(r'(?:\r\n){2}|\r{2}|\n{2}')
|
||||
HTTP_HEADER_PARAMETER:REPattern = re_compile(r'([^:]+):\s*(.+)')
|
||||
HTTP_REQUEST:REPattern = re_compile(r'^([^\s]+)\s([^\s\?\#]+)(?:\?([^#]+))?(?:\#([^\s]+))?\s([^\/]+)\/([0-9\.]+)$')
|
||||
PYTHON_MODULES_PATH:REPattern = re_compile(r'[\/\\]python[0-9\.]+[\/\\]site-packages[\/\\]?$', RE_IGNORECASE)
|
||||
PYTHON_MODULES_PATH:REPattern = re_compile(r'[\/\\]python[0-9\.]+[\/\\]site-packages[\/\\]?$', RE_IGNORECASE)
|
||||
ODBC_STRING_VARIABLE:REPattern = re_compile(r'\{([a-z_][a-z0-9_]*)\}|@([a-z0-9_]+)', RE_IGNORECASE)
|
||||
Loading…
Reference in New Issue
Block a user