首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何处理更改SQL参数传递的大量代码结构转换?

如何处理更改SQL参数传递的大量代码结构转换?
EN

Stack Overflow用户
提问于 2016-10-11 22:04:58
回答 1查看 42关注 0票数 2

我需要用旧的方式转换SQL,或者将参数插入到查询中,将参数替换为问号(?)并分别传递给查询处理程序-请参阅下面的“旧”和“新”示例。

我已经使用了各种参数和各种参数的SQL语句的顺序或1200条,我想将它们全部转换为新的of。

这是我必须为之创建一个自定义解析器的东西吗?还是有一些工具可以让我轻松地进行批量转换?

非参数化查询(又称旧查询)

代码语言:javascript
复制
$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);

参数化查询(又名新查询)

代码语言:javascript
复制
$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行中有两个块不同。

EN

回答 1

Stack Overflow用户

发布于 2016-10-12 04:13:28

你需要的是一个程序转换系统(PTS)。PTS是一种工具,可以将源代码解析为编译器数据结构(例如,抽象语法树或AST ),可以将转换应用到表示所需更改的AST,然后可以从修改后的AST重新生成有效的源代码。

良好的PTS将允许您指定使用语法转换的langauge,并允许您使用源到源重写规则对树进行代码修改,这些规则本质上是形式的:

代码语言:javascript
复制
**when** you see *this*, replace it by *that*, **if** condition(*this*)

其中----即是使用正在转换的语言的语法编写的模式,并且条件可以检查匹配的模式以获得额外的约束。

在OP的例子中,我猜他使用的是PHP (telltales:"$“作为变量名的前缀,”。用于级联操作符)。因此,他需要一个好的PTS和一个精确的PHP语法。

在OP中,他有一个双重语法问题:他不仅希望转换将SQL字符串片段粘合在一起的PHP代码,而且还希望修改SQL字符串本身。可以说,他还需要PTS解析SQL字符串片段,然后应用同时修改PHP和SQL字符串的转换。如果我们假设SQL字符串总是由遗留程序通过串接字符串片段来组装,而这些片段总是表示参数之间的SQL块,那么我们就可以避免这个双重解析问题。

第二个问题是知道字符串表示SQL字符串片段。考虑以下代码:

代码语言:javascript
复制
  $A=1; $B=10;
  echo  "SELECT number from '" . $A . "' to '" . $B . "'";

这看起来非常像一个真正的select语句,但事实并非如此;我们不想对这段代码应用任何转换。通常,您无法知道组装的字符串实际上是SQL字符串,或者仅仅是看起来类似的字符串。我们将假设所有分别终止和以"'“开头的连接字符串都是SQL字符串。

我们的DMS软件重组工具包是一个PTS,可以解决这个问题;它甚至有一个可用的PHP语法。大约需要以下DMS重写规则:

代码语言:javascript
复制
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表示匹配指定(非)终端类型的子树的元数据。表达式->表达式告诉DMS,规则仅适用于表达式。

这个模式是“\s1 . db_input( \v ) . \s2 ";"是一个元引号,它将DMS重写规则语法与PHP语法分隔开来。\s1、\v和\s2使用*来表示一个元可调,该模式实际上是这样写的:”如果您能够找到一个包含有变量名的dbinput函数作为参数的两个文字字符串的连接.“

遵循第二个->是该模式;它非常复杂,因为我们需要对匹配的字符串进行一些计算。为此,它使用了如下所写的元功能

代码语言:javascript
复制
\fnname\( arg1 \,  arg2 \, ...  \)

通过匹配从绑定到模式变量的树中计算新树。注意*转义以区分元函数调用的元素和目标语言的语法。我希望我建议使用的一组元功能的用途是明确的;它们必须被编码为对此规则的自定义辅助支持。该规则以";“结尾。

应该清楚的是,此规则将SQL字符串补丁为"?“在构造的字符串中。

但是等等哦..。我们没有收集db_input变量。

我们可以这样做:隐藏累加器(这里没有显示,因为它看起来像魔法),或者笨拙,但更容易重写标记。

DMS的标记是一棵树,它包含我们希望它包含的任何内容;它通常表明我们打算做进一步的工作,我们需要更多的重写规则来完成这项工作。首先,我们介绍标记树的定义:

代码语言:javascript
复制
 pattern accumulated_db_variable( vars:expression, computed:expression) :expression = TAG;

这使得accumulated_db_variable成为这样一个标记,有两个子标记,第一个是变量名列表,第二个是任意表达式。

现在,我们修改上述规则:

代码语言:javascript
复制
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变量集,并将这一对树封装在标记中。坏消息是,我们现在在表达式的中间有标记;但是,我们可以编写额外的规则来消除这些标记,方法是在它们彼此接近时组合它们:

代码语言:javascript
复制
 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建议的以下语句:

代码语言:javascript
复制
 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示例的预期输出:

代码语言:javascript
复制
$sql = "SELECT description
        FROM resource.product
        WHERE
           product.model = '" . db_input($product) . "'
           and product.price = '" . db_input($price) . "'
        ";
$result = db_query($sql);

-> (“转换为”):

代码语言:javascript
复制
$sql =  TAG_accumulated_dbinputs([$product],
       "SELECT description
        FROM resource.product
        WHERE
           product.model = ?
           and product.price = '" . db_input($price) . "'
        ");
$result = db_query($sql);

-> (“转换为”):

代码语言:javascript
复制
$sql =  TAG_accumulated_dbinputs([$product],
           TAG_accumulated_dbinputs([$price],
        "SELECT description
        FROM resource.product
        WHERE
           product.model = ?
           and product.price = ?
        "));
$result = db_query($sql);

-> (“转换为”):

代码语言:javascript
复制
$sql =  TAG_accumulated_dbinputs([$product,$price],
        "SELECT description
        FROM resource.product
        WHERE
           product.model = ?
           and product.price = ?
        ");
$result = db_query($sql);

-> (“转换为”):

代码语言:javascript
复制
$sql =  "SELECT description
        FROM resource.product
        WHERE
           product.model = ?
           and product.price = ?
        ";
$result = db_param_query($sql,[$product,$price]);

哇哦。未经测试,但我认为这是非常接近正确的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39987521

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档