簡體   English   中英

在 node.js 中導入 sql 文件並針對 PostgreSQL 執行

[英]Import sql file in node.js and execute against PostgreSQL

我正在尋找一種有效的方法來獲取原始 sql 文件並讓它針對 postgres 數據庫同步執行,類似於通過psql運行它。

我有一個 sql 文件,它創建所有數據庫、導入數據等。我需要使用 node.js 執行它,但找不到任何自動執行此操作的模塊。 對於 node.js 應用程序本身,我們使用 node-postgres ('pg')、knex.js 和 bookshelf.js。 我認為 pg 最適合這個。

我能想到的一種替代方法是讀取整個文件,用分號分割,用空格替換換行符,修剪任何重復的空格,然后以順序執行而不是異步執行的方式將其一個一個地輸入 pg。 如果這真的是最有效的方法,並且還沒有庫來解決這個問題,我有點驚訝。 我有點猶豫要不要跳進去,因為 SQL 語法本身就有點挑戰性,我可能會不小心把它混搭。

提前說明幾點:

  • psql不能使用,因為它沒有安裝在目標機器上
  • 我選擇以 sql 原生形式開發和源代碼控制 sql 語句,因為對於 DBA 來說使用和操作它要容易得多

傳遞給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);
    });
});

我編寫了以下適用於我的案例的函數。 如果不是因為:

  • 使用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 語句之外的任何地方使用分號,這都會失敗。

@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());

本例中,每條語句依次運行,返回最后一條語句的結果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM