У меня проблема с 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 переполняется, или уменьшается до нуля, или что-то в этом роде, и сравнение работает неправильно.
Итак, мой вопрос (ы):
- Верна ли эта гипотеза (согласно какой-то документации, которую я пропустил)? или
- Есть ли способ подтвердить эту гипотезу?
- Это ошибка, или есть разумный обходной путь, учитывая, что я использую DBIx :: Class, поэтому я несколько отвлечен от базы данных.