Целочисленное сравнение с использованием DBD :: SQLite - PullRequest
1 голос
/ 19 августа 2011

У меня проблема с DBIx :: Class для базы данных SQLite3.

Если вы не хотите читать полностью следующее, вот версия TL; DR:

Есть ли способ заставить DBD :: SQLite обрабатывать целочисленные поля как беззнаковые, когда дело доходит до сравнений?

Определение таблицы здесь:

sqlite> PRAGMA table_info(entry);
0|entry_key|INTEGER|1||1
1|node|varchar(256)|1||0
2|object_type|varchar(128)|1||0
3|object_id|int|1||0
4|copy_id|tinyint|0||0
5|seq_number|int|1||0
6|root_seq_number|int|1||0
7|first_error|int|1||0
8|last_error|int|1||0
9|error_count|int|1||0
10|error_id|int|1||0
11|error_code|int|0||0
12|status|varchar(64)|1||0
13|type|varchar(64)|1||0
14|sense|char(256)|0||0

Области интереса first_error и last_error. Эти поля содержат значения времени эпохи. Таким образом, это 32-битные числа, но они меньше, чем 2147483647

В моем коде у меня есть следующее:

my @entries =  $self->{row}->search_related_rs('eventlog_entries')
                           ->search_related('entry', {
                             first_error => {'>', $range->{start}},
                             last_error  => {'<', $range->{end}},
                           }
           )->all();

start установлен на 0; end установлено на 2**32 - 1

При запуске с DBI_TRACE=1 я получаю:

<- prepare_cached('SELECT entry.entry_key, entry.node, entry.object_type,
                   entry.object_id, entry.copy_id, entry.seq_number,
                   entry.root_seq_number, entry.first_error, entry.last_error,
                   entry.error_count, entry.error_id, entry.error_code, entry.status,
                   entry.type, entry.sense FROM eventlog_entry me  JOIN entry entry ON
                   entry.entry_key = me.entry_key WHERE ( ( ( first_error > ? AND
                     last_error < ? ) AND me.eventlog_key = ? ) )', 
                  HASH(0x2472b54), ...)= ( DBI::st=HASH(0x2442efc) ) [1 items] 
   at DBI.pm line 2245
<- bind_param(1, 0, ...)= ( 1 ) [1 items] at DBI.pm line 1574
<- bind_param(2, '4294967295', ...)= ( 1 ) [1 items] at DBI.pm line 1574
<- bind_param(3, 1, ...)= ( 1 ) [1 items] at DBI.pm line 1574
<- execute= ( '0E0' ) [1 items] at DBI.pm line 1586
<- fetchall_arrayref= ( [ ] ) [1 items] row-1 at Cursor.pm line 133

В этом случае @entries - пустой массив.

С другой стороны, если я установлю end на 2**31 - 1, все будет работать.

Мой гипоптез такой:

Поля SQLite имеют «сходство», что означает, что поля распознаются как целые числа, но не имеют собственного размера. Таким образом, SQLite «угадывает», какой размер, основываясь на содержимом поля. Поскольку значение в поле last_error меньше 2147483647, но больше 16777215, я предполагаю, что SQLite рассматривает поле как SIGNED INTEGER (то есть 32-разрядное число со знаком).

Таким образом, я предполагаю, что когда происходит bind_param, выполняется какая-то проверка, в результате которой DBI идентифицирует last_error как ПОДПИСАННЫЙ ИНТЕГЕР. В результате значение 4294967295 переполняется, или уменьшается до нуля, или что-то в этом роде, и сравнение работает неправильно.

Итак, мой вопрос (ы):

  1. Верна ли эта гипотеза (согласно какой-то документации, которую я пропустил)? или
  2. Есть ли способ подтвердить эту гипотезу?
  3. Это ошибка, или есть разумный обходной путь, учитывая, что я использую DBIx :: Class, поэтому я несколько отвлечен от базы данных.

1 Ответ

1 голос
/ 19 августа 2011

Как насчет использования литерала sql в вашем запросе:

my $cond = " < $range->{end} ";

my @entries =  $self->{row}->search_related_rs('eventlog_entries')
                       ->search_related('entry', {
                         first_error => {'>', $range->{start}},
                         last_error  => \$cond,
                       }
       )->all();

Должно работать, если perl преобразует $range->[end} в положительное число.

...