Hi!
I use a simple trick:
A clone of the sqlite3_exec that passes the sqlite3_stmt as an argument
to the callback function.
So no conversion is made and we can use the sqlite3_column... functions
directly on the retrieved row.
Happy coding!
On 2019-07-30 05:00, sqlite-users-requ...@mailinglists.sqlite.org wrote:
Send sqlite-users mailing list submissions to
sqlite-users@mailinglists.sqlite.org
6. sqlite3_exec without ubiqitous text conversions (Barone Ashura)
------------------------------
Message: 6
Date: Tue, 30 Jul 2019 13:13:52 +0200
From: Barone Ashura <bar0n3ash...@gmail.com>
To: sqlite-users@mailinglists.sqlite.org
Subject: [sqlite] sqlite3_exec without ubiqitous text conversions
Message-ID:
<cad3lth+nfostqjf50dgsdzuts0exvk7n89amtsjpygx_6b+...@mail.gmail.com>
Content-Type: text/plain; charset="UTF-8"
Hello,
I have been working on an application based on SQLite for 2 years now,
and
recently we started running some performance profiling to check if
there
are areas where we can squeeze some extra performance.
SQlite query execution is used almost exclusively through sqlite3_exec,
and
the implementation of callbacks.
One of the areas of interest we identified is that every column is
always
passed to the callback as a c-char-string. Then we need to convert that
string back to the intended data type. Apparently a consistent amount
of
time is spent doing this.
I started looking into SQLite code and realized that int and real
values
retrieved from database, is actually fecthed as binary value, then
converted to string, passed to the callback, and in the callback we
convert
it back to its original data type (as we keep track of 'true' datatype
for
each column).
I decided to try skipping these conversions, by creating a custom
implementation of sqlite3_exec, which does the following in the inside
'step' loop:
if (rc == SQLITE_ROW) {
azVals = azCols;
for (i = 0; i < nCol; i++) {
azVals[i] = (char *)sqlite3_column_text(pStmt,
i);
int col_type = sqlite3_column_type(pStmt, i);
switch (col_type)
{
case SQLITE_INTEGER:
case SQLITE_FLOAT:
{
azVals[i] = (char*)columnMem(pStmt, i);
columnMallocFailure(pStmt);
break;
}
default:
{
azVals[i] = (char
*)sqlite3_column_text(pStmt, i);
break;
}
}
if (!azVals[i] && sqlite3_column_type(pStmt, i)
!=
SQLITE_NULL) {
sqlite3OomFault(db);
goto exec_out;
}
}
}
Instead of ALWAYS converting to text, in case of INTEGER or FLOAT
columns
types, I fetch the address of the data through columnMem, put it in
the
azVals array, and go on. The callback knows which is the expected data
type
for the column, reads and copies data in the destination variable
through a
simple assignment.
I am aware that forcing a 'typed' pointer into a generic char* pointer
(azVals[x]), rings a looooot of alarm bells; I am, as well, aware that
I
could avoid sqlite3_exec, and call sqlite3_step myself.
As of now the application is running smoothly and faster as far as
satabase
access is concerned.
I am writing here to get opinions about other potential pitfalls or
oversights in this approach, as well as reason why I should not proceed
on
this path.
Thanks in advance
_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users