【问题标题】:Perl: String to anonymous array?Perl:字符串到匿名数组?
【发布时间】:2013-11-29 09:49:50
【问题描述】:

已经解决 -->见编辑7

此时我对 Perl 还很陌生,并试图修改现有页面的一部分(在 Wonderdesk 中)。 页面的工作方式是,它从 GET url 获取信息并将其解析为 SQL 查询。

由于这是一个更大的系统的一部分,我无法修改它周围的编码,必须在这个脚本中解决它。

我执行的工作测试:

$input->{help_id} = ['33450','31976'];

运行时,正在构建的查询返回如下内容

select * from table where help_id in(33450,31976)

我的代码中没有按预期工作的部分:

my $callIDs = '33450,31450';
my @callIDs = split(/,/,$callIDs);
my $callIDsearch = \@callIDs;
$input->{help_id} = $callIDsearch;

运行时,正在构建的查询返回如下内容

从 help_id = '33450,31976' 的表中选择 *

我尝试过调试,使用 Data::Dumper 得到 $callIDsearch 的结果,在我的浏览器中显示为 [33450, 31450]。

有人能告诉我如何从 '123,456' 转换为 ['123', '456'] 吗?

诚挚的问候, 马塞尔

--===--

编辑:

根据要求,工作的最小代码片段:

$input->{help_id} = ['123','456']

不起作用的代码:

$str = '123,456';
@ids = split(/,/,$str);
$input->{help_id} = \@ids;

--===--

编辑 2:

问题来源: 以下部分代码负责从数据库中获取正确的信息:

my $input = $IN->get_hash;
my $db = $DB->table('help_desk');
foreach (keys %$input){
    if (/^corr/ and !/-opt$/ and $input->{$_} or $input->{keyword}){
        $db = $DB->table('help_desk','correspondence');
        $input->{rs} = 'DISTINCT help_id,help_name,help_email,help_datetime,help_subject,help_website,help_category,
                       help_priority,help_status,help_emergency_flag,help_cus_id_fk,help_tech,help_attach';
       $input->{left_join} = 1;
        last;
    }
}

# Do the search
my $sth  = $db->query_sth($input);
my $hits = $db->hits;

现在我希望能够提供多个参数,而不是能够提供单个参数 help_id。

--===--

编辑 3:

query_sth 是以下两种之一,还没查到:

$COMPILE{query} = __LINE__ . <<'END_OF_SUB';
sub query {
# -----------------------------------------------------------
# $obj->query($HASH or $CGI);
# ----------------------------
#   Performs a query based on the options in the hash.
#   $HASH can be a hash ref, hash or CGI object.
#
#   Returns the result of a query as fetchall_arrayref.
#
    my $self = shift;
    my $sth = $self->_query(@_) or return;
    return $sth->fetchall_arrayref;
}
END_OF_SUB

$COMPILE{query_sth} = __LINE__ . <<'END_OF_SUB';
sub query_sth {
# -----------------------------------------------------------
# $obj->query_sth($HASH or $CGI);
# --------------------------------
# Same as query but returns the sth object.
#
    shift->_query(@_)
}
END_OF_SUB

或者

$COMPILE{query} = __LINE__ . <<'END_OF_SUB';
sub query {
# -------------------------------------------------------------------
# Just performs the query and returns a fetchall.
#
    return shift->_query(@_)->fetchall_arrayref;
}
END_OF_SUB

$COMPILE{query_sth} = __LINE__ . <<'END_OF_SUB';
sub query_sth {
# -------------------------------------------------------------------
# Just performs the query and returns an active sth.
#
    return shift->_query(@_);
}
END_OF_SUB

--===--

编辑 4:_query

