我之前编写的 sql 脚本确实遇到了麻烦,并尝试使用我的 python 代码读取并执行它。这是我的简单 mysql 脚本,其中删除了 DELIMITER 命令:
DROP FUNCTION IF EXISTS `employee_db`.`func_create_token`;
CREATE FUNCTION `employee_db`.`func_create_token`() RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
DECLARE `chars` VARCHAR(26);
DECLARE `charLen` INT;
DECLARE `randomPassword` VARCHAR(100);
SET `chars` = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
SET `charLen` = LENGTH(`chars`);
SET `randomPassword` = '';
WHILE LENGTH(`randomPassword`) < 12 DO
SET `randomPassword` = CONCAT(`randomPassword`, SUBSTRING(`chars`, CEILING(RAND() * `charLen`), 1));
END WHILE;
RETURN `randomPassword`;
END;
我可以直接执行它并创建函数。到目前为止没有问题。但是尝试在 python 中读取 sql 文件并执行它,总是会导致我无法解决的错误。下面你可以看到 python 函数:
# Function to execute sql file
def execute_sql_script(path_to_sql: os.PathLike[str], title: str) -> None:
# Read sql file from folder `sql`
try:
with open(path_to_sql, 'r') as f:
queries : list = f.read().split(';')
queries : list = [q.strip() for q in queries if q.strip()]
f.close()
except FileNotFoundError as f:
print(color.BOLD + color.RED + f'File couldn\'t be read. Please check error: {e}' + color.END)
# Execute sql script with progress bar
try:
with alive_bar(len(queries), dual_line = True, title = title, force_tty = True) as bar:
for _, query in enumerate(queries):
sleep(.05)
cursor.execute(query)
bar()
cursor.commit()
except Error as e:
print(color.BOLD + color.RED + f'Error in executing sql script. Check error: {e}' + color.END)
错误消息不是很有帮助:
Create Function - Employee |███▋⚠︎ | (!) 1/11 [
[1m[91mError in executing sql script. Check error: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 3[0m
奇怪的是,创建表的所有其他脚本的执行确实工作得非常好,没有任何异常。
Create Function - Employee |████████████████████████████████████████| 27/27 [100
您阅读 SQL 脚本,将输入拆分为
';'
字符上的各个 SQL 语句,然后执行每个语句。
但是 MySQL 例程在其主体中包含一个
;
字符。如果您将输入拆分为 ;
字符,您会得到以下结果:
CREATE FUNCTION `employee_db`.`func_create_token`() RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
DECLARE `chars` VARCHAR(26);
这不是完整的 CREATE FUNCTION 语句。它在第一个
;
字符处提前终止,因为这是您告诉它分割输入的字符。 SQL 解析器期望 BEGIN
具有 END
,但事实并非如此。
这就是为什么简单地拆分
;
是不够的。您需要跟踪任何 BEGIN...END
块,包括嵌套块。
您还需要解析输入,以确保您没有找到作为字符串文字一部分、注释内部甚至 SQL 标识符一部分的
;
字符。
您基本上需要一个成熟的 SQL 解析器,而不仅仅是
f.read().split(';')
。
第二种选择是执行 MySQL 客户端在读取 SQL 脚本时执行的操作:您需要支持
DELIMITER
命令,并在 SQL 脚本中使用不是 ;
或任何其他字符的分隔符在存储例程的主体中找到。您需要逐行解析输入,直到找到 current 分隔符(因为它不会是 ;
)。
第三种选择是忘记编写“运行”SQL 脚本的代码,而只是分叉一个子进程来运行
mysql
客户端,该客户端已经完成了必要的解析。
这取决于你。您可以花费数小时或数天的时间编写一个用 Python 执行此操作的解析器。或者您可以运行
mysql
客户端并在五分钟内完成您的任务。