Files
shishirpatil--gorilla/goex/exec_engine/db_manager.py
T
wehub-resource-sync bbfc60cd69
Publish BFCL to PyPI / build_and_publish (push) Has been cancelled
Update API Zoo Data / send-updates (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:37:27 +08:00

303 lines
10 KiB
Python

from exec_engine.docker_sandbox import DockerSandbox
"""
This module will handle all database interactions
The DBManager class is the base class for all database managers
"""
class DBManager:
"""Base class for all DB connectors.
Attributes:
connection_config (type): JSON Config for connection.
Methods:
connect: Establish connections to the DB
execute_db_call: Execute DB call
commit_db_calls: Commit DB calls
rollback_db_calls: Rollback DB calls
close: Close the connection to the database
"""
def __init__(self, connection_config):
"""Initialize the DBManager.
Args:
connection_config (dict): Configuration for connecting to the database. This can be a path for file-based databases or connection details for server-based databases.
"""
self.connection_config = connection_config
self.docker_sandbox = None
def connect(self):
"""Establish connection to the database."""
raise NotImplementedError
def get_schema_as_string(self):
prompt = ""
for table_name, schema in self.schema.items():
prompt += f"Table '{table_name}':\n"
for column in schema:
column_name, column_type, is_nullable, key, default, extra = column
prompt += f"- Column '{column_name}' of type '{column_type}'"
if is_nullable == 'NO':
prompt += ", not nullable"
if key == 'PRI':
prompt += ", primary key"
prompt += "\n"
prompt += "\n"
return prompt
def task_to_prompt(self, task_description, forward=True):
"""Format the schemas of all tables into a prompt for GPT, including a task description."""
prompt = ""
if self.schema == None:
raise Exception("Please connect to the database first.")
if self.schema:
"No schema information available."
prompt += "Given the following table schemas in a sqlite database:\n\n"
prompt += self.get_schema_as_string()
if forward:
prompt += f"Task: {task_description}\n\n"
prompt += "Based on the task, select the most appropriate table and generate an SQL command to complete the task. In the output, only include SQL code."
else:
prompt += f"SQL command: {task_description}\n\n"
prompt += "Based on the SQL command and the given table schemas, generate a reverse command to reverse the SQL command. In the output, only include SQL code."
return prompt
def execute_db_call(self, call):
"""Execute DB call.
Args:
call (str): DB call to execute.
"""
raise NotImplementedError
def fetch_db_call(self, call):
raise NotImplementedError
def commit_db_calls(self):
"""Commit DB calls."""
raise NotImplementedError
def rollback_db_calls(self):
"""Rollback DB calls not committed"""
raise NotImplementedError
def close(self):
"""Close the connection to the database."""
raise NotImplementedError
class SQLiteManager(DBManager):
"""SQLite database manager.
Attributes:
_sqlite_imported (bool): flag to check if sqlite3 is imported.
Methods:
connect: Establish connections to the DB
execute_db_call: Execute SQL call
commit_db_calls: Commit SQL calls
rollback_db_calls: Rollback SQL calls
close: Close the connection to the database
"""
_sqlite_imported = False # flag to check if sqlite3 is imported
db_type = "sqlite"
TEST_CONFIG = "" # No config required to access sqlite
def __init__(self, connection_config, docker_sandbox: DockerSandbox = None):
"""Initialize the SQLLiteManager.
Args:
connection_config(str): path to the database file.
"""
if not SQLiteManager._sqlite_imported:
global sqlite3
import sqlite3
SQLiteManager._sqlite_imported = True
keys = connection_config.keys()
if any(key not in keys for key in ['path']):
raise ValueError("Failed to initialize SQLite Manager due to bad configs")
self.db_path = connection_config['path']
if not self.db_path:
raise ValueError("Failed to initialize SQLite Manager due to missing path")
def update_schema_info(self):
schema_info = {}
self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = self.cursor.fetchall()
for (table_name,) in tables:
self.cursor.execute(f"PRAGMA table_info({table_name});")
schema_info[table_name] = self.cursor.fetchall()
self.schema = schema_info
def connect(self):
"""Establish connection to the SQLLite3 database and create a cursor."""
self.conn = sqlite3.connect(self.db_path)
self.cursor = self.conn.cursor()
self.update_schema_info()
def execute_db_call(self, call):
if not self.conn:
self.connect()
try:
commands_list = [cmd.strip() for cmd in call.split(';') if cmd.strip() and not cmd.strip().startswith('--')]
for command in commands_list:
if command.upper().startswith('SELECT'):
self.cursor.execute(command)
print(self.cursor.fetchall())
else:
self.cursor.execute(command)
self.update_schema_info()
return 0
except Exception as e:
return 1
def fetch_db_call(self, call):
if not self.conn:
self.connect()
try:
self.cursor.execute(call)
ret_val = self.cursor.fetchall()
self.update_schema_info()
return ret_val
except Exception as e:
return []
def commit_db_calls(self):
"""Commit SQL calls."""
if not self.conn:
self.connect()
self.conn.commit()
def rollback_db_calls(self):
"""Rollback SQL calls not committed"""
if not self.conn:
self.connect()
self.conn.rollback()
self.close()
self.connect()
def close(self):
if self.conn:
self.cursor.close()
self.conn.close()
class MySQLManager(DBManager):
"""MySQL database manager.
Attributes:
_mysql_imported (bool): flag to check if pymysql is imported.
Methods:
connect: Establish connections to the DB
execute_db_call: Execute SQL call
commit_db_calls: Commit SQL calls
rollback_db_calls: Rollback SQL calls
close: Close the connection to the database
"""
_mysql_imported = False
db_type = "mysql"
TEST_CONFIG = "{'host': '127.0.0.1', 'user': 'root', 'password': ''}\n Use Pymysql and make sure to create the database using subprocess before connection."
def __init__(self, connection_config, docker_sandbox: DockerSandbox = None):
"""Initialize the MySQLManager.
Args:
connection_config (dict): configuration for the database connection, including keys for 'user', 'password', 'host', and 'database'.
"""
if not MySQLManager._mysql_imported:
global pymysql
import pymysql
MySQLManager._mysql_imported = True
keys = connection_config.keys()
if any(key not in keys for key in ['host', 'user', 'password', 'database']):
raise ValueError("Failed to initialize MySQL Manager due to bad configs")
elif any([not connection_config['host'], not connection_config['user'], not connection_config['password'], not connection_config['database']]):
raise ValueError("Failed to initialize MySQL Manager due to missing configs")
self.connection_config = {
'host': connection_config['host'],
'user': connection_config['user'],
'password': connection_config['password'],
'database': connection_config['database'],
"client_flag": pymysql.constants.CLIENT.MULTI_STATEMENTS
}
def connect(self):
"""Establish connection to the MySQL database and create a cursor."""
self.conn = pymysql.connect(**self.connection_config)
self.cursor = self.conn.cursor()
self.update_schema_info()
def update_schema_info(self):
schema_info = {}
self.cursor.execute("SHOW TABLES")
tables = self.cursor.fetchall()
for (table_name,) in tables:
self.cursor.execute(f"DESCRIBE {table_name}")
schema_info[table_name] = self.cursor.fetchall()
self.schema = schema_info
def execute_db_call(self, call):
"""Execute a SQL call using the cursor."""
if not self.conn:
self.connect()
try:
self.cursor.execute(call)
self.update_schema_info()
return 0
except Exception as e:
return 1
def fetch_db_call(self, call: str) -> list[dict]:
"""Execute a SQL call and return the results.
Args:
call (str): SQL query to execute.
Returns:
list[dict]: A list of dictionaries representing each row in the query result.
"""
if not self.conn:
self.connect()
try:
self.cursor.execute(call)
ret_val = self.cursor.fetchall()
self.update_schema_info()
return ret_val
except Exception as e:
return []
def commit_db_calls(self):
"""Commit SQL calls."""
if not self.conn:
self.connect()
self.conn.commit()
def rollback_db_calls(self):
"""Rollback SQL calls not committed."""
if not self.conn:
self.connect()
self.conn.rollback()
def close(self):
"""Close the cursor and the connection to the database."""
if self.conn:
self.cursor.close()
self.conn.close()