Привязка "unsigned long" (uint64) в заявлении sqlite3? С++
Я использую библиотеку sqlite3, доступную по адресу sqlite.org.
У меня есть несколько unsigned longs, которые я хотел бы хранить в базе данных. Я не хочу самостоятельно строить запрос и оставлять его открытым для какой-либо инъекции (будь то случайный или нет). Таким образом, я использую функции sqlite_bind_*
для "дезинфекции" моих параметров.
Проблема заключается в том, что для целых чисел без знака нет целых чисел, а только целых чисел.
int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
У меня наверняка будут числа, которые будут переполняться, если я не смогу хранить их неподписанным образом.
Мне нужно будет самому справиться с этим? (т.е. приведение к неподписанному типу после выбора из db или литья в подписанный тип перед вставкой в базу данных)
Если мне действительно нужно самому управлять этим, как бы сделать некоторые сравнительные запросы, которые хранятся в виде знакового длинного целого числа, когда сравнение действительно предназначено для использования в неподписанном диапазоне?
Глядя на типы данных INTEGER, которые преобразуются, можно было бы подумать, что беззнаковые longs могут быть представлены без проблем.
Если есть другие доступные решения, пожалуйста, просветите меня!
Спасибо!
Ответы
Ответ 1
Если вы хотите сохранить 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 аналогичным образом.
Это не идеальное решение, так как вам нужно будет написать пользовательскую функцию для каждой операции, которую вы хотите сделать, но она должна по крайней мере работать.
Ответ 2
В базе данных SQLite нет возможности хранить в нем неподписанные 64-битные целые числа. Это просто ограничение данных.
Ваши варианты:
- Сохраните его как строку, конвертируя при необходимости.
- Сохраняйте его как двоичный blob, конвертируя при необходимости.
- Представьте, что это подписанное 64-битное целое с литой, при этом преобразование при необходимости.
- Сохраняйте две части информации в виде двух столбцов: 63-битное целое число без знака (младшие 63-разрядные) и значение, представляющее бит знака.
Поскольку это хеши, вам, вероятно, не нужны сравнения, кроме проверки равенства. Таким образом, большинство из этих методов будет работать отлично для вас.
Ответ 3
Я попробовал несколько разных подходов, в основном, пытаясь заставить тип столбца работать как sqlite3_uint64, как описано в https://www.sqlite.org/c3ref/int64.html
Не повезло, поскольку uint64 не был сохранен как uint64 в таблице. Когда я посмотрел на сохраненные данные, он был подписан, поэтому я предположил, что он был сохранен как int64.
Я закончил префикс значения одним char, чтобы заставить sqlite хранить его как текст без какого-либо преобразования. Было гораздо проще просто добавить char в значение выбора запроса для сравнения. Таким образом, 9223360144090060712 стал A9223360144090060712, и я мог просто сделать SELECT WHERE HASH = 'A9223360144090060712'
Ответ 4
Лично я обнаружил, что самый простой способ справиться с этой проблемой, при этом позволяя базе данных выполнять сравнения и т.д., - это хранить целые числа без знака в базе данных с помощью функции 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.
Ответ 5
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, как обычно.
Ответ 6
это взломать, но 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