2011-12-13 3 views
14

Я использую библиотеку sqlite3, доступную по адресу sqlite.org.Связывание «unsigned long» (uint64) в заявлении sqlite3? C++

У меня есть несколько неподписанных длин, которые я хотел бы хранить в базе данных. Я не хочу самостоятельно строить запрос и оставлять его открытым для какой-либо инъекции (будь то случайный или нет). Таким образом, я использую функции sqlite_bind_* для «дезинфекции» моих параметров.

Проблема заключается в том, что для целых чисел без знака целых чисел нет типа функции.

int sqlite3_bind_int(sqlite3_stmt*, int, int);

int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);

Я определенно буду иметь номера, которые переполнится, если я не могу хранить их в беззнаковое образом.

Должен ли я сам управлять этим? (т. е. приведение к неподписанному типу после выбора из db или отбрасывания до подписанного типа перед вставкой в ​​базу данных)

Если мне действительно нужно управлять этим, как бы сделать некоторые сравнительные запросы, которые хранятся в виде подписанного длинного целого числа когда сравнения действительно предназначены для неподписанного диапазона?

Рассматривая INTEGER datatypes, которые преобразуются, можно подумать, что беззнаковые долготы могут быть представлены без проблем.

Если есть другие решения, пожалуйста, просветите меня! Спасибо!

+0

Я столкнулся с этой проблемой и решил ее, рассматривая 64-битный хэш без знака как подписанное 64-битное значение с целью помещая его в базу данных. – Michael

ответ

8

Если вы хотите сохранить uint64_t в базе данных sqlite и по-прежнему разрешать использовать его в качестве uint64_t в SQL, вам, вероятно, понадобится написать некоторые пользовательские функции.

Вам просто нужно направить 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_collation в аналогичным образом.

Это не идеальное решение, так как вам нужно будет написать пользовательскую функцию для каждой операции, которую вы хотите сделать, но она должна хотя бы работать.

-1

это хак, но Int и без знака имеют такую ​​же битовую глубину, чтобы вы могли

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 
+0

Я думаю, что если вы застряли с db, который поддерживает dosent unsigned int, немного взломайте, как это меньше всего из ваших проблем. не уверен, что вам даже придется конвертировать до записи в db, поскольку все неподписанные значения изменяют значение одного бита. – Stuart

+1

Это не отличная идея, когда вы хотите использовать что-либо, кроме базового SQL, для значений. SQLite будет думать, что любые большие uint - фактически отрицательные числа. Поэтому порядок и сравнения выходят из окна. Не говоря уже о том, чтобы случайно забыть броски и тому подобное. – obmarg

+0

Это правда, если вы собираетесь сравнивать значение, которое вызовет проблемы, если вы просто используете SQLite в качестве файла хранилища, я бы не стал беспокоиться. – 111111

0

Лично я обнаружил, что самый простой способ справиться с этой проблемой при одновременном разрешении базы данных выполнять сравнения и т. Д. - хранить целые числа без знака в базе данных с помощью функции sqlite3_bind_int64. Я предполагаю, что вы используете 32-разрядные целые числа без знака, которые могут быть легко сохранены в 64-битном значении целого числа.

Например:

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

Затем, чтобы получить переменную (предполагая, что вы используете импульс, в противном случае вам придется написать свой собственный чек переполнения код):

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

Конечно это разваливается, если вы хотите хранить неподписанные int64, но это немного ограничено sqlite.

+1

Спасибо за ваш ответ. Я не могу контролировать свой источник данных и иметь значения за пределами int64. Конечно, я мог хранить данные без проблем, но делать сравнения запросов было бы невозможно. Если мои данные будут вписываться в 'int64', у меня не было бы никаких проблем, и я бы не задал вопрос. – g19fanatic

+0

@ g19fanatic Ах, моя ошибка - я предположил, что беззнаковое целое вы просто имели в виду uint32_t. Возможно, вам захочется взглянуть на альтернативный бэкэнд базы данных, если вы не можете жить с опцией кастинга. – obmarg

+0

Моя вина на самом деле, недостаточно конкретная – g19fanatic

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 базы данных, как обычно.

+0

Спасибо за ваш ответ. Я не могу контролировать свой источник данных и иметь значения за пределами int64. Конечно, я мог хранить данные без проблем, но делать сравнения запросов было бы невозможно. Если мои данные будут вписываться в 'int64', у меня не было бы никаких проблем, и я бы не задал вопрос. – g19fanatic

8

База данных SQLite не имеет возможности хранить в ней беззнаковые 64-разрядные целые числа. Это просто ограничение данных.

варианты:

  • магазин его в виде строки, преобразуя по мере необходимости.
  • Храните его как бинарный блок, преобразовывая при необходимости.
  • Представьте, что это подписанное 64-разрядное целое с литой, при этом, при необходимости, преобразование.
  • Храните две части информации в виде двух столбцов: неподписанное 63-битное целое число (младшие 63 бита) и значение, представляющее бит знака.

Поскольку эти хеши, вы, вероятно, не заботитесь о сравнении, кроме проверки равенства. Таким образом, большинство из этих методов будет отлично работать для вас.

+0

Но эта страница (https://www.sqlite.org/c3ref/int64.html) явно упоминает тип 'sqlite3_uint64', что здесь происходит? @! – Michael

+1

@Michael: Что происходит, так это то, что SQLite объявляет 64-битный тип без знака. Это не значит, что вы можете хранить его в таблице. [Документы делают это совершенно ясно) (https://www.sqlite.org/datatype3.html), что значения INTEGER всегда являются целыми целыми числами, размером до 64 бит. –

1

Я попробовал несколько различных подходов, в основном пытаются получить тип столбца, чтобы работать в качестве sqlite3_uint64, как описано в https://www.sqlite.org/c3ref/int64.html

не везло как uint64 не был сохранен как uint64 в таблице. Когда я посмотрел на сохраненные данные, он был подписан, поэтому я предположил, что он был сохранен как int64.

Я закончил префикс значения с помощью одного символа, чтобы заставить sqlite хранить его как текст без какого-либо преобразования. Было гораздо проще просто добавить символ в значение выбора запроса для сравнения. Таким образом, 9223360144090060712 стал A9223360144090060712, и я мог просто сделать SELECT WHERE HASH = 'A9223360144090060712'

 Смежные вопросы

  • Нет связанных вопросов^_^