Knex 加载 SQL 文件时出错 - "undefined" 处或附近的语法错误

Knex error loading SQL FIle - syntax error at or near "undefined"

我确定这是一个简单的问题,但我卡住了,Google 博士没有帮助。

基本上,我正在尝试使用 Node.js 中的 Knex 在迁移中加载架构 SQL 文件。 SQL 文件直接来自 PGAdmin,没有数据,只有模式。

本质上,我正在从文件系统加载 SQL 文件并 knex.raw() 应用它们。我可以看到 SQL 正在正常加载,它会吐到控制台中。

我的迁移方法是这样的

exports.up = knex => {
    const schemaFilePath = path.join(__dirname, "../db/schema");
    const schemaFiles = fs.readdirSync(schemaFilePath);
    let sql;
    schemaFiles.forEach(async file => {
        sql += fs.readFileSync(path.join(schemaFilePath, file), "utf8");
    });
    return knex.raw(sql);
};

有几个 SQL 文件,从该架构目录加载,如下所示。 (带有注释行等)

-- Table: public.roles

-- DROP TABLE public.roles;

CREATE TABLE public.roles
(
    _id uuid NOT NULL DEFAULT uuid_generate_v4(),
    "displayName" citext COLLATE pg_catalog."default",
    "createdAt" timestamp with time zone NOT NULL,
    "updatedAt" timestamp with time zone NOT NULL,
    "deletedAt" timestamp with time zone,
    "_fullTextSearch" citext COLLATE pg_catalog."default",
    system boolean DEFAULT false,
....

我得到的错误是

    ON public.validations USING btree
    ("updatedAt" ASC NULLS LAST)
    TABLESPACE pg_default;

 - syntax error at or near "undefined"
error: syntax error at or near "undefined"
at Connection.parseE (/xxx/node_modules/pg/lib/connection.js:604:13)

这不是那个特定的文件,就像我只是删除那个文件一样,我仍然得到同样的错误,只是在另一个文件上。

滚动查看输出的日志,SQL 我觉得一切正常。

我检查了坏字符、空格等。我不知道在哪里。

希望我只是在这里做错了什么。

有什么想法吗?

从 pg@7 开始,应该支持使用 knex raw 执行多个语句。所以有一个细微的变化,这实际上可以工作,但我自己从未测试过。

如果你 运行 那些 SQL 个文件一个一个地处理,效果会更好吗?

你确定 concat 没有把它们搞砸吗?

一定数量的 SQL 之后是否总是发生这种情况,也许最大查询大小有限制?

我一直使用命令行工具来 save/restore 数据库转储。

人们可能会在 knex 迁移文件中执行这些工具,然后让 knex 迁移系统标记该迁移已被使用。

我已经使用这种功能从节点代码保存/恢复数据库状态(通常存储不同 e2e 测试设置之间的系统状态,以便能够从执行中间启动 e2e 运行) :

 async dump(dumpFileName, user, password, database, host) {
    return new Promise((resolve, reject) => {
      const cmd = [
        `export PGPASSWORD=${password};`,
        `pg_dump -a -O -x -F c`,
        `-f '${dumpFileName}'`,
        `-d ${database}`,
        `-h ${host}`,
        `-p 5432`,
        `-U ${user}`
      ].join(' ');

      shelljs.rm('-f', dumpFileName);
      shelljs.exec(cmd, (code, stdout, stderr) => {
        console.log(`Command ready: ${cmd}, with exit code: ${code}`);
        if (code === 0) {
          resolve(stdout);
        } else {
          reject(new Error(stderr));
        }
      });
    });
  }

  async restore(dumpFileName, user, password, database, host) {
    return new Promise((resolve, reject) => {
      const cmd = [
        `export PGPASSWORD=${password};`,
        `cat '${dumpFileName}' | `,
        `pg_restore -a -O -x -F c`,
        `-d ${database}`,
        `-h ${host}`,
        `-p 5432`,
        `-U ${user}`,
        `--disable-triggers"`
      ].join(' ');

      shelljs.exec(cmd, (code, stdout, stderr) => {
        console.log(`Command ready: ${cmd}, with exit code: ${code}`);
        if (code === 0) {
          resolve(stdout);
        } else {
          reject(new Error(stderr));
        }
      });
    });
  }

不是完美的解决方案,但可能对某些人有用...

不想回答我自己的问题,但我知道发生了什么。感谢所有回复的人。

对于其他尝试做同样事情的人来说,该代码存在一些问题。

首先,关于 "undefined" 的错误是由于

let sql;
sql += fs.readFileSync(path.join(schemaFilePath, file), "utf8");

这会在 SQL 语句的开头生成一个 "undefined"。由

解决
let sql = "";

接下来我遇到了错误字符的问题,这次完全不可见并且很难找到。我只能通过将字符串的内容写回文件才能找到它们,然后我在文件中看到了一堆无效字符,我猜这些字符与 UTF-8 不兼容。

这是因为隐藏文件被从文件系统中移除。即

    schemaFiles.forEach(file => {
        sql += fs.readFileSync(path.join(schemaFilePath, file), "utf8");
    });

当我在 Mac 上时,结果它也在加载那些烦人的 .DS_Store 文件,这些文件插入了二进制垃圾,尽管这在屏幕输出上不可见,只有在写入之后内容返回到平面文件。

我打算尝试 psql 加载它。 psql 给了我更好的错误信息。

通过仅过滤 .sql 个文件解决了这个问题

     schemaFiles.forEach(file => {
        if (file.substr(file.length - 4) === ".sql") {
         code ....
        }
     })

最后,由于 CONSTRAINTS 和表不存在于架构中,我在加载架构文件时遇到问题,因此我使用 pg_dump 获取有效的架构文件,不包括 knex 迁移表

pg_dump --schema-only --exclude-table=knex* > db/schema.sql

现在迁移文件基本上是一行

return knex.raw(fs.readFileSync(path.join(__dirname, "../db/schema.sql"), "utf8"));

希望这对遇到困难的其他人有所帮助