Skip to main content
append_rows bulk-inserts an array of JSON objects into a table as one multi-row INSERT. It is the ergonomic path for pushing in-memory data from the webview without hand-building SQL or writing a file.
  • Signature: appendRows(table: string, rows: Record<string, unknown>[]): Promise<ExecuteResult>.
  • ExecuteResult.rowsAffected is the number of rows inserted.

Semantics

1

Column order comes from the first row

The columns of the generated INSERT are taken from rows[0]’s keys, in iteration order.
2

Missing keys bind NULL

For later rows, any column present in the first row but absent here binds NULL. Extra keys not present in the first row are ignored.
3

Values bind as parameters

Every cell is bound as a positional parameter (same JS → DuckDB mapping as execute), not interpolated — so values are injection-safe.
4

Empty input is a no-op

An empty rows array returns { rowsAffected: 0 } without running any SQL.
The backend emits a single statement of the form:
The table name and every column name are validated as bare SQL identifiers (ASCII alphanumeric + _). A name with spaces, quotes, or punctuation is rejected with backend error: invalid table identifier: …. This guards the identifiers that cannot be bound as parameters.
Because the column list is fixed from the first row, make sure the first object carries every column you intend to populate. If row 1 omits a column that later rows set, that column is never part of the INSERT and those later values are silently dropped.

When to use it

Policy and permissions

  • append_rows is a write: rejected under read_only(true) with denied by policy: ….
  • Permission: better-duck-tauri:allow-append-rows (not in the default set). It also honors the AfterWrites checkpoint strategy if configured.

See also