无法通过Python执行mysql脚本 - 'CREATE FUNCTION' 或 'CREATE TRIGGER

0 投票
1 回答
29 浏览
提问于 2025-04-12 19:03

我在使用之前写的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

1 个回答

1

你读取了一个SQL脚本,然后根据;这个符号把输入分割成一个个独立的SQL语句,并执行每个语句。

但是,MySQL的例程里面也有;这个符号。如果你就这样简单地用;来分割输入,你得到的结果是:

CREATE FUNCTION `employee_db`.`func_create_token`() RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
    DECLARE `chars` VARCHAR(26);

这样就不是一个完整的创建函数的语句了。因为在第一个;符号处就结束了,这正是你告诉它要分割的地方。SQL解析器期待BEGIN后面有一个END,但实际上没有。

所以,单纯用;来分割是不够的。你需要记录下任何BEGIN...END的块,包括嵌套的块。

你还需要解析输入,确保你找到的;符号不是字符串的一部分,也不是注释里的内容,甚至不是SQL标识符的一部分。

简单来说,你需要一个完整的SQL解析器,而不仅仅是用f.read().split(';')

第二种选择是模仿MySQL客户端读取SQL脚本的方式:你需要支持DELIMITER命令,并在你的SQL脚本中使用不是;的其他分隔符,这些分隔符也不能出现在存储例程的内容里。你需要逐行解析输入,直到找到当前的分隔符(因为它不会是;)。

第三种选择是放弃自己写代码来“运行”SQL脚本,而是直接启动一个子进程来运行mysql客户端,它已经具备了必要的解析功能。

这完全取决于你。你可以花几个小时甚至几天来写一个解析器,用Python实现这个功能。或者你也可以直接运行mysql客户端,五分钟就能完成你的任务。

撰写回答