在use SQL::Abstract::Tree
Perl 中,我可以通过以下方式为 SQL 生成 AST:
my $sqlat = SQL::Abstract::Tree->new;
my $tree = $sqlat->parse($query_str);
$query_str
SQL 查询在哪里。
例如,使用查询字符串SELECT cust_id, a as A, z SUM(price) as q, from orders WHERE status > 55
,产生:
[
[
"SELECT",
[
[
"-LIST",
[
["-LITERAL", ["cust_id"]],
["AS", [["-LITERAL", ["a"]], ["-LITERAL", ["A"]]]],
[
"AS",
[
["-LITERAL", ["z"]],
["SUM", [["-PAREN", [["-LITERAL", ["price"]]]]]],
["-LITERAL", ["q"]],
],
],
[],
],
],
],
],
["FROM", [["-LITERAL", ["orders"]]]],
[
"WHERE",
[[">", [["-LITERAL", ["status"]], ["-LITERAL", [55]]]]],
],
]
我想走 AST 并获得有关它的某些信息。
我想知道是否有指南/教程/示例源代码以这种格式遍历 AST。
我发现的大多数考虑步行 AST 的文献通常假设我有某种类层次结构,描述了访问 AST 的访问者模式的某种变体。
我的具体用例是将简单的 SQL 查询转换为聚合框架的 Mongo 查询,这里给出了一些示例。
到目前为止,这是我一直在做的事情:
我首先调用一个parse
函数,在给定其类型和(这是每个子树中的第一个参数)的每个子树上调度树,然后用树的其余部分调用它。这是我的parse
功能:
sub parse {
my ($tree) = @_;
my %results = (ret => []);
for my $subtree (@$tree) {
my ($node_type, $node) = @$subtree;
my $result_dic = $dispatch{$node_type}->($node);
if ($result_dic->{type}) {
my $type = $result_dic->{type};
$results{$type} = [] unless $results{$type};
push $results{$type}, $result_dic->{ret};
%results = merge_except_for($result_dic, \%results, 'ret', $type);
}
else {
push @{$results{ret}}, @{$result_dic->{ret}};
}
}
return \%results;
}
它使用以下调度表:
my %dispatch = (
SELECT => sub {
my $node = shift;
my $result_dic = parse($node);
$result_dic->{type} = 'select';
if ($result_dic->{as}) {
push $result_dic->{ret}, $result_dic->{as}->[0][0];
}
return $result_dic;
},
'-LITERAL' => sub {
my $node = shift;
my $literal = $node;
return {ret => $node};
},
'-LIST' => sub {
my $node = shift;
my $result_dic = parse($node);
my $ret = flatten_ret($result_dic);
return flatten_ret($result_dic);
},
WHERE => sub {
my $tree = shift;
my @bin_ops = qw/= <= < >= >/;
my $op = $tree->[0];
if ($op ~~ @bin_ops) {
# Not yet implemented
}
return {ret => ''};
},
FROM => sub {
my $tree = shift;
my $parse_result = parse($tree);
return {ret => $parse_result->{ret},
type => 'database'};
},
AS => sub {
my $node = shift;
my $result_dic = parse($node);
$result_dic->{type} = 'as';
return $result_dic;
}
);
sub flatten_ret {
my $result_dic = shift;
return {ret => [
map {
ref($_) ? $_->[0] : $_
} @{$result_dic->{ret}}]};
}
但我不确定某些事情,比如我是否应该检查节点名称是否"AS"
在SELECT
子例程中,或者找到一种递归方式来填充数据。
另外,每个调度调用应该返回什么类型的数据,最后我该如何组合它?
此外,我是 AST 处理的新手,并希望掌握它,因此我也将不胜感激有关如何改进我的问题的建议。