首页 > 解决方案 > 节点:使用变量替换管理 SQL 文件

问题描述

我有大型 SQL 语句,我想将它们存储为单独的文件(带有语法突出显示等)。我喜欢这里提出的公认解决方案。

在我的用例中,我用 Javascript Template Literal 语法编写 SQL 来进行变量替换。所以我的文件看起来像

-- some_file.sql
select col1, col2, col3
from   my_table
where  col1 = '${val1}'
and    col2 between ${val2} and ${val3}

事实上,这种编写查询的方式最初是在查询增长并需要自己的文件之前使用模板文字开始的。

问题是如何实现模板文字,例如对使用读取的查询字符串进行评估,fs.readFileSync而不必执行可怕的操作eval?我查看了es6-template-render,但是该实现不适合执行上下文中的变量;即不指定单独的context参数,而是在运行时隐式使用环境中可用的变量(全局/局部)。

任何指针?

标签: javascriptnode.js

解决方案


如果我的假设不正确,我们深表歉意,但周围的引号'${val1}'表明您打算使用字符串替换而不是参数化查询。不要那样做。:-) 永远不要使用字符串替换将值放入 SQL 查询中。让我把你介绍给我的朋友鲍比

在此处输入图像描述

请改用参数化查询。

例如,您可能会使用与您非常相似的格式,只是没有任何引号${val1}

select col1, col2, col3
from   my_table
where  col1 = ${val1}
and    col2 between ${val2} and ${val3}

然后您的代码可以将其转换为适合您的 DB API 的查询。他们中的许多人使用?占位符,例如(这里我node-mysql2用作 DB API,但特定的 API 不是重点):

const rexParam = /(?<!\$)\$\{([^}]+)\}/g;
function doQuery(sql, params) {
    return new Promise((resolve, reject) => {
        const values = [];
        const preppedSql = sql.replace(rexParam, (_, paramName) => {
            const value = params[paramName];
            if (value === undefined) { // Or do an `in` check if you want to allow `undefined`
                throw new Error(`Missing parameter ${paramName}`);
            }
            values.push(value);
            return "?";
        });
        return connection.execute(
            preppedSql,
            values,
            function(err, results, fields) {
                if (err) {
                    reject(err);
                } else {
                    resolve({results, fields});
                }
            }
        );
    });
}

这会在字符串中旋转,将${val1}和 等标记替换为并?同时填充要传递给参数化查询函数的值数组。

(请注意负面的lookbehind,因此它$${...}不会被扩展,就像在模板文字中一样。正则表达式有点原始,但我认为应该足以满足SQL......)

现场示例只是转储字符串和值:

const sql =
    "select col1, col2, col3\n" +
    "from   my_table\n" +
    "where  col1 = ${val1}\n" +
    "and    col2 between ${val2} and ${val3}";

const rexParam = /(?<!\$)\$\{([^}]+)\}/g;
function doQuery(sql, params) {
    const values = [];
    const preppedSql = sql.replace(rexParam, (_, paramName) => {
        const value = params[paramName];
        if (value === undefined) { // Or do an `in` check if you want to allow `undefined`
            throw new Error(`Missing parameter '${paramName}'`);
        }
        values.push(value);
        return "?";
    });
    console.log(preppedSql);
    console.log(values);
}

doQuery(sql, {val1: "one", val2: 2, val3: 20});


推荐阅读