Skip to main content
Deno 2 is finally here 🎉️
Learn more
Module

x/sqlite/build/src/vfs.c

Deno SQLite module
Go to Latest
File
#include <stdlib.h>#include <stdint.h>#include <string.h>#include <time.h>#include <sqlite3.h>#include <pcg.h>#include "imports.h"#include "debug.h"
// SQLite VFS component.// Based on demoVFS from SQLlite.// https://www.sqlite.org/src/doc/trunk/src/test_demovfs.c
#define MAXPATHNAME 1024#define JS_MAX_SAFE_INTEGER 9007199254740991
// When using this VFS, the sqlite3_file* handles that SQLite uses are// actually pointers to instances of type DenoFile.typedef struct DenoFile DenoFile;struct DenoFile { sqlite3_file base; // Deno file resource id int rid;};
static int denoClose(sqlite3_file *pFile) { DenoFile* p = (DenoFile*)pFile; js_close(p->rid); debug_printf("closing file (rid %i)\n", p->rid); return SQLITE_OK;}
// Read data from a file.static int denoRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst) { DenoFile *p = (DenoFile*)pFile; int read_bytes = 0; if (iOfst <= JS_MAX_SAFE_INTEGER) { // Read bytes from buffer read_bytes = js_read(p->rid, (char*)zBuf, (double)iOfst, iAmt); debug_printf("attempt to read from file (rid %i, amount %i, offset %lli, read %i)\n", p->rid, iAmt, iOfst, read_bytes); } else { debug_printf("read offset %lli overflows JS_MAX_SAFE_INTEGER\n", iOfst); } // Zero memory if read was short if (read_bytes < iAmt) memset(&((char*)zBuf)[read_bytes], 0, iAmt-read_bytes); return read_bytes < iAmt ? SQLITE_IOERR_SHORT_READ : SQLITE_OK;}
// Write data to a file.static int denoWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst) { DenoFile *p = (DenoFile*)pFile; int write_bytes = 0; if (iOfst <= JS_MAX_SAFE_INTEGER) { // Write bytes to buffer write_bytes = js_write(p->rid, (char*)zBuf, (double)iOfst, iAmt); debug_printf("attempt to write to file (rid %i, amount %i, offset %lli, written %i)\n", p->rid, iAmt, iOfst, write_bytes); } else { debug_printf("write offset %lli overflows JS_MAX_SAFE_INTEGER\n", iOfst); } return write_bytes == iAmt ? SQLITE_OK : SQLITE_IOERR_WRITE;}
// Truncate file.static int denoTruncate(sqlite3_file *pFile, sqlite_int64 size) { DenoFile *p = (DenoFile*)pFile; if (size <= JS_MAX_SAFE_INTEGER) { js_truncate(p->rid, (double)size); debug_printf("truncating file (rid %i, size: %lli)\n", p->rid, size); return SQLITE_OK; } else { debug_printf("truncate length %lli overflows JS_MAX_SAFE_INTEGER\n", size); return SQLITE_IOERR; }}
// Deno provides no explicit sync for us, so we// just have a no-op here.// TODO(dyedgreen): Investigate if there is a better waystatic int denoSync(sqlite3_file *pFile, int flags) { DenoFile *p = (DenoFile*)pFile; js_sync(p->rid); debug_printf("syncing file (rid %i)\n", p->rid); return SQLITE_OK;}
// Write the size of the file in bytes to *pSize.static int denoFileSize(sqlite3_file *pFile, sqlite_int64 *pSize) { DenoFile *p = (DenoFile*)pFile; *pSize = (sqlite_int64)js_size(p->rid); debug_printf("read file size: %lli (rid %i)\n", *pSize, p->rid); return SQLITE_OK;}
// File lockingstatic int denoLock(sqlite3_file *pFile, int eLock) { DenoFile *p = (DenoFile*)pFile; switch (eLock) { case SQLITE_LOCK_NONE: // no op break; case SQLITE_LOCK_SHARED: case SQLITE_LOCK_RESERVED: // one WASM process <-> one open database js_lock(p->rid, 0); break; case SQLITE_LOCK_PENDING: case SQLITE_LOCK_EXCLUSIVE: js_lock(p->rid, 1); break; } return SQLITE_OK;}
static int denoUnlock(sqlite3_file *pFile, int eLock) { DenoFile *p = (DenoFile*)pFile; switch (eLock) { case SQLITE_LOCK_NONE: // no op break; case SQLITE_LOCK_SHARED: case SQLITE_LOCK_RESERVED: case SQLITE_LOCK_PENDING: case SQLITE_LOCK_EXCLUSIVE: js_unlock(p->rid); break; } return SQLITE_OK;}
static int denoCheckReservedLock(sqlite3_file *pFile, int *pResOut) { *pResOut = 0; return SQLITE_OK;}
// No xFileControl() verbs are implemented by this VFS.static int denoFileControl(sqlite3_file *pFile, int op, void *pArg) { return SQLITE_NOTFOUND;}
// TODO(dyedgreen): Should we try to get these?static int denoSectorSize(sqlite3_file *pFile) { return 0;}static int denoDeviceCharacteristics(sqlite3_file *pFile) { return 0;}
// Open a file handle.static int denoOpen( sqlite3_vfs *pVfs, /* VFS */ const char *zName, /* File to open, or 0 for a temp file */ sqlite3_file *pFile, /* Pointer to DenoFile struct to populate */ int flags, /* Input SQLITE_OPEN_XXX flags */ int *pOutFlags /* Output SQLITE_OPEN_XXX flags (or NULL) */) { static const sqlite3_io_methods denoio = { 1, /* iVersion */ denoClose, /* xClose */ denoRead, /* xRead */ denoWrite, /* xWrite */ denoTruncate, /* xTruncate */ denoSync, /* xSync */ denoFileSize, /* xFileSize */ denoLock, /* xLock */ denoUnlock, /* xUnlock */ denoCheckReservedLock, /* xCheckReservedLock */ denoFileControl, /* xFileControl */ denoSectorSize, /* xSectorSize */ denoDeviceCharacteristics /* xDeviceCharacteristics */ }; DenoFile *p = (DenoFile*)pFile; p->base.pMethods = &denoio; // TODO(dyedgreen): The current approach is to raise // the permission error on the vfs.js side of things, // should the error be propagates through the wrapper // and be raised on the wrapper side of things? p->rid = js_open(zName, zName ? 0 : 1, flags); if (pOutFlags) { *pOutFlags = flags; } debug_printf("opened file (rid %i)\n", p->rid); debug_printf("file path name: '%s'\n", zName); return SQLITE_OK;}
// Delete the file at the path.static int denoDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync) { js_delete(zPath); return SQLITE_OK;}
// All valid id files are accessible.static int denoAccess(sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut) { switch (flags) { case SQLITE_ACCESS_EXISTS: *pResOut = js_exists(zPath); break; default: *pResOut = js_access(zPath); break; } debug_printf("determining file access (path %s, access %i)\n", zPath, *pResOut); return SQLITE_OK;}
// TODO(dyedgreen): Actually resolve the full path namestatic int denoFullPathname(sqlite3_vfs *pVfs, const char *zPath, int nPathOut, char *zPathOut) { sqlite3_snprintf(nPathOut, zPathOut, "%s", zPath); debug_printf("requesting full path name for path: %s\n", zPath); return SQLITE_OK;}
// We don't support shared objectsstatic void *denoDlOpen(sqlite3_vfs *pVfs, const char *zPath) { return 0;}static void denoDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg) { sqlite3_snprintf(nByte, zErrMsg, "Loadable extensions are not supported"); zErrMsg[nByte-1] = '\0';}static void (*denoDlSym(sqlite3_vfs *pVfs, void *pH, const char *z))(void) { return 0;}static void denoDlClose(sqlite3_vfs *pVfs, void *pHandle) { return;}
// Generate pseudo-random datastatic int denoRandomness(sqlite3_vfs *pVfs, int nByte, char *zByte) { pcg_bytes(zByte, nByte); return SQLITE_OK;}
// TODO(dyedgreen): Can anything be done here?static int denoSleep(sqlite3_vfs *pVfs, int nMicro) { return 0;}
// Retrieve the current timestatic int denoCurrentTime(sqlite3_vfs *pVfs, double *pTime) { *pTime = js_time() / 1000 / 86400.0 + 2440587.5; return SQLITE_OK;}
// Implement localtime_rstruct tm* localtime_r(const time_t *time, struct tm *result) { debug_printf("running localtime_r"); time_t shifted = *time - 60 * js_timezone(); return gmtime_r(&shifted, result);}
// This function returns a pointer to the VFS implemented in this file.sqlite3_vfs *sqlite3_denovfs(void) { static sqlite3_vfs denovfs = { 3, /* iVersion */ sizeof(DenoFile), /* szOsFile */ MAXPATHNAME, /* mxPathname */ 0, /* pNext */ "deno", /* zName */ 0, /* pAppData */ denoOpen, /* xOpen */ denoDelete, /* xDelete */ denoAccess, /* xAccess */ denoFullPathname, /* xFullPathname */ denoDlOpen, /* xDlOpen */ denoDlError, /* xDlError */ denoDlSym, /* xDlSym */ denoDlClose, /* xDlClose */ denoRandomness, /* xRandomness */ denoSleep, /* xSleep */ denoCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ 0, /* xCurrentTimeInt64 */ 0, /* xSetSystemCall */ 0, /* xGetSystemCall */ 0, /* xNextSystemCall */ }; return &denovfs;}
int sqlite3_os_init(void) { debug_printf("running sqlite3_os_init\n"); // Register VFS return sqlite3_vfs_register(sqlite3_denovfs(), 1);}
int sqlite3_os_end(void) { return SQLITE_OK;}