2016-03-08 37 views
2

我想解析存储在日志数据库中的一些SQL查询 - 我不想将它们提交给SQL数据库,只是为了提取SELECT和WHERE子句中使用的字段。Perl SQL :: Parser表别名替换:适用于SELECT列名但不适用于WHERE列名

我一直在处理Java,Python和Perl中的几个SQL解析器。似乎对我的问题更好地工作的是SQL :: Parser和SQL :: Statement。有了这些我能写出下面的代码:

#!/usr/bin/perl 

use strict; 
use SQL::Parser; 
use SQL::Statement; 

use Data::Dumper; 

my $sql = "SELECT sl.plate,sp.fehadop FROM sppLines AS sl ". 
      "JOIN sppParams AS sp ON sl.specobjid = sp.specobjid ". 
      "WHERE fehadop < -3.5 "; 

my $parser = SQL::Parser->new(); 
my $stmt = SQL::Statement->new($sql,$parser); 
printf("COMMAND [%s]\n",$stmt->command); 
printf("COLUMNS \n"); 
my @columns = @{$stmt->column_defs()}; 
foreach my $column (@columns) 
    { 
    print " ".$column->{value}."\n"; 
    } 
printf("TABLES \n"); 
my @tables = $stmt->tables(); 
foreach my $table (@tables) 
    { 
    print " ".$table->{name}."\n"; 
    } 
printf("WHERE COLUMNS\n"); 
my $where_hash = $stmt->where_hash(); 
print Dumper($where_hash); 

很抱歉,如果实在是太长了,它是最小的,自包含的例子,我可以想出。

这段代码的输出是:

COMMAND [SELECT] 
COLUMNS 
    spplines.plate 
    sppparams.fehadop 
TABLES 
    spplines 
    sppparams 
WHERE COLUMNS 
$VAR1 = { 
      'arg1' => { 
         'value' => 'fehadop', 
         'type' => 'column', 
         'fullorg' => 'fehadop' 
        }, 
      'op' => '<', 
      'nots' => {}, 
      'arg2' => { 
         'str' => '-?0?', 
         'fullorg' => '-3.5', 
         'name' => 'numeric_exp', 
         'value' => [ 
            { 
            'fullorg' => '3.5', 
            'value' => '3.5', 
            'type' => 'number' 
            } 
           ], 
         'type' => 'function' 
        }, 
      'neg' => 0 
     }; 

解析器返回列已经与真实的表的名称(如spplines更名为 .plate不是名称(通过对$stmt->column_defs()调用获得) s1 .plate) - 这就是我想要的。

我也想要在WHERE子句中使用的列的名称。 我已经知道如何递归地解析$stmt->where_hash()的结果(不包括使帖子清晰的代码),但即使从转储其内容,我也可以看到列名与表没有关联。

我想确保WHERE子句中的列名称前面还有表名称。解析$stmt->where_hash()的结果后,我会得到sppparams.fehadop而不是fehadop。

这可能与SQL :: Parser?

感谢 (大编辑 - 试图使问题更清晰)

+0

您能否以更清晰的方式添加预期的输出?问题的下半部分有点喋喋不休。 – simbabque

+0

我编辑了这个问题,希望它更清晰。 –

回答

1

由于SQL语句::有eval_where,我怀疑有可能是一个更好的办法,但你可以尝试这样的功能:

get_column($stmt->column_defs(), $where_hash->{arg1}); 

sub get_column { 
    my ($columns, $arg) = @_; 
    return $arg->{fullorg} if ($arg->{type} ne 'column'); 
    foreach my $col (@$columns) { 
     return $col->{value} if ($col->{fullorg} eq $arg->{fullorg}); 
     my ($name) = ($col->{fullorg} =~ /([^.]+)$/); 
     return $col->{value} if ($name eq $arg->{fullorg}); 
    } 
    return $arg->{fullorg}; 
} 
+0

谢谢 - 为我发布的简单案例工作。我必须考虑处理更复杂的WHERE子句。我不确定在这种情况下'eval_where'是否有帮助,我只需要提取完整的列名称,而不是评估它是否与值匹配(除非我误解了'eval_where'的作用 - 文档和示例很稀疏) –

+0

什么我的意思是,因为他们有一个eval函数,所以他们有可能在某处深入解析源代码。我没有深入阅读。让我知道,如果它不适用于其他情况。 – bolav

+0

感谢您的提示 - 我可能需要从该模块获取源代码并进行检查。我刚刚发现解析器抱怨“选择前10个X,Y来自...”(错误:'不好的表或列名:'TOP 10'有字母不是字母数字或下划线') - 我以为“select顶部“是标准的SQL。 –