Datasets¶
Each [[dataset]] block declares one table that DataPress will expose.
Common fields¶
| Field | Required | Default | Notes |
|---|---|---|---|
name |
yes | — | URL slug + SQL table name. Must be unique. |
source |
yes | — | Sub-table: { kind = "parquet" \| "delta", location = "..." }. |
s3 |
no | absent | Only meaningful when location starts with s3://. See S3 / object storage. |
index |
no | mode="auto" |
Equality-index policy (DataFusion only). See Indexing. |
lazy |
no | false |
Skip materialisation; stream row groups at query time. DataFusion + DuckDB, parquet + delta. |
predicate_filter |
no | absent | Access control: restrict which columns may be filtered on. See Column access control. |
projection_filter |
no | absent | Access control: hide columns from queries entirely. See Column access control. |
source reference¶
[dataset.source] is a tagged enum.
kind |
location |
Notes |
|---|---|---|
parquet |
a .parquet file |
Read as-is. |
parquet |
a directory | Every *.parquet inside (sorted, non-recursive). No glob patterns. |
parquet |
a glob (data/*/2024-*.parquet) |
Supported wildcards: *, ?, [abc]. |
parquet |
s3://bucket/key.parquet or s3://bucket/prefix/ |
Requires [dataset.s3]. DuckDB autoloads httpfs. |
delta |
a local directory | Pointed at the table root (the dir containing _delta_log/). |
delta |
s3://bucket/path/to/table |
Requires [dataset.s3]. DuckDB autoloads delta; DataFusion uses the deltalake crate. |
Delta on S3 always materialises
When kind = "delta" and location is s3://..., both backends
fully materialise the table at startup. There is no incremental
scan path — switch to parquet if you need on-demand page reads.
Single parquet file¶
[[dataset]]
name = "accidents"
[dataset.source]
kind = "parquet"
location = "data/us_accidents/march_2023.parquet"
Directory of parquet files¶
location can be a directory; every *.parquet underneath is loaded
in sorted order (non-recursive).
Glob pattern¶
[[dataset]]
name = "sales_2024"
[dataset.source]
kind = "parquet"
location = "data/sales/2024/*/*.parquet"
Lazy mode for huge datasets¶
When the decompressed Arrow size won't fit in RAM (or the index is too
expensive to build), set lazy = true. The DataFusion backend
registers a ListingTable and streams row groups at query time;
column-projection pushdown and parquet row-group skipping happen
automatically.
Trade-off: higher per-query latency, no equality index. Always
pass explicit columns=[...] in your queries to maximise projection
pushdown.
[[dataset]]
name = "us_accidents"
lazy = true
[dataset.source]
kind = "parquet"
location = "data/us_accidents/*.parquet"
Lazy mode requirements:
backend = "datafusion"kind = "parquet"(lazy delta is rejected at startup)
Delta — local¶
For S3-backed parquet and delta tables, see S3 / object storage.
Column access control¶
Two optional per-dataset filters restrict what callers can do with
individual columns. Each takes either include (an allowlist) or
exclude (a denylist) — setting both is a configuration error. Column
names are matched case-insensitively.
| Filter | Effect |
|---|---|
projection_filter |
Hides columns. A hidden column cannot be selected, grouped, ordered, aggregated, or seen in the schema — it behaves as if it does not exist. |
predicate_filter |
Blocks filtering. The column stays visible and selectable, but may not appear in a where/having predicate. |
[[dataset]]
name = "people"
[dataset.source]
kind = "parquet"
location = "data/people.parquet"
# Hide `ssn` and `internal_notes` everywhere.
[dataset.projection_filter]
exclude = ["ssn", "internal_notes"]
# `email` is selectable, but callers may not filter/probe on it.
[dataset.predicate_filter]
exclude = ["email"]
Allowlist form (only the named columns are exposed / filterable):
Enforcement is uniform across every read surface and both backends (DataFusion and DuckDB), including the Python-embedded server:
- Structured
/queryand/count: a predicate on a hidden column is rejected as an unknown column (400, to avoid revealing that the column exists); a predicate on a predicate-restricted-but-visible column is403 Forbidden. Naming a hidden column incolumns/group_by/order_by/aggregations is a400. A default (emptycolumns) query silently returns only the visible columns. - Schema, sample, and dataset listing endpoints omit hidden columns.
- The raw SQL endpoint applies a conservative rule: a hidden or
predicate-restricted column may not be referenced anywhere in the
statement, and
SELECT *is rejected whenever any column is hidden (it would otherwise expand to include them). List the columns you want explicitly. - Parquet export (
/datasets/{name}.parquet) is disabled with a403while aprojection_filteris active, since it would stream the raw source and bypass the filter.
An unknown column name in either filter is rejected at startup (or at
register/reload time), so a typo can't silently expose a column.
Explorer DuckDB-WASM console
The in-browser SQL console in the explorer UI downloads the raw parquet and runs entirely client-side, so it is not subject to these server-side filters. Disable the explorer, or don't rely on column access control, if untrusted users can reach it.
Empty datasets are skipped, not fatal¶
If a dataset's source.location resolves to no files at startup —
an empty directory, a glob that matches nothing, an S3 prefix with no
objects yet, or a Delta table with no data files (no log segment, or a
committed schema with zero rows) — DataPress logs a warning and skips
just that dataset. The rest of the registry still loads and serves
traffic:
This applies to both backends (datafusion and duckdb), to parquet
sources whether local or s3://, to Delta tables, and to the Python
bindings the same way. The skipped dataset simply won't appear in
/api/v1/datasets.
Empty Delta tables are skipped
A Delta table that resolves to zero data files is skipped, whether
it has a committed transaction log + schema but no rows, or no log
segment at all (an uninitialized or not-yet-a-Delta-table path). It
won't appear in /api/v1/datasets or the explorer until it has rows.
Inaccessible S3 sources are skipped too
An s3:// source that returns 403 Access Denied at startup (bad
credentials, missing bucket/prefix policy, or an expired token) is
logged and skipped the same way, instead of aborting the whole
server. Fix the credentials or policy and reload (or restart) to
pick it up.
reload still errors on empty
POST /api/v1/datasets/{name}/reload returns an error if the
reloaded source is empty. A reload is an explicit admin action, so it
reports failure rather than silently dropping the live dataset.
See Troubleshooting › empty datasets for diagnosis tips.