31

我正在寻找一种有效的方法来获取原始 sql 文件并让它针对 postgres 数据库同步执行,类似于你通过psql.

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

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

提前说明几点:

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

3 回答 3

44

您可以在传递给时用分号分隔后续查询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);
    });
});
于 2014-03-31T08:48:57.253 回答
16

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

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

代码片段:

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 语句之外的任何地方使用分号,这将失败。

于 2014-03-26T11:14:24.640 回答
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());

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

于 2020-12-18T17:35:36.320 回答