【问题标题】:SQL syntax error when executing MySQL script using Go during Heroku deployment在 Heroku 部署期间使用 Go 执行 MySQL 脚本时出现 SQL 语法错误
【发布时间】:2021-08-23 06:08:18
【问题描述】:

我正在将使用 Go 和 MySQl 为数据库创建的 API 部署到 Heroku。我正在关注此guide 并设置所有内容,但现在我正在尝试执行 MySQL 脚本来设置带有一些虚拟数据的表。但是我经常收到错误消息,说脚本错误,即使我在本地使用它也没有问题。我已经将 MySQL 数据库连接到 Heroku 环境,启动数据库没有报错。

以下是在将其部署到 Heroku 时显示错误的日志:

2021-06-05T12:49:16.442359+00:00 app[web.1]: ERROR  2021/06/05 12:49:16 main.go:45: Error executing SQL script : Error 1064: 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 'USE assessment;

schema.sql

CREATE DATABASE assessment;

USE assessment;

CREATE TABLE users (
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, 
    email VARCHAR(255) NOT NULL
);
INSERT INTO `users` VALUES (1,"test1@gmail.com");
INSERT INTO `users` VALUES (2,"test2@gmail.com");
INSERT INTO `users` VALUES (3,"test3@gmail.com");
INSERT INTO `users` VALUES (4,"test4@gmail.com");
INSERT INTO `users` VALUES (5,"test5@gmail.com");

CREATE TABLE features (
    feature_id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    user_id INTEGER NOT NULL,
    feature_name VARCHAR(100) NOT NULL,
    can_access BOOLEAN NOT NULL DEFAULT FALSE,
    FOREIGN KEY (user_id) REFERENCES users(id) 
);

INSERT INTO `features` VALUES (1, 1, "automated-investing", 1);
INSERT INTO `features` VALUES (2, 1, "crypto", 0);
INSERT INTO `features` VALUES (3, 2, "crypto", 0);
INSERT INTO `features` VALUES (4, 3, "automated-investing", 0);
INSERT INTO `features` VALUES (5, 4, "automated-investing", 1);
INSERT INTO `features` VALUES (7, 1, "financial-tracking", 1);
INSERT INTO `features` VALUES (8, 2, "financial-tracking", 0);
INSERT INTO `features` VALUES (9, 3, "financial-tracking", 1);
INSERT INTO `features` VALUES (10, 4, "financial-tracking", 0);

ma​​in.go

package main

import (
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "time"

    _ "github.com/go-sql-driver/mysql"
    "github.com/gorilla/mux"
    "github.com/joho/godotenv"

    "github.com/yudhiesh/api/controller"
    "github.com/yudhiesh/api/middleware"
)

func main() {
    router := mux.NewRouter()
    router.Use(middleware.ResponseHeaders)
    router.Use(middleware.LogRequest)
    errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
    infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
    err := godotenv.Load(".env")
    port := os.Getenv("PORT")
    if port == "" {
        errorLog.Fatal("$PORT is not set")
    }
    dsn := os.Getenv("DSN")
    if dsn == "" {
        errorLog.Fatal("$DATABASE_URL is not set")
    }
    db, err := controller.OpenDB(dsn)
    if err != nil {
        errorLog.Fatal(err)
    }
    defer db.Close()
    c, ioErr := ioutil.ReadFile("./schema.sql")
    sqlScript := string(c)
    if ioErr != nil {
        errorLog.Fatalf("Error loading SQL schema : %s", ioErr)
    }
    _, err = db.Exec(sqlScript)
    if err != nil {
        // FAILS HERE!
        errorLog.Fatalf("Error executing SQL script : %s", err)
    }
    app := &controller.Application{
        DB:       db,
        ErrorLog: errorLog,
        InfoLog:  infoLog,
    }
    addr := ":" + port
    server := &http.Server{
        Handler:      router,
        Addr:         addr,
        WriteTimeout: 15 * time.Second,
        ReadTimeout:  15 * time.Second,
    }
    router.HandleFunc("/feature", app.GetCanAccess).Methods("GET")
    router.HandleFunc("/feature", app.InsertFeature).Methods("POST")
    http.Handle("/", router)
    infoLog.Printf("Connected to port %s", port)
    errorLog.Fatal(server.ListenAndServe())
}