$COMPILE{_query} = __LINE__ . <<'END_OF_SUB';
sub _query {
# -------------------------------------------------------------------
# Parses the input, and runs a select based on input.
#
    my $self = shift;
    my $opts = $self->common_param(@_) or return $self->fatal(BADARGS => 'Usage: $obj->insert(HASH or HASH_REF or CGI) only.');
    $self->name or return $self->fatal('NOTABLE');
# Clear errors.
    $self->{_error} = [];

# Strip out values that are empty or blank (as query is generally derived from
# cgi input).
my %input = map { $_ => $opts->{$_} } grep { defined $opts->{$_} and $opts->{$_} !~ /^\s*$/ } keys %$opts;
    $opts = \%input;

# If build_query_cond returns a GT::SQL::Search object, then we are done.
    my $cond = $self->build_query_cond($opts, $self->{schema}->{cols});

    if ( ( ref $cond ) =~ /(?:DBI::st|::STH)$/i ) {
        return $cond;
    }

# If we have a callback, then we get all the results as a hash, send them
# to the callback, and then do the regular query on the remaining set.
    if (defined $opts->{callback} and (ref $opts->{callback} eq 'CODE')) {
        my $pk  = $self->{schema}->{pk}->[0];
        my $sth = $self->select($pk, $cond) or return;
        my %res = map { $_ => 1 } $sth->fetchall_list;
        my $new_results = $opts->{callback}->($self, \%res);
        $cond = GT::SQL::Condition->new($pk, 'IN', [keys %$new_results]);
    }

# Set the limit clause, defaults to 25, set to -1 for none.
    my $in = $self->_get_search_opts($opts);
    my $offset   = ($in->{nh} - 1) * $in->{mh};
    $self->select_options("ORDER BY $in->{sb} $in->{so}") if ($in->{sb});
    $self->select_options("LIMIT $in->{mh} OFFSET $offset") unless $in->{mh} == -1;

# Now do the select.
    my @sel = ();
    if ($cond)                  { push @sel, $cond }
    if ($opts->{rs} and $cond)  { push @sel, $opts->{rs} }
    my $sth = $self->select(@sel) or return;

    return $sth;
}
END_OF_SUB

--===--

编辑 5:我已经上传了使用的 SQL 模块: https://www.dropbox.com/s/yz0bq8ch8kdgyl6/SQL.zip

--===--

编辑 6:

根据要求,转储(修剪为仅包含 help_id 的部分):

Base.pm 中非工作代码的修改结果:

$VAR1 = [
          33450,
          31450
        ];

Condition.pm 中对非工作代码的修改结果:

$VAR1 = [
          "help_id",
          "IN",
          [
            33450,
            31450
          ]
        ];
$VAR1 = [
      "cus_username",
      "=",
      "Someone"
    ];
$VAR1 = [
          "help_id",
          "=",
          "33450,31450"
        ];

Base.pm 中工作代码的修改结果:

$VAR1 = [
          33450,
          31976
        ];

在Condition.pm中修改工作代码的结果:

$VAR1 = [
          "help_id",
          "IN",
          [
            33450,
            31976
          ]
        ];

看起来好像值后来以某种方式改变了:S 我为工作/非工作代码所做的所有更改都是替换:

$input->{help_id} = ['33450','31976'];

与:

$input->{help_id} = [ split(/,/,'33450,31450') ];

--===--

编辑 7:

阅读所有提示后,我决定重新开始,发现通过将一些日志写入文件,我可以更详细地分解问题。

我仍然不确定为什么,但它现在可以使用,使用与以前相同的方法。我认为这是我代码某处的错字/故障/错误..

很抱歉打扰了大家,但由于他的提示提供了突破,我仍然推荐积分去阿蒙。

【问题讨论】:

  • 问题出在其他地方,因为您显示的两个代码 sn-ps 是等效的。此代码有效。但是 SQL 是如何创建的呢?
  • 我也是这么想的,但是如果代码是等价的,那为什么不是结果,如果我不改变被调用的函数呢?
  • 问题在于创建arrayref和打印出SQL之间,我们需要看到那部分。您能否展示一段完整但最小的代码,表明一个 sn-p 有效而另一个无效?有关提示,请参阅 SSCCE.org
  • 我已添加(参见编辑 2)用于获取结果的原始最小源。如果你想要后面的代码,我得在午饭后查一下。
  • 我认为我们需要看到的部分是声明query_sth的部分

标签: string perl transform anonymous-arrays


【解决方案1】:

我没有答案,但我发现了一些我们需要知道发生了什么的关键点。

build_query_condBase.pm 第 528 行)中,数组参数将转换为 key in (...) 关系:

if (ref($opts->{$field}) eq 'ARRAY' ) {
    my $add = [];
    for ( @{$opts->{$field}} ) {
        next if !defined( $_ ) or !length( $_ ) or !/\S/;
        push @$add, $_;
    }
    if ( @$add ) {
        push @ins, [$field, 'IN', $add];
    }
}

