diff --git a/conn.go b/conn.go index 0b6b3fa..ed45ade 100644 --- a/conn.go +++ b/conn.go @@ -40,7 +40,7 @@ func OpenFlags(filename string, flags OpenFlag) (conn *Conn, err error) { c := newConn(module) c.ctx = context.Background() namePtr := c.newString(filename) - connPtr := c.new(ptrSize) + connPtr := c.new(wordSize) defer c.free(namePtr) defer c.free(connPtr) @@ -86,8 +86,8 @@ func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) { func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) { sqlPtr := c.newString(sql) - stmtPtr := c.new(ptrSize) - tailPtr := c.new(ptrSize) + stmtPtr := c.new(wordSize) + tailPtr := c.new(wordSize) defer c.free(sqlPtr) defer c.free(stmtPtr) defer c.free(tailPtr) @@ -218,4 +218,4 @@ func getString(memory api.Memory, ptr, maxlen uint32) string { } } -const ptrSize = 4 +const wordSize = 4 diff --git a/embed/build.sh b/embed/build.sh index 647c9d8..70d0b3a 100755 --- a/embed/build.sh +++ b/embed/build.sh @@ -12,22 +12,7 @@ zig cc --target=wasm32-wasi -flto -g0 -O2 \ -mmutable-globals \ -mbulk-memory -mreference-types \ -mnontrapping-fptoint -msign-ext \ - -DSQLITE_OS_OTHER=1 -DSQLITE_BYTEORDER=1234 \ - -DHAVE_ISNAN -DHAVE_USLEEP -DHAVE_MALLOC_USABLE_SIZE \ - -DSQLITE_DQS=0 \ - -DSQLITE_THREADSAFE=0 \ - -DSQLITE_DEFAULT_MEMSTATUS=0 \ - -DSQLITE_DEFAULT_LOCKING_MODE=1 \ - -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \ - -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ - -DSQLITE_MAX_EXPR_DEPTH=0 \ - -DSQLITE_OMIT_DECLTYPE \ - -DSQLITE_OMIT_DEPRECATED \ - -DSQLITE_OMIT_PROGRESS_CALLBACK \ - -DSQLITE_OMIT_SHARED_CACHE \ - -DSQLITE_OMIT_AUTOINIT \ - -DSQLITE_OMIT_UTF16 \ - -DSQLITE_USE_ALLOCA \ + -D_HAVE_SQLITE_CONFIG_H \ -Wl,--export=malloc \ -Wl,--export=free \ -Wl,--export=malloc_destructor \ diff --git a/sqlite3/main.c b/sqlite3/main.c index 2f6d24c..b2cf92f 100644 --- a/sqlite3/main.c +++ b/sqlite3/main.c @@ -1,4 +1,5 @@ #include +#include #include "sqlite3.h" @@ -7,6 +8,8 @@ int main() { if (rc != SQLITE_OK) return 1; } +int go_localtime(sqlite3_int64, struct tm *); + int go_randomness(sqlite3_vfs *, int nByte, char *zOut); int go_sleep(sqlite3_vfs *, int microseconds); int go_current_time(sqlite3_vfs *, double *); @@ -48,6 +51,10 @@ static int no_file_control(sqlite3_file *pFile, int op, void *pArg) { static int no_sector_size(sqlite3_file *pFile) { return 0; } static int no_device_characteristics(sqlite3_file *pFile) { return 0; } +int localtime_s(struct tm *const pTm, time_t const *const pTime) { + return go_localtime((sqlite3_int64)*pTime, pTm); +} + static int go_open_c(sqlite3_vfs *vfs, sqlite3_filename zName, sqlite3_file *file, int flags, int *pOutFlags) { static const sqlite3_io_methods go_io = { diff --git a/sqlite3/sqlite_cfg.h b/sqlite3/sqlite_cfg.h new file mode 100644 index 0000000..e558235 --- /dev/null +++ b/sqlite3/sqlite_cfg.h @@ -0,0 +1,36 @@ +#include + +// Platform Configuration + +#define SQLITE_OS_OTHER 1 +#define SQLITE_BYTEORDER 1234 + +#define HAVE_ISNAN 1 +#define HAVE_USLEEP 1 +#define HAVE_LOCALTIME_S 1 +#define HAVE_MALLOC_USABLE_SIZE 1 + +// Recommended Options + +#define SQLITE_DQS 0 +#define SQLITE_THREADSAFE 0 +#define SQLITE_DEFAULT_MEMSTATUS 0 +#define SQLITE_DEFAULT_WAL_SYNCHRONOUS 1 +#define SQLITE_LIKE_DOESNT_MATCH_BLOBS +#define SQLITE_MAX_EXPR_DEPTH 0 +#define SQLITE_OMIT_DECLTYPE +#define SQLITE_OMIT_DEPRECATED +#define SQLITE_OMIT_PROGRESS_CALLBACK +#define SQLITE_OMIT_SHARED_CACHE +#define SQLITE_OMIT_AUTOINIT +#define SQLITE_USE_ALLOCA + +// Need this to access WAL databases without the use of shared memory. +#define SQLITE_DEFAULT_LOCKING_MODE 1 +// Go uses UTF-8 everywhere. +#define SQLITE_OMIT_UTF16 +// Remove some testing code. +#define SQLITE_UNTESTABLE + +// Implemented in Go. +int localtime_s(struct tm *const pTm, time_t const *const pTime); \ No newline at end of file diff --git a/vfs.go b/vfs.go index cdbe4f7..a3a6582 100644 --- a/vfs.go +++ b/vfs.go @@ -26,6 +26,7 @@ func vfsInstantiate(ctx context.Context, r wazero.Runtime) (err error) { } env := r.NewHostModuleBuilder("env") + env.NewFunctionBuilder().WithFunc(vfsLocaltime).Export("go_localtime") env.NewFunctionBuilder().WithFunc(vfsRandomness).Export("go_randomness") env.NewFunctionBuilder().WithFunc(vfsSleep).Export("go_sleep") env.NewFunctionBuilder().WithFunc(vfsCurrentTime).Export("go_current_time") @@ -54,6 +55,29 @@ func vfsExit(ctx context.Context, mod api.Module, exitCode uint32) { panic(sys.NewExitError(mod.Name(), exitCode)) } +func vfsLocaltime(ctx context.Context, mod api.Module, t uint64, pTm uint32) uint32 { + tm := time.Unix(int64(t), 0) + var isdst int + if tm.IsDST() { + isdst = 1 + } + + // https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html + if mem := mod.Memory(); true && + mem.WriteUint32Le(pTm+0*wordSize, uint32(tm.Second())) && + mem.WriteUint32Le(pTm+1*wordSize, uint32(tm.Minute())) && + mem.WriteUint32Le(pTm+2*wordSize, uint32(tm.Hour())) && + mem.WriteUint32Le(pTm+3*wordSize, uint32(tm.Day())) && + mem.WriteUint32Le(pTm+4*wordSize, uint32(tm.Month()-time.January)) && + mem.WriteUint32Le(pTm+5*wordSize, uint32(tm.Year()-1900)) && + mem.WriteUint32Le(pTm+6*wordSize, uint32(tm.Weekday()-time.Sunday)) && + mem.WriteUint32Le(pTm+7*wordSize, uint32(tm.YearDay()-1)) && + mem.WriteUint32Le(pTm+8*wordSize, uint32(isdst)) { + return _OK + } + panic(rangeErr) +} + func vfsRandomness(ctx context.Context, mod api.Module, pVfs, nByte, zByte uint32) uint32 { mem, ok := mod.Memory().Read(zByte, nByte) if !ok { diff --git a/vfs_files.go b/vfs_files.go index 656fd89..8b96c18 100644 --- a/vfs_files.go +++ b/vfs_files.go @@ -92,7 +92,7 @@ func (p vfsFilePtr) OSFile() *os.File { } func (p vfsFilePtr) ID() uint32 { - id, ok := p.Memory().ReadUint32Le(p.ptr + ptrSize) + id, ok := p.Memory().ReadUint32Le(p.ptr + wordSize) if !ok { panic(rangeErr) } @@ -100,7 +100,7 @@ func (p vfsFilePtr) ID() uint32 { } func (p vfsFilePtr) Lock() uint32 { - lk, ok := p.Memory().ReadUint32Le(p.ptr + 2*ptrSize) + lk, ok := p.Memory().ReadUint32Le(p.ptr + 2*wordSize) if !ok { panic(rangeErr) } @@ -108,14 +108,14 @@ func (p vfsFilePtr) Lock() uint32 { } func (p vfsFilePtr) SetID(id uint32) vfsFilePtr { - if ok := p.Memory().WriteUint32Le(p.ptr+ptrSize, id); !ok { + if ok := p.Memory().WriteUint32Le(p.ptr+wordSize, id); !ok { panic(rangeErr) } return p } func (p vfsFilePtr) SetLock(lock uint32) vfsFilePtr { - if ok := p.Memory().WriteUint32Le(p.ptr+2*ptrSize, lock); !ok { + if ok := p.Memory().WriteUint32Le(p.ptr+2*wordSize, lock); !ok { panic(rangeErr) } return p