I need to load/save SQLite database in memory buffer. For this, I want embed the memvfs extension into sqlite3 code and compile it wholly as sqlite3.dll.
How do it?
Update1:
I want use the memvfs as temp memory buffer. My program load data from net to buffer, connect to this memory buffer and restore data into empty in-memory db. I thoutgh that inclusion of memvfs to sqlite amalgamation would improve perfomance.
Update2:
If you want to use memvfs extension pay attention to bug in readme comment in source. Use "PRAGMA journal_mode=OFF" instead "journal_mode=NONE"
Update3:
Another bug in memvfs.c - use 'max' instead 'maxsz' for maxsz param in URI.
The sqlite developers carefully set a rakes :(
Test program to demonstrate using memvfs:
#include <fcntl.h>
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
sqlite3 *db;
char *err;
// Open an in-memory database to use as a handle for loading the memvfs extension
if (sqlite3_open(":memory:", &db) != SQLITE_OK) {
fprintf(stderr, "open :memory: %s\n", sqlite3_errmsg(db));
return EXIT_FAILURE;
}
sqlite3_enable_load_extension(db, 1);
if (sqlite3_load_extension(db, "./memvfs", NULL, &err) != SQLITE_OK) {
fprintf(stderr, "load extension: %s\n", err);
return EXIT_FAILURE;
}
// Done with this database
sqlite3_close(db);
// Read the real database into memory
int fd = open("foo.db", O_RDONLY);
if (fd < 0) {
perror("open");
return EXIT_FAILURE;
}
struct stat s;
if (fstat(fd, &s) < 0) {
perror("fstat");
return EXIT_FAILURE;
}
void *memdb = sqlite3_malloc64(s.st_size);
if (read(fd, memdb, s.st_size) != s.st_size) {
perror("read");
return EXIT_FAILURE;
}
close(fd);
// And open that memory with memvfs now that it holds a valid database
char *memuri = sqlite3_mprintf("file:whatever?ptr=0x%p&sz=%lld&freeonclose=1",
memdb, (long long)s.st_size);
printf("Trying to open '%s'\n", memuri);
if (sqlite3_open_v2(memuri, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI,
"memvfs") != SQLITE_OK) {
fprintf(stderr, "open memvfs: %s\n", sqlite3_errmsg(db));
return EXIT_FAILURE;
}
sqlite3_free(memuri);
// Try querying the database to show it works.
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, "SELECT b FROM test", -1, &stmt, NULL) !=
SQLITE_OK) {
fprintf(stderr, "prepare: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return EXIT_FAILURE;
}
for (int rc = sqlite3_step(stmt); rc == SQLITE_ROW; rc = sqlite3_step(stmt)) {
printf("%d\n", sqlite3_column_int(stmt, 0));
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
}
Usage:
# Create a test database to use with memvfs
$ sqlite3 foo.db
sqlite> CREATE TABLE test(b INTEGER);
sqlite> INSERT INTO test VALUES (1), (2);
sqlite> .quit
# Compile the memvfs module and test program
$ gcc -O -fPIC -shared -o memvfs.so memvfs.c
$ gcc -O -Wall -Wextra testmem.c -lsqlite3
# And run it.
$ ./a.out
Trying to open 'file:whatever?ptr=0x56653FE2B940&sz=8192&freeonclose=1'
1
2
Same workflow if you compile it directly into your program instead of using a loadable module; you just have to call sqlite3_memvfs_init() with the right arguments instead of using sqlite3_load_extension().
Related
C code:
sql.c
#include <stdio.h>
#include <sqlite3.h>
int main(int argc, char* argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("test.db", &db);
if( rc ) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return(0);
} else {
fprintf(stderr, "Opened database successfully\n");
}
sqlite3_close(db);
}
compile:
gcc sql.c -DSQLITE_DEFAULT_FILE_PERMISSIONS=0600 -lsqlite3
File permissions are not changing:
-rw-r--r-- 1 user user2 0 Feb 15 02:17 test.db
Can any one please help me to change default file permission
Using Qt 5.15.2 on Windows 10 x64
My sqlite3.h says
#define SQLITE_VERSION "3.34.0"
#define SQLITE_VERSION_NUMBER 3034000
#define SQLITE_SOURCE_ID "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"
I'm copying a temp database to disk (:memory: if release, tmp file if debug) according to the sqlite3 documentation and other stuff I've found on this site.
How to backup/store between sqlite memory database and file database in Qt?
How to access sqlite3 directly from Qt without linking sqlite3.dll a second time
The problem is if the database is over a given size, the db.close() line crashes the program, showing me this.
The database is created like this. If I use 100 instead of 1000 it seems to work fine, no crash.
void SymbolLibDocument::init() {
if (m_activeState) {
return;
} else {
// Creates temp database to prime save, save as, etc.
// Does not create anything with full filename
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", QString::fromStdString(m_connName));
#if defined(QT_DEBUG)
db.setDatabaseName("tmp_" + QString::fromStdString(m_name));
#elif defined(QT_NO_DEBUG)
db.setDatabaseName(":memory:");
#endif
if(!db.open()) {
qDebug() << "Can't create database";
}
QSqlQuery query(db);
const
QStringList qsl = {"DROP TABLE IF EXISTS hello;",
"CREATE TABLE hello (ID INTEGER PRIMARY KEY AUTOINCREMENT, \n"
" name TEXT CHECK(length(name) > 0)); ",
"INSERT INTO hello (name) VALUES ('giraffe');"};
dbutils::executeList(query, qsl, "Could not init", __LINE__);
query.exec("BEGIN");
QString s = QString("INSERT INTO hello (name) VALUES (:v);");
query.prepare(s);
for (int i = 0; i < 10000; i++) {
query.bindValue(":v", QVariant(i));
query.exec();
}
query.exec("COMMIT");
}
m_activeState = true;
}
void dbSaveFromTo(const std::string & connFrom, const std::string & fileTo) {
// Uses sqlite3 backup mechanism to write database connFrom to fileTo
//auto secs = std::chrono::milliseconds(50);
QString qsConnFrom = QString::fromStdString(connFrom);
// Need to clone db so it can be used from this function, which is callable
// as another thread.
// Also this needs to go in another scope so db object is destroyed
// at exit, prior to close and removal
{
QSqlDatabase db = QSqlDatabase::cloneDatabase(qsConnFrom, "CloneDb");
db.open();
QVariant qvhandle = db.driver()->handle();
if (qvhandle.isValid() && qstrcmp(qvhandle.typeName(), "sqlite3*") == 0) {
sqlite3 *pFrom = *static_cast<sqlite3 **>(qvhandle.data());
sqlite3 *pTo;
sqlite3_open(fileTo.c_str(), &pTo);
sqlite3_backup *pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main");
if (pBackup) {
int pagesPerCycle = 1;
int pageCount = 0;
int pagesCopied = 0;
int pagesRemaining = 0;
do {
(void) sqlite3_backup_step(pBackup, pagesPerCycle);
if (sqlite3_errcode(pFrom) != SQLITE_OK) {
qDebug() << sqlite3_errmsg(pFrom);
}
if (sqlite3_errcode(pTo) != SQLITE_OK) {
qDebug() << sqlite3_errmsg(pTo);
}
pageCount = sqlite3_backup_pagecount(pBackup);
pagesRemaining = sqlite3_backup_remaining(pBackup);
pagesCopied = pageCount - pagesRemaining;
emit intEmitter.emitInt(pagesCopied, pageCount);
qDebug() << "emitting" << pagesCopied << "/" << pageCount;
//std::this_thread::sleep_for(secs);
} while(pagesRemaining > 0);
(void) sqlite3_backup_finish(pBackup);
} else {
throw std::logic_error("sqlite3_backup_init(...) failed");
}
// causes error 21 bad parameter or other API misuse,
// But this occurs even if open and close are called back-to-back
// with no operations in between
sqlite3_close(pTo);
if (sqlite3_errcode(pFrom) != SQLITE_OK) {
qDebug() << sqlite3_errmsg(pFrom);
}
if (sqlite3_errcode(pTo) != SQLITE_OK) {
qDebug() << sqlite3_errmsg(pTo);
}
} else {
throw std::logic_error("invalid driver handle");
}
db.close();
}
QSqlDatabase::removeDatabase("CloneDb");
}
I'm concerned this is caused because I'm using the built-in Qt Sql functionality (QT += sql in .pro file) for db.open, db.close, QSqlDatabase::cloneDatabase, etc.., but I'm using a separate sqlite3 precompiled .dll to access the sqlite3_backup_* functions.
Why does this crash when I do db.close()?
The problem was indeed some weird interaction between the Qt sqlite code used to access the db connection and the sqlite3.dll code used to init and step through the backup.
The solution was to use the Qt Sqlite functionality only to get the filename from the connection name, then use the raw sqlite api to open the source file and do the backup.
My use case is a main process that compiles a bunch of Open CL kernels. Later in the program several subprocesses are forked and will execute one or more of the kernels. For some reason the subprocesses have device errors.
I have determined the problem has nothing to do with compilation and have reproduced it with the following simple script:
import multiprocessing
import pyopencl as cl
def printme():
platforms = cl.get_platforms()
for p in platforms:
print 75*'!'
print p
print 75*':'
printme()
p = multiprocessing.Process(target = printme)
p.start()
p.join()
Seems to be tied to the NVIDIA OpenCL implementation and not related to PyOpenCL as I initially thought. The same problem occurs with the example below.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <CL/cl.h>
#define CHECK(A) if ((status = A) != 0) { \
fprintf(stderr, "failed status %d at line %d\n", status, __LINE__); \
exit(1); \
}
static void
runit() {
int i;
cl_int status;
cl_platform_id *platforms;
cl_uint num_platforms;
cl_uint num_devices;
CHECK(clGetPlatformIDs(0, NULL, &num_platforms));
if (num_platforms == 0) {
fprintf(stderr, "no platforms\n");
exit(1);
}
platforms = malloc(sizeof(cl_platform_id)*num_platforms);
CHECK(clGetPlatformIDs(num_platforms, platforms, NULL));
CHECK(clGetDeviceIDs(platforms[0], CL_DEVICE_TYPE_ALL, 0, NULL,
&num_devices));
free(platforms);
}
int main(void) {
runit();
if (fork() == 0) {
runit();
}
else {
wait(NULL);
}
return 0;
}
Exits with failed status = -33 (Invalid Device). Seems that there is some stored state inside the implementation. Note this is running from an NVidia driver.
Written a custom ranking function for the sqlite3 full text engine (fts5). The extension compiles but throws a "segmentation fault (core dumped) sqlite3" error when attempting to load it with ".load /var/www/rankprice.so" . Unsure about the correctness of my extension code.
I have replaced my original ranking function with code from the docs. The extension loading code is also pretty much copied from the docs.
THere are 3 functions:
static void rankprice( //Actual extension function,
fts5_api *fts5_api_from_db(sqlite3 *db) // Gets pointer to the fts5_api pointer
int sqlite3_extension_init // Loads the extension
The docs have not been updated on https://www.sqlite.org/fts5.html#extending_fts5 . In the function
fts5_api *fts5_api_from_db(sqlite3 *db){
fts5_api *pRet = 0;
sqlite3_stmt *pStmt = 0;
if( SQLITE_OK==sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0) ){
sqlite3_bind_pointer(pStmt, (void*)&pRet, "fts5_api_ptr", NULL);
sqlite3_step(pStmt);
}
sqlite3_finalize(pStmt);
return pRet;
}
sqlite3_bind_pointer should have 5 arguments , the second argument should be an int, but I am unsure of its correct value.
Here is the complete code:
#include <stdio.h>
#include <assert.h>
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
/*
** Actual extension function, copied from https://www.sqlite.org/fts5.html
*/
static void rankprice(
const Fts5ExtensionApi *pApi,
Fts5Context *pFts,
sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal
){
int rc;
int nToken;
rc = pApi->xColumnSize(pFts, -1, &nToken);
if( rc==SQLITE_OK ){
sqlite3_result_int(pCtx, nToken);
}else{
sqlite3_result_error_code(pCtx, rc);
}
}
/*
** Return a pointer to the fts5_api pointer for database connection db.
** If an error occurs, return NULL and leave an error in the database
** handle (accessible using sqlite3_errcode()/errmsg()).
*/
fts5_api *fts5_api_from_db(sqlite3 *db){
fts5_api *pRet = 0;
sqlite3_stmt *pStmt = 0;
if( SQLITE_OK==sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0) ){
sqlite3_bind_pointer(pStmt, 1,(void*)&pRet, "fts5_api_ptr", NULL);
sqlite3_step(pStmt);
}
sqlite3_finalize(pStmt);
return pRet;
}
/* Creates a SQL extension function. Tells sqlite which function to load as extension*/
int sqlite3_extension_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
fts5_api *fpApi;
fpApi = fts5_api_from_db(db);
SQLITE_EXTENSION_INIT2(pApi);
struct Builtin {
const char *zFunc; /* Function name (nul-terminated) */
void *pUserData; /* User-data pointer */
fts5_extension_function xFunc; /* Callback function */
void (*xDestroy)(void*); /* Destructor function */
} aBuiltin [] = {
{ "rankingMe", 0, rankprice, 0 },
};
int rc = SQLITE_OK;
rc = fpApi->xCreateFunction(fpApi,
aBuiltin[0].zFunc,
aBuiltin[0].pUserData,
aBuiltin[0].xFunc,
aBuiltin[0].xDestroy
);
return rc;
}
Compiled using
gcc -shared -fPIC -g /var/www/rankprice.c -o /var/www/rankprice.so
Exact error message:
SQLite version 3.28.0 2019-04-16 19:49:53
Connected to a transient in-memory database.
sqlite> .load /var/www/rankprice.so
[2] 52632 segmentation fault (core dumped) sqlite3
Links:
https://www.sqlite.org/fts5.html
https://github.com/sqlite/sqlite/blob/ff119f04b42a7ad3a16c306446af7e301d34026a/ext/fts5/fts5_aux.c
I am trying to add a custom sqlite3 regexp function into my Qt application (as recommended by this answer). But as soon as I call the sqlite3_create_function function, I get the message The program has unexpectedly finished. When I debug, it terminates in a segmentation fault in sqlite3_mutex_enter. There is a MWE below, with apologies for the absolute file paths.
The regexp implementation in my code is from this site; it also fails with the msign function here. The various checks of driver()->handle() are straight from the Qt docs.
Incidentally, I used select sqlite_version(); to determine that Qt 5.5 uses sqlite version 3.8.8.2. I found that version by looking through old commits in the Qt GitHub repository.
MWE.pro
QT += core gui
TARGET = MWE
TEMPLATE = app
QT += sql
SOURCES += main.cpp \
D:\Qt\Qt5.5.0\5.5\Src\3rdparty\sqlite\sqlite3.c
HEADERS += D:\Qt\Qt5.5.0\5.5\Src\3rdparty\sqlite\sqlite3.h
main.cpp
#include <QtSql>
#include "D:/Qt/Qt5.5.0/5.5/Src/3rdparty/sqlite/sqlite3.h"
void qtregexp(sqlite3_context* ctx, int argc, sqlite3_value** argv)
{
QRegExp regex;
QString str1((const char*)sqlite3_value_text(argv[0]));
QString str2((const char*)sqlite3_value_text(argv[1]));
regex.setPattern(str1);
regex.setCaseSensitivity(Qt::CaseInsensitive);
bool b = str2.contains(regex);
if (b)
{
sqlite3_result_int(ctx, 1);
}
else
{
sqlite3_result_int(ctx, 0);
}
}
int main(int argc, char *argv[])
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("my.db");
db.open();
QVariant v = db.driver()->handle();
if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*")==0) {
sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data());
if (db_handle != 0) { // check that it is not NULL
// This shows that the database handle is generally valid:
qDebug() << sqlite3_db_filename(db_handle, "main");
sqlite3_create_function(db_handle, "regexp", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, &qtregexp, NULL, NULL);
qDebug() << "This won't be reached."
QSqlQuery query;
query.prepare("select regexp('p$','tap');");
query.exec();
query.next();
qDebug() << query.value(0).toString();
}
}
db.close();
}
You need to call sqlite3_initialize() after you get the database handle from Qt, according to this forum post.
...
sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data());
if (db_handle != 0) { // check that it is not NULL
sqlite3_initialize();
...
This solves the issue.