├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── package-lock.json ├── package.json ├── src ├── c │ ├── config.h │ └── glue.c ├── exported_functions.json └── ts │ ├── connection.ts │ ├── error.ts │ ├── module.ts │ ├── sqlite-constants.ts │ ├── sqlite-functions.ts │ ├── sqlite-objects.ts │ └── type.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | deps/ 3 | temp/ 4 | ext/ 5 | debug/ 6 | dist/ 7 | node_modules/ 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | deps/ 3 | temp/ 4 | debug/ 5 | .git/ 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib", 3 | "C_Cpp.default.includePath": [ 4 | "${workspaceFolder}/src/c", 5 | "${workspaceFolder}/deps/sqlite-amalgamation-3300100" 6 | ] 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Ryusei Yamaguchi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # dependencies 2 | 3 | SQLITE_AMALGAMATION = sqlite-amalgamation-3330000 4 | SQLITE_AMALGAMATION_ZIP_URL = https://www.sqlite.org/2020/sqlite-amalgamation-3330000.zip 5 | SQLITE_AMALGAMATION_ZIP_SHA1 = 5b0a95fc6090499c0cdf7f15fcec9c132f8e021e 6 | 7 | EXTENSION_FUNCTIONS = extension-functions.c 8 | EXTENSION_FUNCTIONS_URL = https://www.sqlite.org/contrib/download/extension-functions.c?get=25 9 | EXTENSION_FUNCTIONS_SHA1 = c68fa706d6d9ff98608044c00212473f9c14892f 10 | 11 | # source files 12 | 13 | EXPORTED_FUNCTIONS_JSON = src/exported_functions.json 14 | 15 | # build options 16 | 17 | EMCC ?= emcc 18 | 19 | TSC ?= node_modules/typescript/bin/tsc 20 | 21 | EXTENSIONS = ext/extension-functions.wasm 22 | 23 | CFLAGS = \ 24 | -fPIC \ 25 | -D_HAVE_SQLITE_CONFIG_H \ 26 | -Isrc/c -I'deps/$(SQLITE_AMALGAMATION)' 27 | 28 | EMFLAGS = \ 29 | -s ALLOW_MEMORY_GROWTH=1 \ 30 | -s RESERVED_FUNCTION_POINTERS=64 \ 31 | -s WASM=1 32 | 33 | EMFLAGS_EXTENSION = \ 34 | -s INLINING_LIMIT=50 \ 35 | -Os 36 | 37 | EMFLAGS_DEBUG = \ 38 | -s INLINING_LIMIT=10 \ 39 | -O1 40 | 41 | EMFLAGS_DIST = \ 42 | -s INLINING_LIMIT=50 \ 43 | -Os 44 | 45 | EMFLAG_INTERFACES = \ 46 | -s MAIN_MODULE=1 \ 47 | -s EXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS_JSON) \ 48 | -s EXTRA_EXPORTED_RUNTIME_METHODS=[$(shell \ 49 | grep -Po '(?<=declare function )\w+' src/ts/module.ts | sed -e 's/\(.*\)/"\1"/;' | paste -s -d,)] \ 50 | --post-js temp/api.js 51 | 52 | # directories 53 | 54 | .PHONY: all 55 | all: dist 56 | 57 | .PHONY: clean 58 | clean: 59 | rm -rf dist debug ext temp 60 | 61 | .PHONY: clean-all 62 | clean-all: 63 | rm -rf dist debug ext temp deps cache 64 | 65 | ## cache 66 | 67 | .PHONY: clean-cache 68 | clean-cache: 69 | rm -rf cache 70 | 71 | cache/$(SQLITE_AMALGAMATION).zip: 72 | mkdir -p cache 73 | curl -LsSf '$(SQLITE_AMALGAMATION_ZIP_URL)' -o $@ 74 | 75 | cache/$(EXTENSION_FUNCTIONS): 76 | mkdir -p cache 77 | curl -LsSf '$(EXTENSION_FUNCTIONS_URL)' -o $@ 78 | 79 | ## deps 80 | 81 | .PHONY: clean-deps 82 | clean-deps: 83 | rm -rf deps 84 | 85 | .PHONY: deps 86 | deps: deps/$(SQLITE_AMALGAMATION) deps/$(EXTENSION_FUNCTIONS) deps/$(EXPORTED_FUNCTIONS) 87 | 88 | deps/$(SQLITE_AMALGAMATION): cache/$(SQLITE_AMALGAMATION).zip 89 | mkdir -p deps 90 | echo '$(SQLITE_AMALGAMATION_ZIP_SHA1)' 'cache/$(SQLITE_AMALGAMATION).zip' | sha1sum -c 91 | rm -rf $@ 92 | unzip 'cache/$(SQLITE_AMALGAMATION).zip' -d deps/ 93 | touch $@ 94 | 95 | deps/$(EXTENSION_FUNCTIONS): cache/$(EXTENSION_FUNCTIONS) 96 | mkdir -p deps 97 | echo '$(EXTENSION_FUNCTIONS_SHA1)' 'cache/$(EXTENSION_FUNCTIONS)' | sha1sum -c 98 | cp 'cache/$(EXTENSION_FUNCTIONS)' $@ 99 | 100 | ## temp 101 | 102 | .PHONY: clean-temp 103 | clean-temp: 104 | rm -rf temp 105 | 106 | temp/bc/shell.bc: deps/$(SQLITE_AMALGAMATION) src/c/config.h 107 | mkdir -p temp/bc 108 | $(EMCC) $(CFLAGS) 'deps/$(SQLITE_AMALGAMATION)/shell.c' -c -o $@ 109 | 110 | temp/bc/sqlite3.bc: deps/$(SQLITE_AMALGAMATION) src/c/config.h 111 | mkdir -p temp/bc 112 | $(EMCC) $(CFLAGS) -s LINKABLE=1 'deps/$(SQLITE_AMALGAMATION)/sqlite3.c' -c -o $@ 113 | 114 | temp/bc/glue.bc: src/c/glue.c 115 | mkdir -p temp/bc 116 | $(EMCC) $(CFLAGS) -s LINKABLE=1 src/c/glue.c -c -o $@ 117 | 118 | temp/bc/extension-functions.bc: deps/$(EXTENSION_FUNCTIONS) 119 | mkdir -p temp/bc 120 | $(EMCC) $(CFLAGS) -s LINKABLE=1 'deps/$(EXTENSION_FUNCTIONS)' -c -o $@ 121 | 122 | temp/api.js: $(wildcard src/ts/*) 123 | $(TSC) 124 | 125 | ## extensions 126 | 127 | .PHONY: clean-ext 128 | clean-ext: 129 | rm -rf ext 130 | 131 | .PHONY: ext 132 | ext: $(EXTENSIONS) 133 | 134 | ext/extension-functions.wasm: temp/bc/extension-functions.bc 135 | mkdir -p ext 136 | $(EMCC) $(EMFLAGS) -s SIDE_MODULE=1 -s EXPORTED_FUNCTIONS='["_sqlite3_extension_init"]' $(EMFLAGS_EXTENSION) \ 137 | temp/bc/extension-functions.bc -o $@ 138 | 139 | ## debug 140 | .PHONY: clean-debug 141 | clean-debug: 142 | rm -rf debug 143 | 144 | .PHONY: debug 145 | debug: debug/sqlite3.html 146 | 147 | debug/sqlite3.html: temp/bc/sqlite3.bc temp/bc/glue.bc $(EXTENSIONS) $(EXPORTED_FUNCTIONS_JSON) temp/api.js 148 | mkdir -p debug 149 | $(EMCC) $(EMFLAGS) $(EMFLAG_INTERFACES) $(EMFLAGS_DEBUG) \ 150 | --no-heap-copy --embed-file ext \ 151 | temp/bc/sqlite3.bc temp/bc/glue.bc -o $@ 152 | 153 | ## dist 154 | 155 | .PHONY: clean-dist 156 | clean-dist: 157 | rm -rf dist 158 | 159 | .PHONY: dist 160 | dist: dist/sqlite3.html 161 | 162 | dist/sqlite3.html: temp/bc/sqlite3.bc temp/bc/glue.bc $(EXTENSIONS) $(EXPORTED_FUNCTIONS_JSON) temp/api.js 163 | mkdir -p dist 164 | $(EMCC) $(EMFLAGS) $(EMFLAG_INTERFACES) $(EMFLAGS_DIST) \ 165 | --no-heap-copy --embed-file ext \ 166 | temp/bc/sqlite3.bc temp/bc/glue.bc -o $@ 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLite compiled to WebAssembly 2 | 3 | ## dependencies 4 | 5 | ### manual install required 6 | 7 | - npm 8 | - make 9 | - the latest version of emscripten 10 | - https://emscripten.org/docs/getting_started/downloads.html 11 | 12 | ### installed by commands 13 | 14 | - typescript (run `npm install`) 15 | - source code of sqlite (run `make deps`) 16 | 17 | ## build 18 | 19 | ```sh 20 | make 21 | ``` 22 | 23 | ## Related 24 | 25 | - [SQLite compiled to javascript](https://github.com/kripken/sql.js/) 26 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sqlite-wasm", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "typescript": { 8 | "version": "3.1.3", 9 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.3.tgz", 10 | "integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA==", 11 | "dev": true 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sqlite-wasm", 3 | "version": "0.1.0", 4 | "description": "SQLite compiled to WebAssembly", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "make", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Ryusei Yamaguchi", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "typescript": "^3.1.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/c/config.h: -------------------------------------------------------------------------------- 1 | #define LONGDOUBLE_TYPE double 2 | #define SQLITE_BYTEORDER 1234 3 | #define SQLITE_DISABLE_LFS 1 4 | #define SQLITE_ENABLE_JSON1 1 5 | #define SQLITE_HAVE_ISNAN 1 6 | #define SQLITE_HAVE_MALLOC_USABLE_SIZE 1 7 | #define SQLITE_HAVE_STRCHRNUL 1 8 | #define SQLITE_LIKE_DOESNT_MATCH_BLOBS 1 9 | #define SQLITE_OMIT_DECLTYPE 1 10 | #define SQLITE_OMIT_DEPRECATED 1 11 | #define SQLITE_TEMP_STORE 2 12 | #define SQLITE_THREADSAFE 0 13 | #define SQLITE_USE_URI 1 14 | -------------------------------------------------------------------------------- /src/c/glue.c: -------------------------------------------------------------------------------- 1 | #include 2 | int glue_sqlite3_db_config_int_pint(sqlite3 *db, int op, int value, int *ret_val) 3 | { 4 | return sqlite3_db_config(db, op, value, ret_val); 5 | } 6 | -------------------------------------------------------------------------------- /src/exported_functions.json: -------------------------------------------------------------------------------- 1 | [ 2 | "_malloc", 3 | "_free", 4 | "_sqlite3_aggregate_context", 5 | "_sqlite3_auto_extension", 6 | "_sqlite3_backup_finish", 7 | "_sqlite3_backup_init", 8 | "_sqlite3_backup_pagecount", 9 | "_sqlite3_backup_remaining", 10 | "_sqlite3_backup_step", 11 | "_sqlite3_bind_blob", 12 | "_sqlite3_bind_blob64", 13 | "_sqlite3_bind_double", 14 | "_sqlite3_bind_int", 15 | "_sqlite3_bind_int64", 16 | "_sqlite3_bind_null", 17 | "_sqlite3_bind_parameter_count", 18 | "_sqlite3_bind_parameter_index", 19 | "_sqlite3_bind_parameter_name", 20 | "_sqlite3_bind_pointer", 21 | "_sqlite3_bind_text", 22 | "_sqlite3_bind_text16", 23 | "_sqlite3_bind_text64", 24 | "_sqlite3_bind_value", 25 | "_sqlite3_bind_zeroblob", 26 | "_sqlite3_bind_zeroblob64", 27 | "_sqlite3_blob_bytes", 28 | "_sqlite3_blob_close", 29 | "_sqlite3_blob_open", 30 | "_sqlite3_blob_read", 31 | "_sqlite3_blob_reopen", 32 | "_sqlite3_blob_write", 33 | "_sqlite3_busy_handler", 34 | "_sqlite3_busy_timeout", 35 | "_sqlite3_cancel_auto_extension", 36 | "_sqlite3_changes", 37 | "_sqlite3_clear_bindings", 38 | "_sqlite3_close", 39 | "_sqlite3_close_v2", 40 | "_sqlite3_collation_needed", 41 | "_sqlite3_collation_needed16", 42 | "_sqlite3_column_blob", 43 | "_sqlite3_column_bytes", 44 | "_sqlite3_column_bytes16", 45 | "_sqlite3_column_count", 46 | "_sqlite3_column_double", 47 | "_sqlite3_column_int", 48 | "_sqlite3_column_int64", 49 | "_sqlite3_column_name", 50 | "_sqlite3_column_name16", 51 | "_sqlite3_column_text", 52 | "_sqlite3_column_text16", 53 | "_sqlite3_column_type", 54 | "_sqlite3_column_value", 55 | "_sqlite3_commit_hook", 56 | "_sqlite3_compileoption_get", 57 | "_sqlite3_compileoption_used", 58 | "_sqlite3_complete", 59 | "_sqlite3_complete16", 60 | "_sqlite3_config", 61 | "_sqlite3_context_db_handle", 62 | "_sqlite3_create_collation", 63 | "_sqlite3_create_collation16", 64 | "_sqlite3_create_collation_v2", 65 | "_sqlite3_create_function", 66 | "_sqlite3_create_function16", 67 | "_sqlite3_create_function_v2", 68 | "_sqlite3_create_module", 69 | "_sqlite3_create_module_v2", 70 | "_sqlite3_create_window_function", 71 | "_sqlite3_data_count", 72 | "_sqlite3_db_cacheflush", 73 | "_glue_sqlite3_db_config_int_pint", 74 | "_sqlite3_db_filename", 75 | "_sqlite3_db_handle", 76 | "_sqlite3_db_mutex", 77 | "_sqlite3_db_readonly", 78 | "_sqlite3_db_release_memory", 79 | "_sqlite3_db_status", 80 | "_sqlite3_declare_vtab", 81 | "_sqlite3_drop_modules", 82 | "_sqlite3_enable_load_extension", 83 | "_sqlite3_enable_shared_cache", 84 | "_sqlite3_errcode", 85 | "_sqlite3_errmsg", 86 | "_sqlite3_errmsg16", 87 | "_sqlite3_errstr", 88 | "_sqlite3_exec", 89 | "_sqlite3_expanded_sql", 90 | "_sqlite3_extended_errcode", 91 | "_sqlite3_extended_result_codes", 92 | "_sqlite3_file_control", 93 | "_sqlite3_finalize", 94 | "_sqlite3_free", 95 | "_sqlite3_free_table", 96 | "_sqlite3_get_autocommit", 97 | "_sqlite3_get_auxdata", 98 | "_sqlite3_get_table", 99 | "_sqlite3_initialize", 100 | "_sqlite3_interrupt", 101 | "_sqlite3_keyword_check", 102 | "_sqlite3_keyword_count", 103 | "_sqlite3_keyword_name", 104 | "_sqlite3_last_insert_rowid", 105 | "_sqlite3_libversion", 106 | "_sqlite3_libversion_number", 107 | "_sqlite3_limit", 108 | "_sqlite3_load_extension", 109 | "_sqlite3_log", 110 | "_sqlite3_malloc", 111 | "_sqlite3_malloc64", 112 | "_sqlite3_memory_highwater", 113 | "_sqlite3_memory_used", 114 | "_sqlite3_mprintf", 115 | "_sqlite3_msize", 116 | "_sqlite3_next_stmt", 117 | "_sqlite3_open", 118 | "_sqlite3_open16", 119 | "_sqlite3_open_v2", 120 | "_sqlite3_os_end", 121 | "_sqlite3_os_init", 122 | "_sqlite3_overload_function", 123 | "_sqlite3_prepare", 124 | "_sqlite3_prepare16", 125 | "_sqlite3_prepare16_v2", 126 | "_sqlite3_prepare16_v3", 127 | "_sqlite3_prepare_v2", 128 | "_sqlite3_prepare_v3", 129 | "_sqlite3_progress_handler", 130 | "_sqlite3_randomness", 131 | "_sqlite3_realloc", 132 | "_sqlite3_realloc64", 133 | "_sqlite3_release_memory", 134 | "_sqlite3_reset", 135 | "_sqlite3_reset_auto_extension", 136 | "_sqlite3_result_blob", 137 | "_sqlite3_result_blob64", 138 | "_sqlite3_result_double", 139 | "_sqlite3_result_error", 140 | "_sqlite3_result_error16", 141 | "_sqlite3_result_error_code", 142 | "_sqlite3_result_error_nomem", 143 | "_sqlite3_result_error_toobig", 144 | "_sqlite3_result_int", 145 | "_sqlite3_result_int64", 146 | "_sqlite3_result_null", 147 | "_sqlite3_result_pointer", 148 | "_sqlite3_result_subtype", 149 | "_sqlite3_result_text", 150 | "_sqlite3_result_text16", 151 | "_sqlite3_result_text16be", 152 | "_sqlite3_result_text16le", 153 | "_sqlite3_result_text64", 154 | "_sqlite3_result_value", 155 | "_sqlite3_result_zeroblob", 156 | "_sqlite3_result_zeroblob64", 157 | "_sqlite3_rollback_hook", 158 | "_sqlite3_set_authorizer", 159 | "_sqlite3_set_auxdata", 160 | "_sqlite3_set_last_insert_rowid", 161 | "_sqlite3_shutdown", 162 | "_sqlite3_sleep", 163 | "_sqlite3_snprintf", 164 | "_sqlite3_soft_heap_limit64", 165 | "_sqlite3_sourceid", 166 | "_sqlite3_sql", 167 | "_sqlite3_status", 168 | "_sqlite3_status64", 169 | "_sqlite3_step", 170 | "_sqlite3_stmt_busy", 171 | "_sqlite3_stmt_isexplain", 172 | "_sqlite3_stmt_readonly", 173 | "_sqlite3_stmt_status", 174 | "_sqlite3_str_append", 175 | "_sqlite3_str_appendall", 176 | "_sqlite3_str_appendchar", 177 | "_sqlite3_str_appendf", 178 | "_sqlite3_str_errcode", 179 | "_sqlite3_str_finish", 180 | "_sqlite3_str_length", 181 | "_sqlite3_str_new", 182 | "_sqlite3_str_reset", 183 | "_sqlite3_str_value", 184 | "_sqlite3_str_vappendf", 185 | "_sqlite3_strglob", 186 | "_sqlite3_stricmp", 187 | "_sqlite3_strlike", 188 | "_sqlite3_strnicmp", 189 | "_sqlite3_system_errno", 190 | "_sqlite3_table_column_metadata", 191 | "_sqlite3_test_control", 192 | "_sqlite3_threadsafe", 193 | "_sqlite3_total_changes", 194 | "_sqlite3_update_hook", 195 | "_sqlite3_uri_boolean", 196 | "_sqlite3_uri_int64", 197 | "_sqlite3_uri_parameter", 198 | "_sqlite3_user_data", 199 | "_sqlite3_value_blob", 200 | "_sqlite3_value_bytes", 201 | "_sqlite3_value_bytes16", 202 | "_sqlite3_value_double", 203 | "_sqlite3_value_dup", 204 | "_sqlite3_value_free", 205 | "_sqlite3_value_frombind", 206 | "_sqlite3_value_int", 207 | "_sqlite3_value_int64", 208 | "_sqlite3_value_nochange", 209 | "_sqlite3_value_numeric_type", 210 | "_sqlite3_value_pointer", 211 | "_sqlite3_value_subtype", 212 | "_sqlite3_value_text", 213 | "_sqlite3_value_text16", 214 | "_sqlite3_value_text16be", 215 | "_sqlite3_value_text16le", 216 | "_sqlite3_value_type", 217 | "_sqlite3_vfs_find", 218 | "_sqlite3_vfs_register", 219 | "_sqlite3_vfs_unregister", 220 | "_sqlite3_vmprintf", 221 | "_sqlite3_vsnprintf", 222 | "_sqlite3_vtab_collation", 223 | "_sqlite3_vtab_config", 224 | "_sqlite3_vtab_nochange", 225 | "_sqlite3_vtab_on_conflict", 226 | "_sqlite3_wal_autocheckpoint", 227 | "_sqlite3_wal_checkpoint", 228 | "_sqlite3_wal_checkpoint_v2", 229 | "_sqlite3_wal_hook" 230 | ] -------------------------------------------------------------------------------- /src/ts/connection.ts: -------------------------------------------------------------------------------- 1 | namespace Module { 2 | interface ConnectionOptions { 3 | "vfs"?: string 4 | "mode"?: "ro" | "rw" | "rwc" | "memory" 5 | "cache"?: "shared" | "private" 6 | "psow"?: boolean 7 | "nolock"?: boolean 8 | "immutable"?: boolean 9 | } 10 | 11 | export class Connection { 12 | private pDb: ptr 13 | public readonly "uri": string 14 | constructor(public readonly filename: string, options: ConnectionOptions = {}) { 15 | const opts = [] 16 | const vfs = options["vfs"] 17 | if (vfs != null) { opts.push(`vfs=${encodeURIComponent(vfs)}`) } 18 | const mode = options["mode"] 19 | if (mode != null) { opts.push(`mode=${encodeURIComponent(mode)}`) } 20 | const cache = options["cache"] 21 | if (cache != null) { opts.push(`cache=${encodeURIComponent(cache)}`) } 22 | const psow = options["psow"] 23 | if (psow != null) { opts.push(`psow=${Number(psow)}`) } 24 | const nolock = options["nolock"] 25 | if (nolock != null) { opts.push(`nolock=${Number(nolock)}`) } 26 | const immutable = options["immutable"] 27 | if (immutable != null) { opts.push(`nolock=${Number(immutable)}`) } 28 | const q = opts.join("&") 29 | const uri = this["uri"] = `file:${encodeURI(filename)}${q ? "?" + q : ""}` 30 | 31 | const { result: code, pDb } = sqlite3_open(uri) 32 | 33 | if (code) { throw new SQLiteError(code) } 34 | 35 | this.pDb = pDb as ptr 36 | } 37 | 38 | close(): void { 39 | const code = sqlite3_close_v2(this.pDb) 40 | if (code) { throw new SQLiteError(code) } 41 | delete this.pDb 42 | } 43 | 44 | exec( 45 | sql: string, 46 | callback?: ((columns: { [k in string]: string }) => T | undefined), 47 | ): T | undefined { 48 | let value: T | undefined = undefined 49 | let reason: any = undefined 50 | let columnNames: string[] | undefined = undefined 51 | function ptrToStringArray(p: ptr>>, length: number) { 52 | const texts = [] 53 | for (let i: number = p; i < p + length * 4; i += 4) { 54 | const text = UTF8ToString(getValue>(i as ptr>, "*")) 55 | texts.push(text) 56 | } 57 | return texts 58 | } 59 | const { result, errmsg } = sqlite3_exec(this.pDb, sql, callback == null ? undefined : (numColumns, pColumnTexts, pColumnNames) => { 60 | const columnTexts = ptrToStringArray(pColumnTexts, numColumns) 61 | if (columnNames == null) columnNames = ptrToStringArray(pColumnNames, numColumns) 62 | const record: Record = Object.create(null) 63 | for (let i = 0; i < numColumns; i++) { 64 | record[columnNames[i]] = columnTexts[i] 65 | } 66 | try { 67 | value = callback(record) 68 | return value !== undefined 69 | } catch (error) { 70 | reason = error 71 | return true 72 | } 73 | }) 74 | 75 | if (value !== undefined) { 76 | return value 77 | } 78 | if (reason !== undefined) { 79 | throw reason 80 | } 81 | if (result) { 82 | throw new SQLiteError(result, errmsg || undefined) 83 | } 84 | } 85 | 86 | enableLoadExtension() { 87 | const { result, value } = sqlite3_db_config(this.pDb, SQLiteDbConfig.ENABLE_LOAD_EXTENSION, 1) 88 | if (result) { throw new SQLiteError(result) } 89 | return value 90 | } 91 | 92 | disableLoadExtension() { 93 | const { result, value } = sqlite3_db_config(this.pDb, SQLiteDbConfig.ENABLE_LOAD_EXTENSION, 0) 94 | if (result) { throw new SQLiteError(result) } 95 | return value 96 | } 97 | 98 | loadExtension(file: string, entry?: string) { 99 | const { result, errmsg } = sqlite3_load_extension(this.pDb, file, entry) 100 | if (result) { throw new SQLiteError(result, errmsg || undefined) } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/ts/error.ts: -------------------------------------------------------------------------------- 1 | namespace Module { 2 | export class SQLiteError extends Error { 3 | public readonly "code": SQLiteResult 4 | constructor(code: SQLiteResult, message?: string) { 5 | super(message || `SQLITE_${SQLiteResult[code]}`) 6 | Object.defineProperty(this, "code", { value: code }) 7 | Object.defineProperty(this, "name", { value: `SQLITE_${SQLiteResult[code]}` }) 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/ts/module.ts: -------------------------------------------------------------------------------- 1 | namespace Module { 2 | type NativeJsTypeSignature = 3 | | "undefined" 4 | | "boolean" 5 | | "string" 6 | | "number" 7 | | "array" 8 | 9 | type NativeJsTypeOf = 10 | S extends NativeJsTypeSignature ? { 11 | "undefined": undefined 12 | "boolean": boolean 13 | "string": string | null | undefined 14 | "number": number 15 | "array": Int8Array | i8[] | null | undefined 16 | }[S] 17 | : never 18 | 19 | export declare function cwrap< 20 | ReturnType extends Exclude, 21 | ArgTypes extends Array>( 22 | ident: string, 23 | returnType: ReturnType, 24 | argTypes: ArgTypes): (...args: { [i in keyof ArgTypes]: NativeJsTypeOf }) => NativeJsTypeOf 25 | 26 | export declare function ccall< 27 | ReturnType extends Exclude, 28 | ArgTypes extends Array>( 29 | ident: string, 30 | returnType: ReturnType, 31 | argTypes: ArgTypes, 32 | args: { [i in keyof ArgTypes]: NativeJsTypeOf }): NativeJsTypeOf 33 | 34 | export declare function getValue>(ptr: ptr, type: "*", noSafe?: boolean): T; 35 | export declare function getValue(ptr: ptr, type: "i32", noSafe?: boolean): i32; 36 | export declare function getValue(ptr: ptr, type: "double", noSafe?: boolean): number; 37 | 38 | export declare function UTF8ToString(ptr: ptr): string 39 | export declare function stringToUTF8(str: string, outPtr: ptr, maxBytesToWrite: number): void 40 | 41 | export declare function stackSave(): stack 42 | export declare function stackRestore(stack: stack): void 43 | export declare function stackAlloc(size: number & T["__size__"]): ptr 44 | export declare function addFunction(func: F, sig: string): ptr> 45 | export declare function removeFunction(ptr: ptr>): void 46 | } -------------------------------------------------------------------------------- /src/ts/sqlite-constants.ts: -------------------------------------------------------------------------------- 1 | namespace Module { 2 | export enum SQLiteResult { 3 | /** Successful result */ 4 | OK = 0, 5 | 6 | /** Generic error */ 7 | ERROR = 1, 8 | /** Internal logic error in SQLite */ 9 | INTERNAL = 2, 10 | /** Access permission denied */ 11 | PERM = 3, 12 | /** Callback routine requested an abort */ 13 | ABORT = 4, 14 | /** The database file is locked */ 15 | BUSY = 5, 16 | /** A table in the database is locked */ 17 | LOCKED = 6, 18 | /** A malloc() failed */ 19 | NOMEM = 7, 20 | /** Attempt to write a readonly database */ 21 | READONLY = 8, 22 | /** Operation terminated by sqlite3_interrupt()*/ 23 | INTERRUPT = 9, 24 | /** Some kind of disk I/O error occurred */ 25 | IOERR = 10, 26 | /** The database disk image is malformed */ 27 | CORRUPT = 11, 28 | /** Unknown opcode in sqlite3_file_control() */ 29 | NOTFOUND = 12, 30 | /** Insertion failed because database is full */ 31 | FULL = 13, 32 | /** Unable to open the database file */ 33 | CANTOPEN = 14, 34 | /** Database lock protocol error */ 35 | PROTOCOL = 15, 36 | /** Not used */ 37 | EMPTY = 16, 38 | /** The database schema changed */ 39 | SCHEMA = 17, 40 | /** String or BLOB exceeds size limit */ 41 | TOOBIG = 18, 42 | /** Abort due to constraint violation */ 43 | CONSTRAINT = 19, 44 | /** Data type mismatch */ 45 | MISMATCH = 20, 46 | /** Library used incorrectly */ 47 | MISUSE = 21, 48 | /** Uses OS features not supported on host */ 49 | NOLFS = 22, 50 | /** Authorization denied */ 51 | AUTH = 23, 52 | /** Not used */ 53 | FORMAT = 24, 54 | /** 2nd parameter to sqlite3_bind out of range */ 55 | RANGE = 25, 56 | /** File opened that is not a database file */ 57 | NOTADB = 26, 58 | /** Notifications from sqlite3_log() */ 59 | NOTICE = 27, 60 | /** Warnings from sqlite3_log() */ 61 | WARNING = 28, 62 | /** sqlite3_step() has another row ready */ 63 | ROW = 100, 64 | /** sqlite3_step() has finished executing */ 65 | DONE = 101, 66 | 67 | IOERR_READ = (IOERR | (1 << 8)), 68 | IOERR_SHORT_READ = (IOERR | (2 << 8)), 69 | IOERR_WRITE = (IOERR | (3 << 8)), 70 | IOERR_FSYNC = (IOERR | (4 << 8)), 71 | IOERR_DIR_FSYNC = (IOERR | (5 << 8)), 72 | IOERR_TRUNCATE = (IOERR | (6 << 8)), 73 | IOERR_FSTAT = (IOERR | (7 << 8)), 74 | IOERR_UNLOCK = (IOERR | (8 << 8)), 75 | IOERR_RDLOCK = (IOERR | (9 << 8)), 76 | IOERR_DELETE = (IOERR | (10 << 8)), 77 | IOERR_BLOCKED = (IOERR | (11 << 8)), 78 | IOERR_NOMEM = (IOERR | (12 << 8)), 79 | IOERR_ACCESS = (IOERR | (13 << 8)), 80 | IOERR_CHECKRESERVEDLOCK = (IOERR | (14 << 8)), 81 | IOERR_LOCK = (IOERR | (15 << 8)), 82 | IOERR_CLOSE = (IOERR | (16 << 8)), 83 | IOERR_DIR_CLOSE = (IOERR | (17 << 8)), 84 | IOERR_SHMOPEN = (IOERR | (18 << 8)), 85 | IOERR_SHMSIZE = (IOERR | (19 << 8)), 86 | IOERR_SHMLOCK = (IOERR | (20 << 8)), 87 | IOERR_SHMMAP = (IOERR | (21 << 8)), 88 | IOERR_SEEK = (IOERR | (22 << 8)), 89 | IOERR_DELETE_NOENT = (IOERR | (23 << 8)), 90 | IOERR_MMAP = (IOERR | (24 << 8)), 91 | IOERR_GETTEMPPATH = (IOERR | (25 << 8)), 92 | IOERR_CONVPATH = (IOERR | (26 << 8)), 93 | IOERR_VNODE = (IOERR | (27 << 8)), 94 | IOERR_AUTH = (IOERR | (28 << 8)), 95 | LOCKED_SHAREDCACHE = (LOCKED | (1 << 8)), 96 | BUSY_RECOVERY = (BUSY | (1 << 8)), 97 | BUSY_SNAPSHOT = (BUSY | (2 << 8)), 98 | CANTOPEN_NOTEMPDIR = (CANTOPEN | (1 << 8)), 99 | CANTOPEN_ISDIR = (CANTOPEN | (2 << 8)), 100 | CANTOPEN_FULLPATH = (CANTOPEN | (3 << 8)), 101 | CANTOPEN_CONVPATH = (CANTOPEN | (4 << 8)), 102 | CORRUPT_VTAB = (CORRUPT | (1 << 8)), 103 | READONLY_RECOVERY = (READONLY | (1 << 8)), 104 | READONLY_CANTLOCK = (READONLY | (2 << 8)), 105 | READONLY_ROLLBACK = (READONLY | (3 << 8)), 106 | READONLY_DBMOVED = (READONLY | (4 << 8)), 107 | ABORT_ROLLBACK = (ABORT | (2 << 8)), 108 | CONSTRAINT_CHECK = (CONSTRAINT | (1 << 8)), 109 | CONSTRAINT_COMMITHOOK = (CONSTRAINT | (2 << 8)), 110 | CONSTRAINT_FOREIGNKEY = (CONSTRAINT | (3 << 8)), 111 | CONSTRAINT_FUNCTION = (CONSTRAINT | (4 << 8)), 112 | CONSTRAINT_NOTNULL = (CONSTRAINT | (5 << 8)), 113 | CONSTRAINT_PRIMARYKEY = (CONSTRAINT | (6 << 8)), 114 | CONSTRAINT_TRIGGER = (CONSTRAINT | (7 << 8)), 115 | CONSTRAINT_UNIQUE = (CONSTRAINT | (8 << 8)), 116 | CONSTRAINT_VTAB = (CONSTRAINT | (9 << 8)), 117 | CONSTRAINT_ROWID = (CONSTRAINT | (10 << 8)), 118 | NOTICE_RECOVER_WAL = (NOTICE | (1 << 8)), 119 | NOTICE_RECOVER_ROLLBACK = (NOTICE | (2 << 8)), 120 | WARNING_AUTOINDEX = (WARNING | (1 << 8)), 121 | AUTH_USER = (AUTH | (1 << 8)), 122 | OK_LOAD_PERMANENTLY = (OK | (1 << 8)), 123 | } 124 | 125 | export enum SQLiteDbConfig { 126 | MAINDBNAME = 1000, 127 | LOOKASIDE = 1001, 128 | ENABLE_FKEY = 1002, 129 | ENABLE_TRIGGER = 1003, 130 | ENABLE_FTS3_TOKENIZER = 1004, 131 | ENABLE_LOAD_EXTENSION = 1005, 132 | NO_CKPT_ON_CLOSE = 1006, 133 | ENABLE_QPSG = 1007, 134 | TRIGGER_EQP = 1008, 135 | RESET_DATABASE = 1009, 136 | DEFENSIVE = 1010, 137 | WRITABLE_SCHEMA = 1011, 138 | LEGACY_ALTER_TABLE = 1012, 139 | DQS_DML = 1013, 140 | DQS_DDL = 1014, 141 | ENABLE_VIEW = 1015, 142 | } 143 | 144 | export type SQLiteDbConfigIntPint = 145 | | SQLiteDbConfig.ENABLE_FKEY 146 | | SQLiteDbConfig.ENABLE_TRIGGER 147 | | SQLiteDbConfig.ENABLE_VIEW 148 | | SQLiteDbConfig.ENABLE_FTS3_TOKENIZER 149 | | SQLiteDbConfig.ENABLE_LOAD_EXTENSION 150 | | SQLiteDbConfig.NO_CKPT_ON_CLOSE 151 | | SQLiteDbConfig.ENABLE_QPSG 152 | | SQLiteDbConfig.TRIGGER_EQP 153 | | SQLiteDbConfig.RESET_DATABASE 154 | | SQLiteDbConfig.DEFENSIVE 155 | | SQLiteDbConfig.WRITABLE_SCHEMA 156 | | SQLiteDbConfig.LEGACY_ALTER_TABLE 157 | | SQLiteDbConfig.DQS_DDL 158 | | SQLiteDbConfig.DQS_DML 159 | export function configTypeIsIntPint(op: SQLiteDbConfig): op is SQLiteDbConfigIntPint { 160 | return ( 161 | op === SQLiteDbConfig.ENABLE_FKEY 162 | || op === SQLiteDbConfig.ENABLE_TRIGGER 163 | || op === SQLiteDbConfig.ENABLE_VIEW 164 | || op === SQLiteDbConfig.ENABLE_FTS3_TOKENIZER 165 | || op === SQLiteDbConfig.ENABLE_LOAD_EXTENSION 166 | || op === SQLiteDbConfig.NO_CKPT_ON_CLOSE 167 | || op === SQLiteDbConfig.ENABLE_QPSG 168 | || op === SQLiteDbConfig.TRIGGER_EQP 169 | || op === SQLiteDbConfig.RESET_DATABASE 170 | || op === SQLiteDbConfig.DEFENSIVE 171 | || op === SQLiteDbConfig.WRITABLE_SCHEMA 172 | || op === SQLiteDbConfig.LEGACY_ALTER_TABLE 173 | || op === SQLiteDbConfig.DQS_DDL 174 | || op === SQLiteDbConfig.DQS_DML 175 | ) 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/ts/sqlite-functions.ts: -------------------------------------------------------------------------------- 1 | namespace Module { 2 | export declare function _sqlite3_close_v2(pDb: ptr): SQLiteResult 3 | export const sqlite3_close_v2 4 | : (pDb: ptr) => SQLiteResult 5 | = _sqlite3_close_v2 6 | 7 | export declare function _sqlite3_free(ptr: sqlite3_ptr | 0): void 8 | export const sqlite3_free 9 | : (ptr: sqlite3_ptr | 0) => void 10 | = _sqlite3_free 11 | 12 | export declare function _sqlite3_open(filename: ptr, ppDb: ptr>): SQLiteResult 13 | export const sqlite3_open 14 | : (filename: string) => { result: SQLiteResult, pDb: ptr | 0 } 15 | = (filename) => { 16 | const stack = Module["stackSave"]() 17 | const ppDb = Module["stackAlloc"]>(4) 18 | const result = Module["ccall"]<"number", ["string", "number"]>("sqlite3_open", "number", ["string", "number"], [filename, ppDb]) 19 | const pDb = Module["getValue"]>(ppDb, "*") 20 | Module["stackRestore"](stack) 21 | return { result, pDb } 22 | } 23 | 24 | export declare function _sqlite3_exec>( 25 | pDb: ptr, 26 | sql: ptr, 27 | callback: ptr>>, columnNames: ptr>>) => i32>> | 0, 28 | errmsg: ptr> | 0 29 | ): SQLiteResult 30 | export const sqlite3_exec 31 | : ( 32 | pDb: ptr, 33 | sql: string, 34 | callback?: (numColumns: i32, pColumnTexts: ptr>>, pColumnNames: ptr>>) => boolean, 35 | ) => { result: SQLiteResult, errmsg: string | null } 36 | = (pDb, sql, callback) => { 37 | const UTF8ToString = Module["UTF8ToString"] 38 | const getValue = Module["getValue"] 39 | const pCallback 40 | = callback == null 41 | ? 0 42 | : Module["addFunction"]( 43 | (_x: 0, numColumns: i32, pColumnTexts: ptr>>, pColumnNames: ptr>>) => { 44 | return (callback(numColumns, pColumnTexts, pColumnNames) as any) | 0 as i32 45 | }, 46 | "iiiii") 47 | const stack = Module["stackSave"]() 48 | const ppErrmsg = Module["stackAlloc"]>(4) 49 | const result = Module["ccall"]<"number", ["number", "string", "number", "number", "number"]>("sqlite3_exec", "number", 50 | ["number", "string", "number", "number", "number"], 51 | [pDb, sql, pCallback, 0, ppErrmsg]) 52 | const pErrmsg = getValue>(ppErrmsg, "*") 53 | Module["stackRestore"](stack) 54 | const errmsg = pErrmsg === 0 ? null : UTF8ToString(pErrmsg) 55 | sqlite3_free(pErrmsg) 56 | if (pCallback !== 0) { 57 | Module["removeFunction"](pCallback) 58 | } 59 | return { result, errmsg } 60 | } 61 | 62 | export const sqlite3_load_extension 63 | = (pDb: ptr, file: string, proc?: string) => { 64 | const stack = Module["stackSave"]() 65 | const ppErrmsg = Module["stackAlloc"]>(4) 66 | const result = Module["ccall"]( 67 | "sqlite3_load_extension", 68 | "number", 69 | ["number", "string", "string", "number"], 70 | [pDb, file, proc, ppErrmsg]) as SQLiteResult 71 | const pErrmsg = Module["getValue"]>(ppErrmsg, "*") 72 | Module["stackRestore"](stack) 73 | sqlite3_free(pErrmsg) 74 | const errmsg = pErrmsg === 0 ? null : Module["UTF8ToString"](pErrmsg) 75 | return { result, errmsg } 76 | } 77 | 78 | export declare function _glue_sqlite3_db_config_int_pint(pDb: ptr, op: SQLiteDbConfigIntPint, value: i32, pValue: ptr | 0): SQLiteResult 79 | export const sqlite3_db_config 80 | : (pDb: ptr, op: SQLiteDbConfigIntPint, value: number) => { result: SQLiteResult, value: i32 } 81 | = (pDb: ptr, op: SQLiteDbConfig, ...args: any[]) => { 82 | if (configTypeIsIntPint(op)) { 83 | const [value] = args 84 | const stack = Module["stackSave"]() 85 | const pValue = Module["stackAlloc"](4) 86 | const result = Module["_glue_sqlite3_db_config_int_pint"](pDb, op, value, pValue) 87 | const retVal = Module["getValue"](pValue, "i32") 88 | Module["stackRestore"](stack) 89 | return { result, value: retVal } 90 | } 91 | throw RangeError("unimplemented") 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/ts/sqlite-objects.ts: -------------------------------------------------------------------------------- 1 | namespace Module { 2 | export type sqlite3_ptr = ptr & { __free__: "sqlite3_free" } 3 | export interface sqlite3 { __type__: "sqlite3" } 4 | } -------------------------------------------------------------------------------- /src/ts/type.ts: -------------------------------------------------------------------------------- 1 | namespace Module { 2 | export type stack = number & { __type__: "stack" } 3 | export type i8 = number & { __type__: "i8", __size__: 1 } 4 | export type i32 = number & { __type__: "i32", __size__: 4 } 5 | export type double = number & { __type__: "double", __size__: 8 } 6 | export type ptr = number & { __type__: "ptr", __deref__: T, __size__: 4 } 7 | export type arr = { __type__: "arr", __deref__: Array } 8 | export type str = { __type__: "str" } 9 | export type fn = { __type__: "fn", __deref__: F } 10 | export type sized = { __type__: unknown, __size__: number } 11 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": false, 4 | "target": "es5", 5 | "module": "none", 6 | "strict": true, 7 | "outFile": "temp/api.js", 8 | "noImplicitUseStrict": true 9 | }, 10 | "include": [ 11 | "src/ts/*" 12 | ] 13 | } --------------------------------------------------------------------------------