【问题讨论】:

    标签: mysql go heroku


    【解决方案1】:

    大多数对 MySQL 运行查询的接口不支持多查询。换句话说,它们不允许使用分号分隔的多条 SQL 语句,它们每次调用只支持一条 SQL 语句。

    在您的情况下,它在 USE ... 上返回了语法错误,因为鉴于输入被解析为单个语句,CREATE DATABASE statement 中没有 USE

    CREATE DATABASE assessment; USE assessment;
    

    这意味着如果你想处理一个包含许多 SQL 语句的文件,你不能只做你正在做的事情并将整个文件作为一个字符串传递给 Exec() 的单个调用:

    c, ioErr := ioutil.ReadFile("./schema.sql")
    sqlScript := string(c)
    ...
    _, err = db.Exec(sqlScript)
    

    您必须将该文件的内容拆分为单独的 SQL 语句,然后遍历这些语句,一次运行每个语句。

    这比听起来要复杂,因为:

    • SQL 脚本可能包含 DELIMITER 语句,它将语句之间的字符从分号更改为其他字符。

    • 在字符串字面量或 cmets 中可能有分号。

    • CREATE PROCEDURE、CREATE TRIGGER 等一些语句可能在例程主体中的语句之间包含分号。您不希望这些分号成为语句的结尾,您希望将所有内容包含到例程定义的末尾。

    • DELIMITER 语句本身不能由 MySQL 服务器执行。它只控制客户端。因此,您必须将其视为未在循环中发送到服务器的异常。事实上,还有很多其他的mysql client builtin commands,如果您在 SQL 脚本中找到它们,则必须进行类似处理。

    如果您最终对所有这些逻辑进行编码,那么您基本上已经在 Go 中重新实现了 MySQL 命令行客户端。

    如果您跳过所有编码工作,只需使用os.exec.Command 运行 MySQL 命令行客户端,将会更快更简单。想一想——它会为你节省数周的编码工作,复制已经在 MySQL 客户端中实现的运行 SQL 脚本的所有细微差别。

    【讨论】:

    • 谢谢,是的,我不知道os.exec.Command,但这是我在未来的情况下需要的!
    【解决方案2】:

    好的,所以我决定使用 Heroku 提供的凭据直接连接到 mysql 数据库。

    这会返回数据库的 URL。

    heroku config | grep CLEARDB_DATABASE_URL
    

    看起来像这样:

    mysql://alphanum-username:alphanum-password@us-cdbr-iron-east-01.cleardb.net/heroku_alphanum_name?reconnect=true
    

    然后使用mysql -u username -h host -p 连接到它,主机来自URL,在本例中为us-cdbr-iron-east-01.cleardb.net。密码来自alphanum-password

    我运行了相同的查询,但得到了这个错误,这意味着我没有访问数据库的权限。

    ERROR 1044 (42000): Access denied for user 'bbf806af82a4c9'@'%' to database 'assessment'
    

    运行show databases; 表明您可以作为用户连接到特定的数据库。

    mysql> SHOW DATABASES;
    +------------------------+
    | Database               |
    +------------------------+
    | information_schema     |
    | herokusdfhsdfhsdfsfsdf |
    +------------------------+
    

    从这里我运行 USE herokusdfhsdfhsdfsfsdf 并运行我的其余查询没有问题。

    【讨论】:

      猜你喜欢
      • 2022-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-06
      • 2023-02-04
      • 2013-10-07
      • 2021-10-21
      • 1970-01-01
      相关资源
      最近更新 更多