在 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 文件中的换行符和制表符完全没有任何问题。您可以在我的示例中添加一些\n
s(甚至\r\n
s)和\t
s,它仍然可以工作。
这对我来说适用于 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 中导入:错误“必须使用导入来加载 ES 模块”[重复]
如何在 node.js 中导入 font-awesome 以在 react.js 中使用?