Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding an 'unsigned long' (uint64) in an sqlite3 statement? C++

I'm using the sqlite3 library that is available at sqlite.org.

I have some unsigned longs that I would like store in a database. I do not want to construct the query myself and leave it open to some sort of injection (whether it be accidental or not). Thus, I'm using the sqlite_bind_* functions to 'sanitize' my parameters.

The issue is that there isn't a function type for unsigned long integers, just integers.

int sqlite3_bind_int(sqlite3_stmt*, int, int);

int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);

I am definitely going to have numbers that will overflow if I am unable to store them in an unsigned manner.

Am I going to need to manage this myself? (i.e. casting to an unsigned type after selecting from the db or casting to signed type before inserting into database)

If I do have to manage this myself, how would one do some comparison queries that are stored as a signed long integer when the comparisons are really meant to be in the unsigned range?

Looking at the INTEGER datatypes that get converted, one would think that unsigned longs could be represented without issue.

If there are other solutions available, please enlighten me! Thanks!

like image 743
g19fanatic Avatar asked Dec 13 '11 21:12

g19fanatic


People also ask

Is uint64_t same as unsigned long long?

In 32-bit mode, the compiler (more precisely the <stdint. h> header) defines uint64_t as unsigned long long , because unsigned long isn't wide enough. In 64-bit mode, it defines uint64_t as unsigned long . It could have defined it as unsigned long long in both modes.

Does sqlite3 support concurrency?

Usually, SQLite allows at most one writer to proceed concurrently. The BEGIN CONCURRENT enhancement allows multiple writers to process write transactions simultanously if the database is in "wal" or "wal2" mode, although the system still serializes COMMIT commands.

Can SQLite have multiple connections?

SQLite does support multiple concurrent connections, and therefore it can be used with a multi-threaded or multi-process application. The catch is that when SQLite opens a write transaction, it will lock all the tables.

What is UInt64?

The UInt64 value type represents unsigned integers with values ranging from 0 to 18,446,744,073,709,551,615. Important. The UInt64 type is not CLS-compliant. The CLS-compliant alternative type is Decimal. Int64 can be used instead to replace a UInt64 value that ranges from zero to MaxValue.


3 Answers

An SQLite database does not have the ability to store unsigned 64-bit integers in it. It's just a limitation of the data.

Your options are:

  • Store it as a string, converting as needed.
  • Store it as a binary blob, converting as needed.
  • Pretend that it is a signed 64-bit integer with a cast, thus converting as necessary.
  • Store two pieces of information as two columns: the unsigned 63-bit integer (the lower 63-bits), and a value that represents the high bit.

Since these are hashes, you probably don't care about comparisons other than equality testing. So most of these methods would work just fine for you.

like image 121
Nicol Bolas Avatar answered Oct 27 '22 16:10

Nicol Bolas


If you wanted to store a uint64_t in a sqlite database and still allow it to be used as a uint64_t in SQL, then you're probably going to need to write some custom functions.

You simply need to cast the uint64_t to an int64_t when sending to and from the database, and then write a custom function to perform whatever comparison etc. you need. For example, to do a greater than comparison:

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 );

Then to use this in SQL:

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

Alternatively, if you need custom collation based on uint64_t, you should be able to use sqlite3_create_collation in a similar manner.

It's not a perfect solution as you'll need to write a custom function for every operation you want to do, but it should at least work.

like image 40
obmarg Avatar answered Oct 27 '22 16:10

obmarg


uint32_t fits entirely inside of a int64_t without any worries, so you can do a simple assignment.

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

Assigning the other way you should check the bounds of the int64_t so that any changes made to the value inside of the database are caught early.

    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;

You can then send your int64 to the sqlite database as you normally would.

like image 20
Tom Kerr Avatar answered Oct 27 '22 16:10

Tom Kerr