-
Notifications
You must be signed in to change notification settings - Fork 84
SQLite #212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
SQLite #212
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import now from "./now.js"; | |
import Promises from "./promises/index.js"; | ||
import resolve from "./resolve.js"; | ||
import requirer from "./require.js"; | ||
import SQLite from "./sqlite.js"; | ||
import svg from "./svg.js"; | ||
import tex from "./tex.js"; | ||
import vegalite from "./vegalite.js"; | ||
|
@@ -26,18 +27,19 @@ export default Object.assign(function Library(resolver) { | |
Mutable: () => Mutable, | ||
Plot: () => require("@observablehq/[email protected]/dist/plot.umd.min.js"), | ||
Promises: () => Promises, | ||
SQLite: () => SQLite(require), | ||
_: () => require("[email protected]/lodash.min.js"), | ||
d3: () => require("[email protected]/dist/d3.min.js"), | ||
dot: () => require("@observablehq/[email protected]/dist/graphviz.min.js"), | ||
htl: () => require("[email protected]/dist/htl.min.js"), | ||
html: () => html, | ||
md: md(require), | ||
md: () => md(require), | ||
now: now, | ||
require: () => require, | ||
resolve: () => resolve, | ||
svg: () => svg, | ||
tex: tex(require), | ||
vl: vegalite(require), | ||
tex: () => tex(require), | ||
vl: () => vegalite(require), | ||
width: width | ||
})); | ||
}, {resolve: requireDefault.resolve}); | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,44 @@ | ||
import template from "./template.js"; | ||
|
||
const HL_ROOT = | ||
"https://cdn.jsdelivr.net/npm/@observablehq/[email protected]/"; | ||
const HL_ROOT = "https://cdn.jsdelivr.net/npm/@observablehq/[email protected]/"; | ||
|
||
export default function(require) { | ||
return function() { | ||
return require("[email protected]/marked.min.js").then(function(marked) { | ||
return template( | ||
function(string) { | ||
var root = document.createElement("div"); | ||
root.innerHTML = marked(string, {langPrefix: ""}).trim(); | ||
var code = root.querySelectorAll("pre code[class]"); | ||
if (code.length > 0) { | ||
require(HL_ROOT + "highlight.min.js").then(function(hl) { | ||
code.forEach(function(block) { | ||
function done() { | ||
hl.highlightBlock(block); | ||
block.parentNode.classList.add("observablehq--md-pre"); | ||
} | ||
if (hl.getLanguage(block.className)) { | ||
done(); | ||
} else { | ||
require(HL_ROOT + "async-languages/index.js") | ||
.then(index => { | ||
if (index.has(block.className)) { | ||
return require(HL_ROOT + | ||
"async-languages/" + | ||
index.get(block.className)).then(language => { | ||
hl.registerLanguage(block.className, language); | ||
}); | ||
} | ||
}) | ||
.then(done, done); | ||
} | ||
}); | ||
return require("[email protected]/marked.min.js").then(function(marked) { | ||
return template( | ||
function(string) { | ||
var root = document.createElement("div"); | ||
root.innerHTML = marked(string, {langPrefix: ""}).trim(); | ||
var code = root.querySelectorAll("pre code[class]"); | ||
if (code.length > 0) { | ||
require(HL_ROOT + "highlight.min.js").then(function(hl) { | ||
code.forEach(function(block) { | ||
function done() { | ||
hl.highlightBlock(block); | ||
block.parentNode.classList.add("observablehq--md-pre"); | ||
} | ||
if (hl.getLanguage(block.className)) { | ||
done(); | ||
} else { | ||
require(HL_ROOT + "async-languages/index.js") | ||
.then(index => { | ||
if (index.has(block.className)) { | ||
return require(HL_ROOT + | ||
"async-languages/" + | ||
index.get(block.className)).then(language => { | ||
hl.registerLanguage(block.className, language); | ||
}); | ||
} | ||
}) | ||
.then(done, done); | ||
} | ||
}); | ||
} | ||
return root; | ||
}, | ||
function() { | ||
return document.createElement("div"); | ||
}); | ||
} | ||
); | ||
}); | ||
}; | ||
return root; | ||
}, | ||
function() { | ||
return document.createElement("div"); | ||
} | ||
); | ||
}); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
export default async function sqlite(require) { | ||
const sql = await require("[email protected]/dist/sql-wasm.js"); | ||
return sql({locateFile: file => `https://cdn.jsdelivr.net/npm/[email protected]/dist/${file}`}); | ||
} | ||
|
||
export class SQLiteDatabaseClient { | ||
constructor(db) { | ||
Object.defineProperties(this, { | ||
_db: {value: db} | ||
}); | ||
} | ||
async query(query, params) { | ||
return await exec(this._db, query, params); | ||
} | ||
async queryRow(query, params) { | ||
return (await this.query(query, params))[0] || null; | ||
} | ||
async explain(query, params) { | ||
const rows = await this.query(`EXPLAIN QUERY PLAN ${query}`, params); | ||
return element("pre", {className: "observablehq--inspect"}, [ | ||
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])); | ||
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)])))]), | ||
element("tbody", rows.map(r => element("tr", columns.map(c => element("td", [text(r[c])]))))) | ||
]); | ||
} | ||
} | ||
|
||
async function exec(db, query, params) { | ||
const [result] = await db.exec(query, params); | ||
if (!result) return []; | ||
const {columns, values} = result; | ||
const rows = values.map(row => Object.fromEntries(row.map((value, i) => [columns[i], value]))); | ||
rows.columns = columns; | ||
return rows; | ||
} | ||
|
||
function element(name, props, children) { | ||
if (arguments.length === 2) children = props, props = undefined; | ||
const element = document.createElement(name); | ||
if (props !== undefined) for (const p in props) element[p] = props[p]; | ||
if (children !== undefined) for (const c of children) element.appendChild(c); | ||
return element; | ||
} | ||
|
||
function text(value) { | ||
return document.createTextNode(value); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,25 +11,23 @@ function style(href) { | |
}); | ||
} | ||
|
||
export default function(require) { | ||
return function() { | ||
return Promise.all([ | ||
require("@observablehq/[email protected]/dist/katex.min.js"), | ||
require.resolve("@observablehq/[email protected]/dist/katex.min.css").then(style) | ||
]).then(function(values) { | ||
var katex = values[0], tex = renderer(); | ||
export default function tex(require) { | ||
return Promise.all([ | ||
require("@observablehq/[email protected]/dist/katex.min.js"), | ||
require.resolve("@observablehq/[email protected]/dist/katex.min.css").then(style) | ||
]).then(function(values) { | ||
var katex = values[0], tex = renderer(); | ||
|
||
function renderer(options) { | ||
return function() { | ||
var root = document.createElement("div"); | ||
katex.render(raw.apply(String, arguments), root, options); | ||
return root.removeChild(root.firstChild); | ||
}; | ||
} | ||
function renderer(options) { | ||
return function() { | ||
var root = document.createElement("div"); | ||
katex.render(raw.apply(String, arguments), root, options); | ||
return root.removeChild(root.firstChild); | ||
}; | ||
} | ||
|
||
tex.options = renderer; | ||
tex.block = renderer({displayMode: true}); | ||
return tex; | ||
}); | ||
}; | ||
tex.options = renderer; | ||
tex.block = renderer({displayMode: true}); | ||
return tex; | ||
}); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,8 @@ | ||
export default function vl(require) { | ||
return async () => { | ||
const [vega, vegalite, api] = await Promise.all([ | ||
"[email protected]/build/vega.min.js", | ||
"[email protected]/build/vega-lite.min.js", | ||
"[email protected]/build/vega-lite-api.min.js" | ||
].map(module => require(module))); | ||
return api.register(vega, vegalite); | ||
}; | ||
export default async function vl(require) { | ||
const [vega, vegalite, api] = await Promise.all([ | ||
"[email protected]/build/vega.min.js", | ||
"[email protected]/build/vega-lite.min.js", | ||
"[email protected]/build/vega-lite-api.min.js" | ||
].map(module => require(module))); | ||
return api.register(vega, vegalite); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it going to be odd that this database client doesn't extend the worker's DatabaseClient function, prototype-chain-wise, even though it implements the same interface? It only occurred to me because queryRow matches exactly the worker's implementation and could be re-used here without copying.
I was thinking alternatively, this implementation could move to the worker and it could accept a file attachment for the constructor:
DatabaseClient(FileAttachment("db.sqlite"))
. Though, there's a lot of assumption going into that expression since nothing references "sqlite" statically.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DatabaseClient isn’t currently part of the standard library, so I’ll need to move parts of it up here to make that work. I’ll take a crack at it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about some more and I don’t think this is worth the effort. It’s not easy to move the DatabaseClient class into the standard library because it hard-codes the known implementations, all of which currently require coordination with the editor (since they are remote databases that require a database connector, not an in-memory local database like SQLite).