【问题标题】:Integer comparision using DBD::SQLite使用 DBD::SQLite 进行整数比较
【发布时间】:2011-08-18 22:50:43
【问题描述】:

我遇到了针对 SQLite3 数据库的 DBIx::Class 问题。

如果您不想完整阅读以下内容,这里是 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_errorlast_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 将该字段视为带符号整数(即带符号的 32 位数字)。

因此,我的猜测是,当 bind_param 发生时,会进行某种检查,这会导致 DBI 将 last_error 识别为有符号整数。结果,4294967295 的值溢出,或者被压为零,或者类似的东西,并且比较不正确。

因此,我的问题:

  1. 这个假设是否正确(根据我忽略的一些文档)?或
  2. 有没有办法证实这个假设?
  3. 这是一个错误,还是有一个合理的解决方法,记住我使用的是 DBIx::Class,所以我从数据库中抽象出来了。

【问题讨论】:

  • 您对“亲和力”类型是正确的。请参阅 sqlite 数据类型文档 (sqlite.org/datatype3.html) 中的第 2.0 节“类型关联”部分 sqlite 始终将整数视为无符号,所以我认为问题出在 DBD::SQLite
  • 实际上,仔细观察,我看到:“INTEGER。该值是一个有符号整数,根据值的大小存储在 1、2、3、4、6 或 8 个字节中。 “...所以 INTEGER 被签名...所以问题实际上是推断的大小。
  • 对不起 - 我的意思是说“sql总是将整数视为有符号”而不是“无符号”:-)

标签: perl sqlite comparison integer dbi


【解决方案1】:

如何在查询中使用文字 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-&gt;[end} 字符串化为正数,应该可以工作。

【讨论】:

  • 我给你一个赞成票,因为这是一个有效的解决方案。但是,我仍然想正确理解这里发生的事情以及是否有解决根本问题的方法。
猜你喜欢
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
相关资源
最近更新 更多