web-dev-qa-db-ja.com

node.jsにSQLファイルをインポートし、PostgreSQLに対して実行します

生のsqlファイルを取得して、postgresデータベースに対して同期的に実行させる効率的な方法を探しています。psqlを実行した場合と同じです。

すべてのデータベースの作成、データのインポートなどを行うsqlファイルがあります。node.jsを使用してこれを実行する必要がありますが、これを自動的に実行するモジュールが見つかりません。 node.jsアプリケーション自体には、node-postgres( 'pg')、knex.js、bookshelf.jsを使用します。 pgがこれに最適だと思います。

私が考えることができる1つの代替案は、ファイル全体を読み取り、セミコロンで分割し、改行をスペースで置き換え、重複するスペースをトリミングし、非同期ではなく順次実行されるように1つずつpgにフィードすることです。これが本当に最も効率的な方法であり、これを解決するためのライブラリがまだ存在しない場合、私は少し驚いています。 SQL構文自体が少しやりがいがあり、誤ってつぶしてしまう可能性があるので、私はそれに飛び込むのを少しためらっています。

事前の説明:

  • psqlはターゲットマシンにインストールされていないため使用できません
  • DBAがSQLステートメントを使用および操作する方がはるかに簡単であるため、SQLネイティブ形式でSQLステートメントを開発およびソース管理することを選択しました
22
rgareth

for myのケースで機能する次の関数を作成しました。それがなかったら、それははるかに簡単だったでしょう:

  • 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ステートメントの終了以外の場所でセミコロンを使用した場合、これが失敗することに注意してください。

11
rgareth

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);
    });
});
35
OhJeez