From c8599fbd7cc337566eb530249bc40c5c2a49ce9c Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 26 Apr 2022 13:37:11 -0700 Subject: [PATCH 1/2] SQLiteDatabaseClient schema --- src/sqlite.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/sqlite.js b/src/sqlite.js index 6b26b5c1..f179ba6b 100644 --- a/src/sqlite.js +++ b/src/sqlite.js @@ -28,10 +28,16 @@ export class SQLiteDatabaseClient { text(rows.map(row => row.detail).join("\n")) ]); } - async describe(object) { - const rows = await (object === undefined - ? this.query(`SELECT name FROM sqlite_master WHERE type = 'table'`) - : this.query(`SELECT * FROM pragma_table_info(?)`, [object])); + async describeTables() { + return this.query(`SELECT name FROM sqlite_master WHERE type = 'table'`); + } + async describeColumns({table} = {}) { + const rows = await this.query(`SELECT name, type, notnull FROM pragma_table_info(?) ORDER BY cid`, [table]); + if (!rows.length) throw new Error(`table not found: ${table}`); + return rows.map(({name, type, notnull}) => ({name, type: sqliteType(type), databaseType: type, nullable: !notnull})); + } + async describe(table) { + const rows = await (table === undefined ? this.describeTables() : this.describeColumns({table})); if (!rows.length) throw new Error("Not found"); const {columns} = rows; return element("table", {value: rows}, [ @@ -46,10 +52,47 @@ export class SQLiteDatabaseClient { return [strings.join("?"), params]; } } + Object.defineProperty(SQLiteDatabaseClient.prototype, "dialect", { value: "sqlite" }); +// https://www.sqlite.org/datatype3.html +function sqliteType(type) { + switch (type) { + case "NULL": + return "null"; + case "INT": + case "INTEGER": + case "TINYINT": + case "SMALLINT": + case "MEDIUMINT": + case "BIGINT": + case "UNSIGNED BIG INT": + case "INT2": + case "INT8": + return "integer"; + case "TEXT": + case "CLOB": + return "string"; + case "REAL": + case "DOUBLE": + case "DOUBLE PRECISION": + case "FLOAT": + case "NUMERIC": + return "number"; + case "BLOB": + return "buffer"; + case "DATE": + case "DATETIME": + return "string"; // TODO convert strings to Date instances in sql.js + default: + return /^(?:(?:(?:VARYING|NATIVE) )?CHARACTER|(?:N|VAR|NVAR)CHAR)\(/.test(type) ? "string" + : /^(?:DECIMAL|NUMERIC)\(/.test(type) ? "number" + : "other"; + } +} + function load(source) { return typeof source === "string" ? fetch(source).then(load) : source instanceof Response || source instanceof Blob ? source.arrayBuffer().then(load) From 400ae038748e8003583e2643b65b5578aa39f66a Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 6 Jun 2022 19:24:59 -0700 Subject: [PATCH 2/2] remove redundant error --- src/sqlite.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sqlite.js b/src/sqlite.js index f179ba6b..1c4f8e43 100644 --- a/src/sqlite.js +++ b/src/sqlite.js @@ -38,7 +38,6 @@ export class SQLiteDatabaseClient { } async describe(table) { const rows = await (table === undefined ? this.describeTables() : this.describeColumns({table})); - if (!rows.length) throw new Error("Not found"); const {columns} = rows; return element("table", {value: rows}, [ element("thead", [element("tr", columns.map(c => element("th", [text(c)])))]),