18

我正在使用sqlite.org上提供的 sqlite3 库。

我有一些未签名的 long 想要存储在数据库中。我不想自己构建查询并将其留给某种注入(无论它是否是偶然的)。因此,我正在使用这些sqlite_bind_*函数来“清理”我的参数。

问题是无符号长整数没有函数类型,只有整数。

int sqlite3_bind_int(sqlite3_stmt*, int, int);

int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);

如果我无法以无符号方式存储它们,我肯定会有数字溢出。

我需要自己管理吗?(即从数据库中选择后转换为无符号类型或在插入数据库之前转换为有符号类型)

如果我必须自己进行管理,那么当比较实际上是在无符号范围内时,如何进行一些存储为有符号长整数的比较查询?

查看被转换的INTEGER 数据类型,人们会认为 unsigned long 可以毫无问题地表示。

如果有其他解决方案,请赐教!谢谢!

4

6 回答 6

14

SQLite 数据库无法在其中存储无符号 64 位整数。这只是数据的限制。

您的选择是:

  • 将其存储为字符串,根据需要进行转换。
  • 将其存储为二进制 blob,根据需要进行转换。
  • 假设它是一个带强制转换的有符号 64 位整数,因此根据需要进行转换。
  • 将两条信息存储为两列:无符号 63 位整数(低 63 位)和表示高位的值。

由于这些是哈希,您可能不关心除相等测试之外的比较。所以这些方法中的大多数对你来说都很好。

于 2011-12-14T04:38:22.350 回答
10

如果您想将 uint64_t 存储在 sqlite 数据库中并仍然允许它在 SQL 中用作 uint64_t,那么您可能需要编写一些自定义函数。

您只需在向数据库发送数据和从数据库发送数据时将 uint64_t 转换为 int64_t,然后编写自定义函数来执行您需要的任何比较等。例如,做一个大于比较:

void greaterThan( sqlite3_context* ctx, sqlite3_value** values )
{
    uint64_t value1 = boost::numeric_cast< uint64_t >( sqlite3_value_int64( value[ 0 ] ) );
    uint64_t value2 = boost::numeric_cast< uint64_t >( sqlite3_value_int64( value[ 1 ] ) );
    sqlite3_result_int( ctx, value1 > value2 );
}

//Register this function using sqlite3_create_function
sqlite3_create_function( db, "UINT_GT", 2, SQLITE3_ANY, NULL, &greaterThan, NULL, NULL );

然后在 SQL 中使用它:

SELECT something FROM aTable WHERE UINT_GT(value1,value2);

或者,如果您需要基于 uint64_t 的自定义排序规则,您应该能够以类似的方式使用 sqlite3_create_collat​​ion。

这不是一个完美的解决方案,因为您需要为要执行的每个操作编写自定义函数,但它至少应该可以工作。

于 2011-12-14T10:56:13.807 回答
1

uint32_t完全适合在 a 内int64_t而无需担心,因此您可以进行简单的分配。

    int64_t i64;
    uint32_t u32 = 32;
    i64 = u32;

分配另一种方式,您应该检查边界,int64_t以便及早发现对数据库内部值所做的任何更改。

    int64_t i64 = 32;
    uint32_t u32;
    if (i64 < 0 || i64 > std::numeric_limits<uint32_t>::max())
        throw std::runtime_error("invalid uint32_t");
    u32 = i64;

然后,您可以int64像往常一样将您的数据发送到 sqlite 数据库。

于 2011-12-13T23:20:57.130 回答
1

我尝试了许多不同的方法,主要是试图让列类型作为 sqlite3_uint64 工作,如https://www.sqlite.org/c3ref/int64.html中所述

没有运气,因为 uint64 没有作为 uint64 存储在表中。当我查看已签名的存储数据时,我假设它存储为 int64。

我最终用一个 char 为该值添加前缀,以强制 sqlite 将其存储为文本而不发生任何转换。将字符添加到选择查询值进行比较要容易得多。因此 9223360144090060712 变成 A9223360144090060712,我可以简单地做一个 SELECT WHERE HASH = 'A9223360144090060712'

于 2015-09-08T13:56:36.883 回答
-1

这是一个黑客,但 int 和 unsigned 具有相同的位深度,所以你可以

int i=static_cast<int>(unsigned_int);

并将其转换回来

unsigned unsigned_int=static_cast<unsigned>(i);

在您的平台上保持安全

static_assert(sizeof(int)==sizeof(unsigned));

或使用特定的宽度

#include <stdint.h>
uint32_t
int32_t 
于 2011-12-13T21:58:58.333 回答
-1

就个人而言,我发现在仍然允许数据库执行比较等的同时处理此问题的最简单方法是使用该sqlite3_bind_int64函数将无符号整数存储在数据库中。我假设您使用的是 32 位无符号整数,它可以很容易地存储在 64 位有符号整数中。

例如:

uint32_t x = 0xFFFFFFFF;
sqlite3_bind_int64( stmt, 1, x );

然后检索您的变量(假设您使用的是 boost,否则您必须编写自己的溢出检查代码):

 #include <boost/numeric/conversion/cast.hpp>
 ...
 uint32_t x = boost::numeric_cast< uint32_t >( sqlite3_column_int64( stmt, 0 ) );

当然,如果你想存储 unsigned int64s,这会分崩离析,但这是 sqlite 的一个限制。

于 2011-12-13T22:05:46.800 回答