Perl SQL::SplitStatement 挂在大字符串上

Posted

技术标签:

【中文标题】Perl SQL::SplitStatement 挂在大字符串上【英文标题】:Perl SQL::SplitStatement hangs on large string 【发布时间】:2020-03-10 02:54:44 【问题描述】:

Debian-9.11 x86、perl-5.24、SQL::SplitStatement-1.0020

有 23 Mb mysql 文件,每个文件行有多个 REPLACE INTO ... VALUES(...) 一个命令。所有字符串都是有效的 SQL。但是一个字符串大约有 800 万个字符。

当尝试通过SQL::SplitStatement(见下文)解析它时,脚本挂起,占用 100% 的 CPU,结果什么也不做。

my $sql_blob=read_file("$INDIR/$f",binmode => ':utf8');
my $sql_splitter= SQL::SplitStatement->new();
my @sql_list=$sql_splitter->split($sql_blob); print "Size: $#sql_list\n"; # This will be never printed

怎么了?漏洞?

【问题讨论】:

似乎SQL::SplitStatement 使用SQL::Tokenizer 将SQL 代码拆分为语句。分词器使用a simple Perl regex(没有真正的解析器)。像你这样长的声明可能确实很重。你可以尝试将$sql_blob 减少到只有长声明吗?它仍然挂起吗? $sql_blob 的输入文件减少为一长行,其中包含 REPLACE INTO (...) VALUES (...), (...), ... (...) ; - 结果相同 - 挂断split($sql_blob) 【参考方案1】:

我还测试了DBIx:RunSQL->split_sql - 它根本不会将 SQL 拆分为一行。 SQL::SplitStatement 即使在具有 16 Gb RAM 的 core-i7 上也吃掉了 2 个 CPU 内核并且什么都不做......

在这里找到答案:Perl DBI - run SQL Script with multiple statements - 子例程似乎适用于我的任务 - 它将SQL1 (?,?) VALUES (?,?);SQL2 <some_statement>;SQL3; 之类的字符串拆分为单独的 SQL 命令。它也适用于长行(超过 800 万行和这一行中的几个 SQL 命令)。

【讨论】:

【参考方案2】:

也许该语句对于模块来说太大了?您可以尝试以下替代方法:C++ SQL Parser。我试过这个:

#include <iostream>
#include <string>
#include <vector>
#include <hsql/SQLParser.h>

int main()

    const std::string query = R"##(SELECT * FROM foo where a =  1; 
            SELECT * FROM foo where a == 2; 
            SELECT * FROM foo where a != 1; 
            SELECT * FROM foo where a <> 1; 
            SELECT * FROM foo where a >  1; 
            SELECT * FROM foo where a <  1; 
            SELECT * FROM foo where a >= 1; 
            SELECT * FROM foo where a <= 1; 
        SELECT * FROM foo where a = TRUE; 
        SELECT * FROM foo where a = false;)##";
    hsql::SQLParserResult result;
    hsql::SQLParser::parse(query, &result);

    if (result.isValid() && result.size() > 0) 
        auto num_statements = result.size();
        std::cout << "Number of statements: " << num_statements << '\n';
        for (int i=0; i< num_statements ; i++) 
            const hsql::SQLStatement* statement = result.getStatement(i);
            if (statement->isType(hsql::kStmtSelect)) 
                const hsql::SelectStatement* select
                    = (const hsql::SelectStatement*) statement;
                std::cout << " " << i << ": SELECT statement\n";
            
        
    
    else 
        std::cout << "parse failed: " << result.errorMsg() << '\n';
    
    return 0;

输出:

Number of statements: 10
 0: SELECT statement
 1: SELECT statement
 2: SELECT statement
 3: SELECT statement
 4: SELECT statement
 5: SELECT statement
 6: SELECT statement
 7: SELECT statement
 8: SELECT statement
 9: SELECT statement

【讨论】:

感谢您的回答。我会更接近模块,你已经发布了。是的,也许语句太长了,但是 SQL::SplitStatement 没有说明这些限制。还有perl -d &lt;script&gt; seems to give infinite loop somewhere in "native" perl subroutines, like split`。

以上是关于Perl SQL::SplitStatement 挂在大字符串上的主要内容,如果未能解决你的问题,请参考以下文章

Perl模块推荐23——Perl::Shell

Perl - 如何查看Perl模块路径

Perl 之父同意 Perl 6 改名为 Raku

以后没有 Perl 6 了!Perl 之父同意改名

Perl基础速成

Perl 的 rpm 版本不同于“perl -v”