在 node.js 中导入 sql 文件并针对 PostgreSQL 执行

Posted

技术标签:

【中文标题】在 node.js 中导入 sql 文件并针对 PostgreSQL 执行【英文标题】:Import sql file in node.js and execute against PostgreSQL 【发布时间】:2014-05-03 09:39:47 【问题描述】:

我正在寻找一种有效的方法来获取原始 sql 文件并使其与 postgres 数据库同步执行,类似于通过 psql 运行它。

我有一个创建所有数据库、导入数据等的 sql 文件。我需要使用 node.js 执行此操作,但找不到任何自动执行此操作的模块。对于 node.js 应用程序本身,我们使用 node-postgres ('pg')、knex.js 和 bookshelf.js。我认为 pg 最适合这个。

我能想到的另一种选择是读取完整文件,用分号分隔,用空格替换换行符,修剪任何重复的空格,然后以顺序执行的方式将其逐个输入 pg,而不是异步。如果这确实是最有效的方法,而且如果还没有库可以解决这个问题,我会感到有点惊讶。我有点犹豫要不要跳进去,因为 SQL 语法本身可能有点挑战性,我可能会不小心把它混在一起。

提前说明一下:

psql 无法使用,因为它没有安装在目标机器上 我选择以 sql 原生形式开发和源代码控制 sql 语句,因为 DBA 更容易使用和操作它

【问题讨论】:

这比您最初想象的要难,因为您需要在 SQL 中区分文字和关键字。举个简单的例子,用分号分割不能与insert into table values(';');一起使用 【参考方案1】:

您可以在传递给client.query 时用分号分隔后续查询

有效:

var pg = require('pg');

pg.connect('postgres://test:test@localhost/test', function(err, client, done)
        client.query('CREATE TABLE test (test VARCHAR(255)); INSERT INTO test VALUES(\'test\') ');
        done();
);

因此,这也有效:

var pg = require('pg');
var fs = require('fs');

var sql = fs.readFileSync('init_database.sql').toString();

pg.connect('postgres://test:test@localhost/test', function(err, client, done)
    if(err)
        console.log('error: ', err);
        process.exit(1);
    
    client.query(sql, function(err, result)
        done();
        if(err)
            console.log('error: ', err);
            process.exit(1);
        
        process.exit(0);
    );
);

【讨论】:

我最初尝试过但发现问题: * 传统上在 SQL 文件中找到的换行符和制表符无法填充到 client.query 中,(可通过一些额外步骤解决) * 每个查询的错误处理非常困难(诚然,我在这里的示例中也忽略了它) @rgareth 我对 SQL 文件中的换行符和制表符完全没有任何问题。您可以在我的示例中添加一些\ns(甚至\r\ns)和\ts,它仍然可以工作。 这对我来说适用于 ibm_db lib。我也面临同样的问题,但我想到了一个问题。 SQL 注入呢?【参考方案2】:

我编写了以下函数适用于我的案例。如果不是因为它会更简单:

使用batch 管理并发 需要考虑棘手的 PostgreSQL COPY 案例

代码sn-p:

function processSQLFile(fileName) 

  // Extract SQL queries from files. Assumes no ';' in the fileNames
  var queries = fs.readFileSync(fileName).toString()
    .replace(/(\r\n|\n|\r)/gm," ") // remove newlines
    .replace(/\s+/g, ' ') // excess white space
    .split(";") // split into all statements
    .map(Function.prototype.call, String.prototype.trim)
    .filter(function(el) return el.length != 0); // remove any empty ones

  // Execute each SQL query sequentially
  queries.forEach(function(query) 
    batch.push(function(done) 
      if (query.indexOf("COPY") === 0)  // COPY - needs special treatment
        var regexp = /COPY\ (.*)\ FROM\ (.*)\ DELIMITERS/gmi;
        var matches = regexp.exec(query);
        var table = matches[1];
        var fileName = matches[2];
        var copyString = "COPY " + table + " FROM STDIN DELIMITERS ',' CSV HEADER";
        var stream = client.copyFrom(copyString);
        stream.on('close', function () 
          done();
        );
        var csvFile = __dirname + '/' + fileName;
        var str = fs.readFileSync(csvFile);
        stream.write(str);
        stream.end();
       else  // Other queries don't need special treatment
        client.query(query, function(result) 
          done();
        );
      
    );
  );

请注意,如果您在除终止 SQL 语句之外的任何地方使用分号,这将失败。

【讨论】:

您能否详细说明batch 是什么? 导入带函数的脚本怎么样?【参考方案3】:

@databases/pg 客户端支持开箱即用地运行 SQL 文件:

const createPool = require('@databases/pg');
const sql = require('@databases/pg');

const db = createPool();

db.query(sql.file('my-file.sql')).catch(ex => 
  console.error(ex);
  process.exitCode = 1;
).then(() => db.dispose());

它还支持在对db.query 的一次调用中包含多个语句:

const createPool = require('@databases/pg');
const sql = require('@databases/pg');

const db = createPool();

db.query(sql`
  INSERT INTO users (name) VALUES ($'Forbes');
  SELECT * FROM users;
`)).then(
  results => console.log(results)
).catch(ex => 
  console.error(ex);
  process.exitCode = 1;
).then(() => db.dispose());

本例中,每条语句按顺序运行,返回最后一条语句的结果。

【讨论】:

虽然您的回答对我有所帮助,但您可以提及 @databases 包是由您创建的。 :smiley: ... 文档(在您的网站上)也可以改进以包含更多示例,例如我(作为 Node JS 新手)很难找到放置连接选项的位置,因为您的所有示例都使用 createConnectionPool() 中的单个字符串,但是,我可以在其中放置一个带有支持的连接选项的对象。继续努力! 感谢@tukusejssirs 的反馈如果您有改进文档的建议,如果您可以提交到 PR,我会很高兴。您需要编辑的所有文件都在这里:github.com/ForbesLindesay/atdatabases/tree/master/docs

以上是关于在 node.js 中导入 sql 文件并针对 PostgreSQL 执行的主要内容,如果未能解决你的问题,请参考以下文章

无法在 node.js 中导入模块 [重复]

在 Node.js 中导入:错误“必须使用导入来加载 ES 模块”[重复]

如何在 node.js 中导入 font-awesome 以在 react.js 中使用?

在 ES6 Node.js 中导入“.json”扩展会引发错误

如何在 Node.js REPL 中导入 ES 模块?

如何在 pl/sql 中导入“导出用户对象”?