Skip to content

Commit 071a546

Browse files
committed
better SQL docs:
* illustrates how to make sql fenced code blocks from a DuckDBClient isntance * syncs the sql fenced code blocks with the mosaic db (thanks, @tel!) * gives better guidance for arbitrary query creation (with a safer approach for `xxx IN(${array})`) supersedes #1849
1 parent 8efdcf4 commit 071a546

File tree

3 files changed

+36
-6
lines changed

3 files changed

+36
-6
lines changed

docs/lib/duckdb.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ db.queryRow("SELECT count() AS count FROM gaia")
9292

9393
See the [DatabaseClient Specification](https://observablehq.com/@observablehq/database-client-specification) for more details on these methods.
9494

95-
Finally, the `DuckDBClient.sql` method <a href="https://github.com/observablehq/framework/releases/tag/v1.4.0" class="observablehq-version-badge" data-version="^1.4.0" title="Added in 1.4.0"></a> takes the same arguments as `DuckDBClient.of` and returns the corresponding `db.sql` tagged template literal. The returned function can be used to redefine the built-in [`sql` tagged template literal](../sql#sql-literals) and thereby change the database used by [SQL code blocks](../sql), allowing you to query dynamically-registered tables (unlike the **sql** front matter option).
95+
## Custom setup
96+
97+
The `DuckDBClient.sql` method <a href="https://github.com/observablehq/framework/releases/tag/v1.4.0" class="observablehq-version-badge" data-version="^1.4.0" title="Added in 1.4.0"></a> takes the same arguments as `DuckDBClient.of` and returns the corresponding `db.sql` tagged template literal.
98+
99+
The returned function can be used to redefine the built-in [`sql` tagged template literal](../sql#sql-literals) and thereby change the database used by [SQL code blocks](../sql), allowing you to query dynamically-registered tables (unlike the **sql** front matter option).
96100

97101
```js
98102
const feed = view(Inputs.select(new Map([["M4.5+", "4.5"], ["M2.5+", "2.5"], ["All", "all"]]), {label: "Earthquake feed"}));
@@ -106,6 +110,13 @@ const sql = DuckDBClient.sql({quakes: `https://earthquake.usgs.gov/earthquakes/f
106110
SELECT * FROM quakes ORDER BY updated DESC;
107111
```
108112

113+
The definition above is shorthand for:
114+
115+
```js run=false
116+
const db = await DuckDBClient.of({quakes: `https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/${feed}_day.csv`});
117+
const sql = db.sql.bind(db);
118+
```
119+
109120
## Extensions <a href="https://github.com/observablehq/framework/releases/tag/v1.13.0" class="observablehq-version-badge" data-version="^1.13.0" title="Added in 1.13.0"></a>
110121

111122
[DuckDB extensions](https://duckdb.org/docs/extensions/overview.html) extend DuckDB’s functionality, adding support for additional file formats, new types, and domain-specific functions. For example, the [`json` extension](https://duckdb.org/docs/data/json/overview.html) provides a `read_json` method for reading JSON files:

docs/lib/mosaic.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const db = await DuckDBClient.of({trips: FileAttachment("nyc-taxi.parquet")});
2525
const coordinator = new vgplot.Coordinator();
2626
coordinator.databaseConnector(vgplot.wasmConnector({duckdb: db._db}));
2727
const vg = vgplot.createAPIContext({coordinator});
28+
const sql = db.sql.bind(db);
2829
```
2930

3031
The code below creates three views, coordinated by Mosaic’s [crossfilter](https://uwdata.github.io/mosaic/api/core/selection.html#selection-crossfilter) helper.

docs/sql.md

+23-5
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,36 @@ The `sql` tag is available by default in Markdown. You can also import it explic
191191
import {sql} from "npm:@observablehq/duckdb";
192192
```
193193

194-
The `sql` tag is also useful for working around a current limitation of DuckDB-Wasm: prepared statements do not support array arguments. (Please upvote [#447](https://github.com/duckdb/duckdb-wasm/issues/447) if you run into this issue.) Instead of passing the array as a parameter, you can interpolate the array values directly into the SQL query.
194+
For a more custom setup, see [DuckDBClient](./lib/duckdb#custom-setup).
195+
196+
<div class="tip">
197+
198+
DuckDB only supports interpolation of strings and numbers with `${…}`. To interpolate an array of values, such as a list of ids, serialize to JSON:
195199

196200
```js echo
197-
const source_ids = [2028328031008716288n, 2076498116457016960n, 4315266827603868160n, 4123529214004874624n, 5312548578630777344n];
201+
const ids = ["2028328031008716288", "2076498116457016960", "4315266827603868160", "4123529214004874624", "5312548578630777344"];
202+
```
203+
204+
```sql echo
205+
SELECT * FROM gaia
206+
WHERE source_id::string IN JSON(${JSON.stringify(ids)});
198207
```
199208

209+
If you need to create a query on the fly, say with inputs that drive the name of a field or table, you can call the `sql` tagged template literal directly.
210+
200211
```js echo
201-
Inputs.table(await sql([`SELECT * FROM gaia WHERE source_id IN (${[source_ids]})`]))
212+
const field = view(Inputs.select(["ra", "dec", "parallax"], {label: "field"}))
213+
```
214+
215+
```js
216+
display(extent);
202217
```
203218

204-
<div class="warning">
219+
```js echo
220+
const query = `SELECT MIN(${field}) AS min, MAX(${field}) AS max FROM gaia;`;
221+
const [extent] = await sql([query]);
222+
```
205223

206-
When interpolating values into SQL queries, be careful to avoid [SQL injection](https://en.wikipedia.org/wiki/SQL_injection) by properly escaping or sanitizing user input. The example above is safe only because `source_ids` are known to be numeric.
224+
Be careful to avoid [SQL injection](https://en.wikipedia.org/wiki/SQL_injection) by properly escaping or sanitizing user input.
207225

208226
</div>

0 commit comments

Comments
 (0)