sql 中的有趣位(Condition.pm 第 181 行)。即使有arrayref,如果IN 测试只包含一个元素,它也会被简化为= 测试。

if (uc $op eq 'IN' || $op eq '=' and ref $val eq 'ARRAY') {
    if (@$val > 1) {
        $op = 'IN';
        $val = '('
            . join(',' => map !length || /\D/ ? quote($_) : $_, @$val)
            . ')';
    }
    elsif (@$val == 0) {
        ($col, $op, $val) = (qw(1 = 0));
    }
    else {
        $op  = '=';
        $val = quote($val->[0]);
    }
    push @output, "$col $op $val";
}

在这两个条件之前,插入以下代码会很有趣:

Carp::cluck(Data::Dumper::Dump(...));

其中... 是第一个sn-p 中的$opts-&gt;{$field} 或第二个sn-p 中的$cond。生成的堆栈跟踪将允许我们找到所有可能修改该值的子例程。为此,必须在开始查询之前将以下代码放在主脚本中:

use Carp ();
use Data::Dumper;

$Data::Dumper::Useqq = 1;  # escape special characters

一旦代码被这样修改,运行工作和不工作的代码,并打印出结果查询

print Dumper($result);

因此,对于您的每个代码 sn-ps,我们应该获得两个堆栈跟踪和一个结果 SQL 查询。

【讨论】:

  • 来自编辑 7:阅读所有提示后,我决定重新开始,发现通过将一些日志写入文件,我可以更详细地分解问题。我仍然不确定为什么,但它现在可以使用,使用与以前相同的方法。我认为这是我的代码中某个地方的错字/故障/错误。很抱歉打扰了大家,但我仍然建议将要点交给 amon,因为他的提示提供了突破。
【解决方案2】:

在黑暗中拍摄...有一个临时数组 @callIDs 由此代码创建:

my @callIDs = split(/,/,$callIDs);
my $callIDsearch = \@callIDs;
$input->{help_id} = $callIDsearch;

如果您的代码的其他部分修改了@callIDs,即使它被分配给$input-&gt;{help_id},也可能会导致问题。当然,它是一个词法 (my) 变量这一事实意味着对 @callIDs 的任何此类更改都可能是“附近的”。

您可以通过这样的拆分来消除命名的临时数组:

$input->{help_id} = [ split(/,/,$callIDs) ];

【讨论】:

  • 我真的认为这一定是正在发生的事情。出于这个原因,我给了+1。对我来说,其他任何理论都没有真正站得住脚。
  • 恐怕这也不起作用,不过感谢您的建议:(
【解决方案3】:

我不确定我是否完全理解为什么会发生这种情况。您的查询构建器似乎需要一个字符串数组引用。你可以使用map 来做到这一点

my $callIDs = '33450,31450';
my @callIDs = map {$_*1} split(/,/,$callIDs);
$input->{help_id} = \@callIDs;

【讨论】:

  • split always 返回一个字符串。如果我们转储元素(使用 Devel::Peek),我们会看到 arrayref 只包含 PVs(字符串标量)。只有 Data::Dumper 模块看到字符串 看起来 像一个数字,并相应地对其进行格式化。这段代码工作得很好,即使没有map。问题出在其他地方。
  • split函数已经返回了一个字符串,所以不需要把匿名变量放在一个字符串中。您应该对单引号进行字符串化。即: ... map { qq{'$_'} } 拆分...
  • Dumper 确实返回了正确的值:$VAR1 = [ '33450', '31450' ];,但它仍然无法按预期工作
  • @Master-Guy:这行得通吗? $input-&gt;{help_id} = [123,456] 。也许它实际上需要一个整数数组引用?查看我编辑的答案
  • 用单引号或不带引号解析返回相同的结果
【解决方案4】:

这段代码应该可以工作

my $callIDs = '33450,31450';
$input->{help_id} = [split ",", $callIDs];

如果您的代码以某种方式检测到您的数据是您可以使用的数字

my $callIDs = '33450,31450';
$input->{help_id} = [map 0+$_, split ',', $callIDs];

如果它以某种方式变成数字并且您需要字符串而不是在这种情况下不应该,而是对未来工作的建议:

my $callIDs = '33450,31450';
$input->{help_id} = [map ''.$_, split ',', $callIDs];

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-16
    • 2012-05-10
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多