在sqlite3语句中绑定'unsigned long'(uint64)? C ++

时间:2011-12-13 21:54:59

标签: c++ sqlite bind unsigned-integer

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

我想要存储在数据库中的一些unsigned longs。我不想自己构建查询并让它开放某种注入(无论是否是偶然的)。因此,我正在使用sqlite_bind_*函数来“清理”我的参数。

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

int sqlite3_bind_int(sqlite3_stmt*, int, int);

int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);

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

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

如果我自己必须自己管理,当比较真的要在无符号范围内时,如何做一些存储为有符号长整数的比较查询?

查看转换后的INTEGER datatypes,可以认为无符号长号可以毫无问题地表示。

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

6 个答案:

答案 0 :(得分:9)

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

您的选择是:

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

由于这些是哈希值,您可能不关心除了相等测试之外的比较。因此,大多数这些方法对您来说都很合适。

答案 1 :(得分:8)

如果你想在sqlite数据库中存储uint64_t并仍然允许它在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。

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

答案 2 :(得分:1)

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

没有运气,因为uint64没有作为uint64存储在表中。当我查看存储的数据时,它被签名,所以我认为它存储为int64。

我最后用一个字符加上前缀,强制sqlite将其存储为文本而不进行任何转换。将char添加到select查询值以进行比较要容易得多。因此9223360144090060712成为A9223360144090060712,我可以简单地做一个SELECT WHERE HASH =&#39; A9223360144090060712&#39;

答案 3 :(得分:0)

就我个人而言,我发现在允许数据库执行比较等同时处理此问题的最简单方法是使用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 ) );

当然,如果你想存储无符号的int64,这会分崩离析,但这有点像sqlite的限制。

答案 4 :(得分:0)

uint32_t完全适合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数据库。

答案 5 :(得分:-1)

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

int i=static_cast<int>(unsigned_int);

并将其转换回来

unsigned unsigned_int=static_cast<unsigned>(i);

在你的plaftform上保持安全

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

或使用特定宽度

#include <stdint.h>
uint32_t
int32_t