Search code examples
sqlitevfs

SQLite: How embed the memvfs extension to Amalgamation?


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 :(


Solution

  • 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().