Je cherche un moyen efficace de prendre un fichier SQL brut et de l'exécuter de manière synchrone sur une base de données PostgreSQL, comme si vous l'avez exécuté via psql
.
J'ai un fichier sql qui crée toutes les bases de données, importe les données, etc. J'ai besoin de l'exécuter en utilisant node.js mais je ne trouve aucun module qui le fasse automatiquement. Pour l'application node.js elle-même, nous utilisons node-postgres ('pg'), knex.js et bookshelf.js. Je suppose cependant que pg est le meilleur pour cela.
Une alternative à laquelle je peux penser est de lire le fichier complet, de le diviser par des points-virgules, de remplacer les sauts de ligne par des espaces, de découper tout espace en double, puis de l'introduire dans pg un par un de manière à ce qu'ils soient exécutés séquentiellement, pas de manière asynchrone. Je suis un peu surpris si c'est vraiment le moyen le plus efficace et aussi si aucune bibliothèque n'existe encore pour résoudre ce problème. J'hésite un peu à y plonger car la syntaxe SQL peut elle-même être un peu difficile et je pourrais accidentellement l'écraser.
Quelques précisions à l'avance:
psql
ne peut pas être utilisé car il n'est pas installé sur la machine cibleJ'ai écrit la fonction suivante qui fonctionne pour mon cas. Cela aurait été beaucoup plus simple s'il n'y avait pas eu:
batch
pour gérer la concurrenceExtrait de code:
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();
});
}
});
});
}
Sachez que cela échouerait si vous utilisiez des points-virgules ailleurs que pour terminer les instructions SQL.
Vous pouvez simplement séparer les requêtes conséquentes par un point-virgule lors de leur transmission à client.query
Ça marche:
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();
});
Et par conséquent, cela fonctionne aussi:
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);
});
});