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 <script> seems to give infinite loop somewhere in "native" perl subroutines, like
split`。以上是关于Perl SQL::SplitStatement 挂在大字符串上的主要内容,如果未能解决你的问题,请参考以下文章