diff options
author | 2022-06-22 23:21:48 -0700 | |
---|---|---|
committer | 2022-06-22 23:21:48 -0700 | |
commit | 729d445b6885f69dd2c6355f38707bd42851c791 (patch) | |
tree | f87a7c408929ea3f57bbb7ace380cf869da83c0e /src/bun.js/bindings/sqlite/sqlite.exports.js | |
parent | 25f820c6bf1d8ec6d444ef579cc036b8c0607b75 (diff) | |
download | bun-729d445b6885f69dd2c6355f38707bd42851c791.tar.gz bun-729d445b6885f69dd2c6355f38707bd42851c791.tar.zst bun-729d445b6885f69dd2c6355f38707bd42851c791.zip |
change the directory structurejarred/rename
Diffstat (limited to 'src/bun.js/bindings/sqlite/sqlite.exports.js')
-rw-r--r-- | src/bun.js/bindings/sqlite/sqlite.exports.js | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/src/bun.js/bindings/sqlite/sqlite.exports.js b/src/bun.js/bindings/sqlite/sqlite.exports.js new file mode 100644 index 000000000..acd28164c --- /dev/null +++ b/src/bun.js/bindings/sqlite/sqlite.exports.js @@ -0,0 +1,470 @@ +var symbolFor = Symbol.for; + +const lazy = globalThis[symbolFor("Bun.lazy")]; +if (!lazy || typeof lazy !== "function") { + throw new Error( + "Something went wrong while loading Bun. Expected 'Bun.lazy' to be defined." + ); +} + +var defineProperties = Object.defineProperties; + +var toStringTag = Symbol.toStringTag; +var apply = Function.prototype.apply; +var isArray = Array.isArray; +var isTypedArray = ArrayBuffer.isView; +export const constants = { + SQLITE_OPEN_READONLY: 0x00000001 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_READWRITE: 0x00000002 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_CREATE: 0x00000004 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_DELETEONCLOSE: 0x00000008 /* VFS only */, + SQLITE_OPEN_EXCLUSIVE: 0x00000010 /* VFS only */, + SQLITE_OPEN_AUTOPROXY: 0x00000020 /* VFS only */, + SQLITE_OPEN_URI: 0x00000040 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_MEMORY: 0x00000080 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_MAIN_DB: 0x00000100 /* VFS only */, + SQLITE_OPEN_TEMP_DB: 0x00000200 /* VFS only */, + SQLITE_OPEN_TRANSIENT_DB: 0x00000400 /* VFS only */, + SQLITE_OPEN_MAIN_JOURNAL: 0x00000800 /* VFS only */, + SQLITE_OPEN_TEMP_JOURNAL: 0x00001000 /* VFS only */, + SQLITE_OPEN_SUBJOURNAL: 0x00002000 /* VFS only */, + SQLITE_OPEN_SUPER_JOURNAL: 0x00004000 /* VFS only */, + SQLITE_OPEN_NOMUTEX: 0x00008000 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_FULLMUTEX: 0x00010000 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_SHAREDCACHE: 0x00020000 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_PRIVATECACHE: 0x00040000 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_WAL: 0x00080000 /* VFS only */, + SQLITE_OPEN_NOFOLLOW: 0x01000000 /* Ok for sqlite3_open_v2() */, + SQLITE_OPEN_EXRESCODE: 0x02000000 /* Extended result codes */, + SQLITE_PREPARE_PERSISTENT: 0x01, + SQLITE_PREPARE_NORMALIZE: 0x02, + SQLITE_PREPARE_NO_VTAB: 0x04, +}; + +var SQL; +var _SQL; + +var controllers; + +export class Statement { + constructor(raw) { + this.#raw = raw; + this.#columns = undefined; + + switch (raw.paramsCount) { + case 0: { + this.get = this.#getNoArgs; + this.all = this.#allNoArgs; + this.values = this.#valuesNoArgs; + this.run = this.#runNoArgs; + break; + } + default: { + this.get = this.#get; + this.all = this.#all; + this.values = this.#values; + this.run = this.#run; + break; + } + } + } + + #raw; + #columns; + + get; + all; + values; + run; + isFinalized = false; + + toJSON() { + return { + sql: this.native.toString(), + isFinalized: this.isFinalized, + paramsCount: this.paramsCount, + columnNames: this.columnNames, + }; + } + + get [toStringTag]() { + return `"${this.native.toString()}"`; + } + + toString() { + return this.native.toString(); + } + + get native() { + return this.#raw; + } + + #getNoArgs() { + return this.#raw.get(); + } + + #allNoArgs() { + return this.#raw.all(); + } + + #valuesNoArgs() { + return this.#raw.values(); + } + + #runNoArgs() { + this.#raw.run(); + } + + #get(...args) { + if (args.length === 0) return this.#getNoArgs(); + var arg0 = args[0]; + // ["foo"] => ["foo"] + // ("foo") => ["foo"] + // (Uint8Array(1024)) => [Uint8Array] + // (123) => [123] + return !isArray(arg0) && + (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) + ? this.#raw.get(args) + : this.#raw.get(...args); + } + + #all(...args) { + if (args.length === 0) return this.#allNoArgs(); + var arg0 = args[0]; + // ["foo"] => ["foo"] + // ("foo") => ["foo"] + // (Uint8Array(1024)) => [Uint8Array] + // (123) => [123] + return !isArray(arg0) && + (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) + ? this.#raw.all(args) + : this.#raw.all(...args); + } + + #values(...args) { + if (args.length === 0) return this.#valuesNoArgs(); + var arg0 = args[0]; + // ["foo"] => ["foo"] + // ("foo") => ["foo"] + // (Uint8Array(1024)) => [Uint8Array] + // (123) => [123] + return !isArray(arg0) && + (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) + ? this.#raw.values(args) + : this.#raw.values(...args); + } + + #run(...args) { + if (args.length === 0) return this.#runNoArgs(); + var arg0 = args[0]; + + !isArray(arg0) && (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) + ? this.#raw.run(args) + : this.#raw.run(...args); + } + + get columnNames() { + return (this.#columns ||= this.#raw.columns); + } + + get paramsCount() { + return this.#raw.paramsCount; + } + + finalize(...args) { + this.isFinalized = true; + return this.#raw.finalize(...args); + } +} + +var cachedCount = symbolFor("Bun.Database.cache.count"); +export class Database { + constructor(filenameGiven, options) { + if (typeof filenameGiven === "undefined") { + } else if (typeof filenameGiven !== "string") { + if (isTypedArray(filenameGiven)) { + this.#handle = Database.deserialize( + filenameGiven, + typeof options === "object" && options + ? !!options.readonly + : ((options | 0) & constants.SQLITE_OPEN_READONLY) != 0 + ); + this.filename = ":memory:"; + return; + } + + throw new TypeError( + `Expected 'filename' to be a string, got '${typeof filenameGiven}'` + ); + } + + var filename = + typeof filenameGiven === "string" ? filenameGiven.trim() : ":memory:"; + var flags = constants.SQLITE_OPEN_READWRITE | constants.SQLITE_OPEN_CREATE; + if (typeof options === "object" && options) { + flags = 0; + + if (options.readonly) { + flags = constants.SQLITE_OPEN_READONLY; + } + + if ("readOnly" in options) + throw new TypeError( + 'Misspelled option "readOnly" should be "readonly"' + ); + + if (options.create) { + flags = constants.SQLITE_OPEN_READWRITE | constants.SQLITE_OPEN_CREATE; + } + + if (options.readwrite) { + flags |= constants.SQLITE_OPEN_READWRITE; + } + } else if (typeof options === "number") { + flags = options; + } + + const anonymous = filename === "" || filename === ":memory:"; + if (anonymous && (flags & constants.SQLITE_OPEN_READONLY) !== 0) { + throw new Error("Cannot open an anonymous database in read-only mode."); + } + + if (!SQL) { + _SQL = SQL = lazy("sqlite"); + } + + this.#handle = SQL.open(anonymous ? ":memory:" : filename, flags); + this.filename = filename; + } + + #handle; + #cachedQueriesKeys = []; + #cachedQueriesLengths = []; + #cachedQueriesValues = []; + filename; + + get handle() { + return this.#handle; + } + + get inTransaction() { + return SQL.isInTransaction(this.#handle); + } + + static open(filename, options) { + return new Database(filename, options); + } + + loadExtension(name, entryPoint) { + return SQL.loadExtension(this.#handle, name, entryPoint); + } + + serialize(optionalName) { + return SQL.serialize(this.#handle, optionalName || "main"); + } + + static deserialize(serialized, isReadOnly = false) { + if (!SQL) { + _SQL = SQL = lazy("sqlite"); + } + + return SQL.deserialize(serialized, isReadOnly); + } + + static setCustomSQLite(path) { + if (!SQL) { + _SQL = SQL = lazy("sqlite"); + } + + return SQL.setCustomSQLite(path); + } + + close() { + this.clearQueryCache(); + return SQL.close(this.#handle); + } + clearQueryCache() { + for (let item of this.#cachedQueriesValues) { + item.finalize(); + } + this.#cachedQueriesKeys.length = 0; + this.#cachedQueriesValues.length = 0; + this.#cachedQueriesLengths.length = 0; + } + + run(query, ...params) { + if (params.length === 0) { + SQL.run(this.#handle, query); + return; + } + + var arg0 = params[0]; + return !isArray(arg0) && + (!arg0 || typeof arg0 !== "object" || isTypedArray(arg0)) + ? SQL.run(this.#handle, query, params) + : SQL.run(this.#handle, query, ...params); + } + + prepare(query, params, flags) { + return new Statement(SQL.prepare(this.#handle, query, params, flags || 0)); + } + + static MAX_QUERY_CACHE_SIZE = 20; + + get [cachedCount]() { + return this.#cachedQueriesKeys.length; + } + + query(query) { + if (typeof query !== "string") { + throw new TypeError( + `Expected 'query' to be a string, got '${typeof query}'` + ); + } + + if (query.length === 0) { + throw new Error("SQL query cannot be empty."); + } + + // this list should be pretty small + var index = this.#cachedQueriesLengths.indexOf(query.length); + while (index !== -1) { + if (this.#cachedQueriesKeys[index] !== query) { + index = this.#cachedQueriesLengths.indexOf(query.length, index + 1); + continue; + } + + var stmt = this.#cachedQueriesValues[index]; + if (stmt.isFinalized) { + return (this.#cachedQueriesValues[index] = this.prepare( + query, + undefined, + willCache ? constants.SQLITE_PREPARE_PERSISTENT : 0 + )); + } + return stmt; + } + + const willCache = + this.#cachedQueriesKeys.length < Database.MAX_QUERY_CACHE_SIZE; + + var stmt = this.prepare( + query, + undefined, + willCache ? constants.SQLITE_PREPARE_PERSISTENT : 0 + ); + + if (willCache) { + this.#cachedQueriesKeys.push(query); + this.#cachedQueriesLengths.push(query.length); + this.#cachedQueriesValues.push(stmt); + } + + return stmt; + } + + // Code for transactions is largely copied from better-sqlite3 + // https://github.com/JoshuaWise/better-sqlite3/blob/master/lib/methods/transaction.js + // thank you @JoshuaWise! + transaction(fn, self) { + if (typeof fn !== "function") + throw new TypeError("Expected first argument to be a function"); + + const db = this; + const controller = getController(db, self); + + // Each version of the transaction function has these same properties + const properties = { + default: { value: wrapTransaction(fn, db, controller.default) }, + deferred: { value: wrapTransaction(fn, db, controller.deferred) }, + immediate: { + value: wrapTransaction(fn, db, controller.immediate), + }, + exclusive: { + value: wrapTransaction(fn, db, controller.exclusive), + }, + database: { value: this, enumerable: true }, + }; + + defineProperties(properties.default.value, properties); + defineProperties(properties.deferred.value, properties); + defineProperties(properties.immediate.value, properties); + defineProperties(properties.exclusive.value, properties); + + // Return the default version of the transaction function + return properties.default.value; + } +} + +Database.prototype.exec = Database.prototype.run; + +// Return the database's cached transaction controller, or create a new one +const getController = (db, self) => { + let controller = (controllers ||= new WeakMap()).get(db); + if (!controller) { + const shared = { + commit: db.prepare("COMMIT", undefined, 0), + rollback: db.prepare("ROLLBACK", undefined, 0), + savepoint: db.prepare("SAVEPOINT `\t_bs3.\t`", undefined, 0), + release: db.prepare("RELEASE `\t_bs3.\t`", undefined, 0), + rollbackTo: db.prepare("ROLLBACK TO `\t_bs3.\t`", undefined, 0), + }; + + controllers.set( + db, + (controller = { + default: Object.assign( + { begin: db.prepare("BEGIN", undefined, 0) }, + shared + ), + deferred: Object.assign( + { begin: db.prepare("BEGIN DEFERRED", undefined, 0) }, + shared + ), + immediate: Object.assign( + { begin: db.prepare("BEGIN IMMEDIATE", undefined, 0) }, + shared + ), + exclusive: Object.assign( + { begin: db.prepare("BEGIN EXCLUSIVE", undefined, 0) }, + shared + ), + }) + ); + } + return controller; +}; + +// Return a new transaction function by wrapping the given function +const wrapTransaction = ( + fn, + db, + { begin, commit, rollback, savepoint, release, rollbackTo } +) => + function transaction(...args) { + let before, after, undo; + if (db.inTransaction) { + before = savepoint; + after = release; + undo = rollbackTo; + } else { + before = begin; + after = commit; + undo = rollback; + } + try { + before.run(); + const result = fn.apply(this, args); + after.run(); + return result; + } catch (ex) { + if (db.inTransaction) { + undo.run(); + if (undo !== rollback) after.run(); + } + throw ex; + } + }; + +export { _SQL as native }; +export { Database as default }; |