Как заставить selectrow_array возвращать неправильное значение?

Я помню, что у меня была проблема с методом DBI selectrow_array. Когда я был недостаточно аккуратен, я получил от него не значение столбца, которое я просил, а количество столбцов (или что-то нежелательное, я не могу точно вспомнить). Теперь я пытаюсь реорганизовать некоторый код и хочу убедиться, что во всех возможных местах я верну только ожидаемое значение. Поэтому я стараюсь избегать сюрпризов и выяснять, в чем было плохое поведение. Из документации DBI я прочитал, что это может быть действительно проблемная ситуация:

Если вызывается в скалярном контексте для дескриптора оператора, который имеет более одного столбца, не определено, вернет ли драйвер значение первого столбца или последнего. Так что не делай этого. Кроме того, в скалярном контексте возвращается undef, если строк больше нет или произошла ошибка. Это «undef» нельзя отличить от возвращаемого «undef», потому что первое значение поля было NULL. По этим причинам вам следует проявлять осторожность, если вы используете "selectrow_array" в скалярном контексте или просто не делаете этого.

Тем не менее я не могу заставить selectrow_array вернуть что-либо, кроме значения col1 (это то, что я ожидаю)

my $query = 'SELECT col1, col2, col3 FROM table WHERE id = 112233';

my ( $c ) = ( $dbh->selectrow_array( $query ) );
my $x = ask_from_db();
my $y = $dbh->selectrow_array( $query );
my $z = ( $dbh->selectrow_array( $query ) );
my @A = $dbh->selectrow_array( $query );

say "C: $c"; # C: col1
say "X: $x"; # X: col1
say "Y: $y"; # Y: col1
say "Z: $z"; # Z: col1
say "A: @A"; # A: col1 col2 col3

sub ask_from_db {
  return $dbh->selectrow_array( $query );
}

Каждый способ, о котором я прошу выше, дает мне прекрасный результат. Как мне запустить запрос, чтобы получить неверный результат?

неверный результат! = col1 значение


person w.k    schedule 23.11.2012    source источник
comment
Мне нравится этот вопрос. Но я понятия не имею. Я бы попробовал те же фрагменты, что и у вас. Одним из способов может быть оператор goatse: my $foo =()= $dbh->selectrow_array($query). Но кто на самом деле запрограммировал бы это непреднамеренно? Писать это случайно - все равно что случайно упасть на mp3-плеер и застрять где-нибудь в темноте и тепле ...   -  person simbabque    schedule 23.11.2012
comment
@simbabque: Нет, к сожалению, это было не так поэтично, это было просто и понятно, но потребовались часы на отладку, чтобы понять, что у меня проблема с контекстом. Я был совершенно потерян :)   -  person w.k    schedule 24.11.2012
comment
@simbabque, это присвоит $foo количество столбцов, а не значение для первого столбца.   -  person ikegami    schedule 24.11.2012
comment
@ikegami: да, это был один из моих воспоминаний о том, что я получил назад количество столбцов, и он предложил способ, как это могло произойти. Но, как он сказал, вряд ли кто-то случайно воспользуется goatse-operator   -  person w.k    schedule 24.11.2012
comment
@ikegami, все дело в том, чтобы заставить его плохо себя вести.   -  person simbabque    schedule 24.11.2012
comment
@simbabque, тогда то, что я сказал, все еще применимо. Это присвоит количество столбцов $foo. Это не заставляет его возвращать что-то, кроме первого элемента.   -  person ikegami    schedule 24.11.2012
comment
@ikegami, это правильно, и я не сказал иначе. Но OP сказал, что он «был небрежным», поэтому я думал о способах небрежно создавать нежелательное поведение. То, что это не меняет возвращаемое значение метода, мне очень ясно, но, как показали тесты OP, он должен был где-то делать что-то не так. Я просто размышлял об этом.   -  person simbabque    schedule 24.11.2012


Ответы (2)


Разница в результатах будет зависеть от реализации драйвера.

wantarray ? @row : $row[0]

vs

wantarray ? @row : $row[-1]

Вы бы использовали другой драйвер, чтобы получить другой результат. Тем не менее, я полагаю, вам будет сложно найти водителя, который не вернется первым.

Если вы хотите быть уверены, что получите первое, используйте:

( $dbh->selectrow_array( $query ) )[0]
person ikegami    schedule 23.11.2012
comment
Я думал об этом, но я не был уверен, может быть, этот иногда дает еще какую-нибудь колонку. Вот почему я хотел найти способ, как проверить ложное значение. Но теперь я вижу, что этого не произойдет, потому что он уже будет в контексте списка, когда появится срез. - person w.k; 24.11.2012
comment
Нет, всегда первым. Это вызывает selectrow_array в контексте списка, который возвращает все столбцы, а затем выбирает первый. Это называется срез списка. - person ikegami; 24.11.2012
comment
Прошу прощения, если мой комментарий был расплывчатым, это тоже была моя точка зрения. - person w.k; 24.11.2012

В документации имеется в виду, что «не определено, вернет ли драйвер значение первого столбца или последнего», так это то, что возвращаемый столбец определяется драйвером базы данных, а не DBI.

Таким образом, драйвер Postgres может решить всегда возвращать первый столбец, тогда как драйвер mysql всегда может возвращать последний столбец, или возвращаемый столбец может зависеть от запроса.

Поэтому не называйте selectrow_array скалярным контекстом - всегда вызывайте его в контексте списка:

my @row = $sth->selectrow_array($query)

и вы избежите всех проблем, упомянутых в документации.

person ErikR    schedule 23.11.2012
comment
Это моя вина, я забыл определить свою цель, почему я хочу вернуть ее в скалярном контексте! Я хотел избежать промежуточных переменных в простых подпрограммах, чтобы я мог напрямую возвращать значения из вызова БД. Когда я не могу быть уверен в получении правильного значения, я должен использовать присвоение @row. - person w.k; 24.11.2012