我需要使用旧方法转换SQL或将参数插入查询中,以新的方式将参数替换为问号(?)并分别传递给查询处理程序 - 请参阅“旧”和“新”的示例“下面。
我有1200个这样的SQL语句,带有各种参数和不同数量的参数,我想将它们全部转换为新的。
我是否需要创建自定义解析器,或者是否有工具可以让我轻松进行批量转换?
非参数化查询(又名旧查询)
$product = "widget";
$price = 10.00;
$sql = "SELECT description
FROM resource.product
WHERE
product.model = '" . db_input($product) . "'
and product.price = '" . db_input($price) . "'
";
$result = db_query($sql);
参数化查询(又名新)
$product = "widget";
$price = 10.00;
$sql = "SELECT description
FROM resource.product
WHERE
product.model = ?
and product.price = ?
";
$result = db_param_query($sql, [$product, $price]);
请注意,底部4行中有两个块不同。
答案 0 :(得分:1)
您需要的是Program Transformation System (PTS)。 PTS是一种可以将源代码解析为编译器数据结构(例如,抽象语法树或AST)的工具,可以将转换应用于表示所需更改的AST,然后可以从修改后的AST重新生成有效的源代码。 / p>
一个好的PTS将允许您使用语法指定要转换的语言,并允许您使用源到源重写规则对树修改进行编码,这些规则基本上是以下形式:
**when** you see *this*, replace it by *that*, **if** condition(*this*)
其中此和 是使用正在转换的语言的语法编写的模式,条件可以检查匹配的模式以获取其他约束。
在OP的情况下,我猜测他正在使用PHP(告密者:" $"作为变量名的前缀,"。"使用用于连接运算符)。所以他需要一个好的PTS和一个准确的PHP语法。
在OP中,他有一个双重语法问题:他不仅希望改变将SQL字符串片段粘合在一起的PHP代码,而且他还希望自己修改SQL字符串。可以说他需要PTS来解析SQL字符串片段,然后应用一个同时修改PHP和SQL字符串的转换。如果我们通过连接始终表示参数之间的SQL块的字符串片段来假设遗留程序始终组装SQL字符串,那么我们可以避免这种双解析问题。
第二个探测器知道字符串表示SQL字符串片段。请考虑以下代码:
$A=1; $B=10;
echo "SELECT number from '" . $A . "' to '" . $B . "'";
这看起来非常像真正的select语句,但它不是;我们不想将任何转换应用于此代码。通常,您不能知道汇编的字符串实际上是一个SQL字符串,或者只是看起来像一个字符串。我们假设所有连接的字符串分别以"'"""""""""是SQL字符串。
我们的DMS软件再造工具包是一个可以解决这个问题的PTS;它甚至有一个可用的PHP语法。人们大致需要以下DMS重写规则:
rule fix_legacy_SQL_parameter_passing(s1: STRING, v, DOLLARVAR, s2:STRING):
expression -> expression=
" \s1 . db_input(\v) . \s2 "
-> " \concatenate3\(\allbutlastcharacter\(\s1\)\,"?"\
\allbutfirstcharacter\(\s2\)"
if last_character_is(s1,"'") and first_character_is(s2,"'");
规则命名为 fix_legacy_SQL_parameter_passing ,以便我们将其与我们可能拥有的许多其他规则区分开来。参数s1和s2表示匹配指定(非)终端类型的子树的元变量。 expression-> expression 告诉DMS该规则仅适用于表达式。
此模式是" \ s1。 db_input(\ v)。 \ s2&#34 ;; " 是一个metaquote,它将DMS重写规则语法与PHP语法分开。 \ s1,\ v和\ s2使用 \ 来表示一个元变量,该效果模式表示"如果你能找到两个文字字符串的串联,其中插入的dbinput函数有一个变量名作为一个论点然后......"
在第二个 - > 之后是那个模式;它非常复杂,因为我们想对匹配的字符串进行一些计算。为此,它使用元函数编写为
\fnname\( arg1 \, arg2 \, ... \)
通过匹配从绑定到模式变量的树计算新树。请注意 \ 转义以区分元函数调用的元素和目标语言的语法。我希望我建议使用的元函数集的目的是明确的;它们必须被编码为此规则的自定义辅助支持。该规则以尾随&#34 ;;"结束。
应该清楚的是,此规则修补SQL字符串以替换"?"在构造的字符串中。
但是,等等,哎呀...我们没有收集db_input变量。
我们可以通过两种方式做到这一点:一个隐藏的累加器(这里没有显示它看起来像魔术),或者更笨拙但更容易重写标签。
DMS的标记是一个包含我们想要包含的内容的树;它通常表明我们有意做进一步的工作,我们需要额外的重写规则来完成这项工作。首先,我们介绍标记树的定义:
pattern accumulated_db_variable( vars:expression, computed:expression) :expression = TAG;
这使 cumulative_db_variable 这样的标签有两个孩子,第一个是变量名列表,第二个是任意表达式。
现在我们修改上面的规则:
rule fix_legacy_SQL_parameter_passing(s1: STRING, v, DOLLARVAR, s2:STRING):
expression -> expression=
" \s1 . db_input(\v) . \s2 "
-> " \accumulated_dbinputs\([\v]\,
\concatenate3\(\allbutlastcharacter\(\s1\)\,"?"\
\allbutfirstcharacter\(\s2\)\)"
if last_character_is(s1,"'") and first_character_is(s2,"'");
此规则计算修订的SQL字符串,但也计算在该字符串中找到的dbinput变量集,并将这对树包装在标记中。坏消息是现在我们在表达式中间有标签;但是,我们可以编写其他规则,通过在标签彼此靠近时组合来摆脱它们:
rule merge_accumulated_dbinputs(vars: element_list,
v: DOLLARVAR,
e: expression):
expression -> expression =
" \accumulated_dbinputs\([\vars]\,
\accumulated_db_inputs\([\v]\,e\)\)"
-> "\accumulated_dbinputs\([vars,v]\,\e)";
我们需要一条规则将收集的变量集移动到OP建议的以下语句中:
rule finalize_accumlated_dbinputs(lhs1: DOLLARVAR,
vars: element_list,
query: expression,
lhs2: DOLLARVAR)
statements -> statements =
" \lhs1 = \accumulated_dbinputs\([\vars],\query);
\lsh2 = db_param_query(\lhs1,[\vars]);
如果他的代码具有比这允许的更多可变性,他可能必须编写其他规则。
最后,我们需要将这组规则粘合在一起,并给它起一个名字:
ruleset fix_legacy_SQL { fix_legacy_SQL_parameter_passing, merge_accumulated_dbinputs, finalize_accumlated_dbinputs}
有了这个,我们可以在文件上调用DMS并告诉它应用规则集直到用尽。
这套规则应该做什么[我将预期输出显示给OP的例子]是通过一系列步骤改变它:
$sql = "SELECT description
FROM resource.product
WHERE
product.model = '" . db_input($product) . "'
and product.price = '" . db_input($price) . "'
";
$result = db_query($sql);
- > ("转换为"):
$sql = TAG_accumulated_dbinputs([$product],
"SELECT description
FROM resource.product
WHERE
product.model = ?
and product.price = '" . db_input($price) . "'
");
$result = db_query($sql);
- > ("转换为"):
$sql = TAG_accumulated_dbinputs([$product],
TAG_accumulated_dbinputs([$price],
"SELECT description
FROM resource.product
WHERE
product.model = ?
and product.price = ?
"));
$result = db_query($sql);
- > ("转换为"):
$sql = TAG_accumulated_dbinputs([$product,$price],
"SELECT description
FROM resource.product
WHERE
product.model = ?
and product.price = ?
");
$result = db_query($sql);
- > ("转换为"):
$sql = "SELECT description
FROM resource.product
WHERE
product.model = ?
and product.price = ?
";
$result = db_param_query($sql,[$product,$price]);
Wooof。未经测试,但我认为这非常接近正确。