mirror of
https://github.com/tursodatabase/libsql.git
synced 2024-12-15 09:49:40 +00:00
a72c066a8e
wal extensions
305 lines
11 KiB
C
305 lines
11 KiB
C
#include "sqlite3.h"
|
|
#include "src/wal.h"
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#define LIBSQL_IMPORT(name) extern __attribute__((import_module("libsql_host"), import_name(name)))
|
|
|
|
LIBSQL_IMPORT("close") int libsql_wasi_close(sqlite3_file*);
|
|
LIBSQL_IMPORT("read") int libsql_wasi_read(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
LIBSQL_IMPORT("write") int libsql_wasi_write(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
|
|
LIBSQL_IMPORT("truncate") int libsql_wasi_truncate(sqlite3_file*, sqlite3_int64 size);
|
|
LIBSQL_IMPORT("sync") int libsql_wasi_sync(sqlite3_file*, int flags);
|
|
LIBSQL_IMPORT("file_size") int libsql_wasi_file_size(sqlite3_file*, sqlite3_int64 *pSize);
|
|
|
|
typedef struct libsql_wasi_file {
|
|
const struct sqlite3_io_methods* pMethods;
|
|
int64_t fd;
|
|
} libsql_wasi_file;
|
|
|
|
// We're running in exclusive mode, so locks are noops.
|
|
// We need to handle locking in the host.
|
|
static int libsql_wasi_lock(sqlite3_file* f, int eLock) {
|
|
(void)f, (void)eLock;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
static int libsql_wasi_unlock(sqlite3_file* f, int eLock) {
|
|
(void)f, (void)eLock;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
static int libsql_wasi_check_reserved_lock(sqlite3_file* f, int *pResOut) {
|
|
(void)f, (void)pResOut;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
static int libsql_wasi_device_characteristics(sqlite3_file* f) {
|
|
(void)f;
|
|
return SQLITE_IOCAP_ATOMIC | SQLITE_IOCAP_SAFE_APPEND | SQLITE_IOCAP_SEQUENTIAL;
|
|
}
|
|
|
|
static int libsql_wasi_file_control(sqlite3_file* f, int opcode, void* arg) {
|
|
(void)opcode, (void)f, (void)arg;
|
|
return SQLITE_NOTFOUND;
|
|
}
|
|
|
|
static int libsql_wasi_sector_size(sqlite3_file* f) {
|
|
(void)f;
|
|
return 512;
|
|
}
|
|
|
|
static const sqlite3_io_methods wasi_io_methods = {
|
|
.iVersion = 1,
|
|
.xClose = &libsql_wasi_close,
|
|
.xRead = &libsql_wasi_read,
|
|
.xWrite = &libsql_wasi_write,
|
|
.xTruncate = &libsql_wasi_truncate,
|
|
.xSync = &libsql_wasi_sync,
|
|
.xFileSize = &libsql_wasi_file_size,
|
|
.xLock = &libsql_wasi_lock,
|
|
.xUnlock = &libsql_wasi_unlock,
|
|
.xCheckReservedLock = &libsql_wasi_check_reserved_lock,
|
|
.xFileControl = &libsql_wasi_file_control,
|
|
.xSectorSize = &libsql_wasi_sector_size,
|
|
.xDeviceCharacteristics = &libsql_wasi_device_characteristics,
|
|
};
|
|
|
|
LIBSQL_IMPORT("open_fd") int64_t libsql_wasi_open_fd(const char *zName, int flags);
|
|
LIBSQL_IMPORT("delete") int libsql_wasi_delete(sqlite3_vfs*, const char *zName, int syncDir);
|
|
LIBSQL_IMPORT("access") int libsql_wasi_access(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
|
|
LIBSQL_IMPORT("full_pathname") int libsql_wasi_full_pathname(sqlite3_vfs*, const char *zName, int nOut, char *zOut);
|
|
LIBSQL_IMPORT("randomness") int libsql_wasi_randomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
LIBSQL_IMPORT("sleep") int libsql_wasi_sleep(sqlite3_vfs*, int microseconds);
|
|
LIBSQL_IMPORT("current_time") int libsql_wasi_current_time(sqlite3_vfs*, double*);
|
|
LIBSQL_IMPORT("get_last_error") int libsql_wasi_get_last_error(sqlite3_vfs*, int, char*);
|
|
LIBSQL_IMPORT("current_time_64") int libsql_wasi_current_time_64(sqlite3_vfs*, sqlite3_int64*);
|
|
|
|
int libsql_wasi_vfs_open(sqlite3_vfs *vfs, const char *zName, sqlite3_file *file_, int flags, int *pOutFlags) {
|
|
libsql_wasi_file *file = (libsql_wasi_file*)file_;
|
|
file->fd = libsql_wasi_open_fd(zName, flags);
|
|
if (file->fd == 0) {
|
|
return SQLITE_CANTOPEN;
|
|
}
|
|
file->pMethods = &wasi_io_methods;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
sqlite3_vfs libsql_wasi_vfs = {
|
|
.iVersion = 2,
|
|
.szOsFile = sizeof(libsql_wasi_file),
|
|
.mxPathname = 100,
|
|
.zName = "libsql_wasi",
|
|
|
|
.xOpen = &libsql_wasi_vfs_open,
|
|
.xDelete = &libsql_wasi_delete,
|
|
.xAccess = &libsql_wasi_access,
|
|
.xFullPathname = &libsql_wasi_full_pathname,
|
|
.xRandomness = &libsql_wasi_randomness,
|
|
.xSleep = &libsql_wasi_sleep,
|
|
.xCurrentTime = &libsql_wasi_current_time,
|
|
.xGetLastError = &libsql_wasi_get_last_error,
|
|
.xCurrentTimeInt64 = &libsql_wasi_current_time_64,
|
|
};
|
|
|
|
libsql_wal_methods *the_wal_methods = NULL;
|
|
|
|
int libsql_wasi_wal_open(sqlite3_vfs* vfs, sqlite3_file* f, const char* path, int no_shm_mode, long long max_size, struct libsql_wal_methods* wal_methods, libsql_wal** wal) {
|
|
fprintf(stderr, "Opening virtual WAL at %s: %s\n", path, wal_methods->zName);
|
|
return the_wal_methods->xOpen(vfs, f, path, no_shm_mode, max_size, wal_methods, wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_close(libsql_wal* wal, sqlite3* db, int sync_flags, int nBuf, unsigned char* zBuf) {
|
|
return the_wal_methods->xClose(wal, db, sync_flags, nBuf, zBuf);
|
|
}
|
|
|
|
void libsql_wasi_wal_limit(libsql_wal* wal, long long limit) {
|
|
return the_wal_methods->xLimit(wal, limit);
|
|
}
|
|
|
|
int libsql_wasi_wal_begin_read_transaction(libsql_wal* wal, int* out) {
|
|
return the_wal_methods->xBeginReadTransaction(wal, out);
|
|
}
|
|
|
|
void libsql_wasi_wal_end_read_transaction(libsql_wal* wal) {
|
|
return the_wal_methods->xEndReadTransaction(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_find_frame(libsql_wal* wal, unsigned int frame, unsigned int* out) {
|
|
return the_wal_methods->xFindFrame(wal, frame, out);
|
|
}
|
|
|
|
int libsql_wasi_wal_read_frame(libsql_wal* wal, unsigned int frame, int n, unsigned char* out) {
|
|
return the_wal_methods->xReadFrame(wal, frame, n, out);
|
|
}
|
|
|
|
unsigned int libsql_wasi_wal_dbsize(libsql_wal* wal) {
|
|
return the_wal_methods->xDbsize(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_begin_write_transaction(libsql_wal* wal) {
|
|
return the_wal_methods->xBeginWriteTransaction(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_end_write_transaction(libsql_wal* wal) {
|
|
return the_wal_methods->xEndWriteTransaction(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_undo(libsql_wal* wal, int (*xUndo)(void*, unsigned int), void* pUndoCtx) {
|
|
return the_wal_methods->xUndo(wal, xUndo, pUndoCtx);
|
|
}
|
|
|
|
void libsql_wasi_wal_savepoint(libsql_wal* wal, unsigned int* aWalData) {
|
|
return the_wal_methods->xSavepoint(wal, aWalData);
|
|
}
|
|
|
|
int libsql_wasi_wal_savepoint_undo(libsql_wal* wal, unsigned int* aWalData) {
|
|
return the_wal_methods->xSavepointUndo(wal, aWalData);
|
|
}
|
|
|
|
int libsql_wasi_wal_frames(libsql_wal* wal, int n, libsql_pghdr* aPgHdr, unsigned int cksum, int mode, int readonly) {
|
|
return the_wal_methods->xFrames(wal, n, aPgHdr, cksum, mode, readonly, NULL);
|
|
}
|
|
|
|
int libsql_wasi_wal_checkpoint(libsql_wal* wal, sqlite3* db, int eMode, int (*xBusy)(void*), void* pBusyArg, int sync_flags, int nBuf, unsigned char* zBuf, int* pnLog, int* pnCkpt) {
|
|
return the_wal_methods->xCheckpoint(wal, db, eMode, xBusy, pBusyArg, sync_flags, nBuf, zBuf, pnLog, pnCkpt);
|
|
}
|
|
|
|
int libsql_wasi_wal_callback(libsql_wal* wal) {
|
|
return the_wal_methods->xCallback(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_exclusive_mode(libsql_wal* wal, int op) {
|
|
return the_wal_methods->xExclusiveMode(wal, op);
|
|
}
|
|
|
|
int libsql_wasi_wal_heap_memory(libsql_wal* wal) {
|
|
return the_wal_methods->xHeapMemory(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_snapshot_get(libsql_wal* wal, sqlite3_snapshot** snapshot) {
|
|
return the_wal_methods->xSnapshotGet(wal, snapshot);
|
|
}
|
|
|
|
void libsql_wasi_wal_snapshot_open(libsql_wal* wal, sqlite3_snapshot* snapshot) {
|
|
return the_wal_methods->xSnapshotOpen(wal, snapshot);
|
|
}
|
|
|
|
int libsql_wasi_wal_snapshot_recover(libsql_wal* wal) {
|
|
return the_wal_methods->xSnapshotRecover(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_snapshot_check(libsql_wal* wal, sqlite3_snapshot* snapshot) {
|
|
return the_wal_methods->xSnapshotCheck(wal, snapshot);
|
|
}
|
|
|
|
void libsql_wasi_wal_snapshot_unlock(libsql_wal* wal) {
|
|
return the_wal_methods->xSnapshotUnlock(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_framesize(libsql_wal* wal) {
|
|
return the_wal_methods->xFramesize(wal);
|
|
}
|
|
|
|
sqlite3_file *libsql_wasi_wal_file(libsql_wal* wal) {
|
|
return the_wal_methods->xFile(wal);
|
|
}
|
|
|
|
int libsql_wasi_wal_writelock(libsql_wal* wal, int bLock) {
|
|
return the_wal_methods->xWriteLock(wal, bLock);
|
|
}
|
|
|
|
void libsql_wasi_wal_db(libsql_wal* wal, sqlite3* db) {
|
|
return the_wal_methods->xDb(wal, db);
|
|
}
|
|
|
|
int libsql_wasi_wal_pathname_len(int orig_len) {
|
|
return the_wal_methods->xPathnameLen(orig_len);
|
|
}
|
|
|
|
void libsql_wasi_get_wal_pathname(char *buf, const char *orig, int len) {
|
|
return the_wal_methods->xGetWalPathname(buf, orig, len);
|
|
}
|
|
|
|
int libsql_wasi_wal_pre_main_db_open(libsql_wal_methods *methods, const char *path) {
|
|
return 0;
|
|
}
|
|
|
|
libsql_wal_methods libsql_wasi_wal_methods = {
|
|
.iVersion = 1,
|
|
.xOpen = &libsql_wasi_wal_open,
|
|
.xClose = &libsql_wasi_wal_close,
|
|
.xLimit = &libsql_wasi_wal_limit,
|
|
.xBeginReadTransaction = &libsql_wasi_wal_begin_read_transaction,
|
|
.xEndReadTransaction = &libsql_wasi_wal_end_read_transaction,
|
|
.xFindFrame = &libsql_wasi_wal_find_frame,
|
|
.xReadFrame = &libsql_wasi_wal_read_frame,
|
|
.xDbsize = &libsql_wasi_wal_dbsize,
|
|
.xBeginWriteTransaction = &libsql_wasi_wal_begin_write_transaction,
|
|
.xEndWriteTransaction = &libsql_wasi_wal_end_write_transaction,
|
|
.xUndo = &libsql_wasi_wal_undo,
|
|
.xSavepoint = &libsql_wasi_wal_savepoint,
|
|
.xSavepointUndo = &libsql_wasi_wal_savepoint_undo,
|
|
.xFrames = &libsql_wasi_wal_frames,
|
|
.xCheckpoint = &libsql_wasi_wal_checkpoint,
|
|
.xCallback = &libsql_wasi_wal_callback,
|
|
.xExclusiveMode = &libsql_wasi_wal_exclusive_mode,
|
|
.xHeapMemory = &libsql_wasi_wal_heap_memory,
|
|
.xSnapshotGet = &libsql_wasi_wal_snapshot_get,
|
|
.xSnapshotOpen = &libsql_wasi_wal_snapshot_open,
|
|
.xSnapshotRecover = &libsql_wasi_wal_snapshot_recover,
|
|
.xSnapshotCheck = &libsql_wasi_wal_snapshot_check,
|
|
.xSnapshotUnlock = &libsql_wasi_wal_snapshot_unlock,
|
|
.xFramesize = &libsql_wasi_wal_framesize,
|
|
.xFile = &libsql_wasi_wal_file,
|
|
.xWriteLock = &libsql_wasi_wal_writelock,
|
|
.xDb = &libsql_wasi_wal_db,
|
|
.xPathnameLen = &libsql_wasi_wal_pathname_len,
|
|
.xGetWalPathname = &libsql_wasi_get_wal_pathname,
|
|
.xPreMainDbOpen = &libsql_wasi_wal_pre_main_db_open,
|
|
.bUsesShm = 0,
|
|
.zName = "libsql_wasi",
|
|
.pNext = NULL,
|
|
};
|
|
|
|
void libsql_wasi_init() {
|
|
the_wal_methods = libsql_wal_methods_find(NULL);
|
|
sqlite3_vfs_register(&libsql_wasi_vfs, 1);
|
|
libsql_wal_methods_register(&libsql_wasi_wal_methods);
|
|
fprintf(stderr, "WASI initialized\n");
|
|
}
|
|
|
|
sqlite3 *libsql_wasi_open_db(const char *filename) {
|
|
sqlite3 *db;
|
|
fprintf(stderr, "opening database %s\n", filename);
|
|
int rc = libsql_open(filename, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "libsql_wasi", "libsql_wasi");
|
|
if (rc != SQLITE_OK) {
|
|
fprintf(stderr, "Failed to open database: %s\n", sqlite3_errmsg(db));
|
|
return NULL;
|
|
}
|
|
fprintf(stderr, "opened database %s\n", filename);
|
|
rc = sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
|
|
if (rc != SQLITE_OK) {
|
|
fprintf(stderr, "Failed to set journal mode: %s\n", sqlite3_errmsg(db));
|
|
return NULL;
|
|
}
|
|
return db;
|
|
}
|
|
|
|
int libsql_wasi_exec(sqlite3 *db, const char *sql) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
if (rc != SQLITE_OK) {
|
|
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
|
return rc;
|
|
}
|
|
// Step in a loop until SQLITE_DONE or error
|
|
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {}
|
|
if (rc != SQLITE_DONE) {
|
|
fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
|
|
return rc;
|
|
}
|
|
return SQLITE_OK;
|
|
} |