Bounds and history
A query accepts four bounds that protect the engine. bind reads the database
at an earlier transaction, or over facts that you supply for one query. Each
example reads the same five entities as Queries.
Bounds
Each bound is a member of the query document. Zero gives no explicit bound.
Queries still receive a dynamic cost budget when maxCost is zero. Each bound
has its own rejection.
| Bound | Limits | Rejection |
|---|---|---|
maxCost | CPU time on the query's execution thread, in 100-nanosecond ticks | Work_Limit_Exceeded |
maxResults | the number of rows in the result | Result_Limit_Exceeded |
maxBytes | the size of the result | Byte_Limit_Exceeded |
maxWorkRows | the rows that the engine handles | Work_Rows_Limit_Exceeded |
maxResults counts the rows that remain after orderBy, distinct and the
window clauses. The engine rejects five result rows against a bound of two.
find [?name]
where [[?e salary ?sal]
[?e name ?name]]
orderBy [?name]
maxResults 2
Error
Result_Limit_Exceeded
The caller can also give maxCost with the request, beside the document.
Stardust then uses the smaller of the two values.
The engine measures CPU time from the start of query execution through result construction. It includes planning, fact scans, expressions, native helpers and rendering on that thread. It excludes time spent waiting and work on other threads. It varies between runs and is neither a bytecode count nor an instruction count.
The public API returns rows, with optional explain records for individual calls. The query-run record stops before later page reads.
A positive maxCost limits this measured time. A clock read has a cost, so the engine schedules its checks. Checks occur
less often while the limit is far and more often as the limit comes near. It reads the clock again after rendering.
A query can pass the limit by a small amount before the
next check. It can pass it by more when its work suddenly becomes slower or a
single helper runs for a long time. Without a positive maxCost,
Stardust derives a CPU time budget from query structure and rows handled.
The other bounds apply to rows and bytes independently.
Reading an earlier transaction
bind.with.db reads the database as it was. asOf accepts a transaction id, a
UTC instant, or a duration relative to now. Transaction ids start at 1.
find [?name]
where [[?e salary ?sal]
[?e name ?name]]
orderBy [?name]
bind {with {db {asOf 6}}}
[{name Ada} {name Bob} {name Cy} {name Dan} {name Eve}]The seed patch creates all five entities atomically in transaction 6. A snapshot before that commit holds none of them.
bind {with {db {asOf 5}}}
[]
bind {with {db {asOf 1}}}
[]
A UTC instant selects the newest transaction committed at or before that time. An instant after the last commit selects the head transaction.
bind {with {db {asOf {#utc 2027-01-01T00:00:00Z}}}}
[{name Ada} {name Bob} {name Cy} {name Dan} {name Eve}]An instant before the first commit gives no facts.
bind {with {db {asOf {#utc 2026-08-22T12:00:00Z}}}}
[]
A duration adds to the current time, so {#dur PT0S} reads the present. A
negative duration reads the past, as {#dur -PT5S} does for five seconds
ago.
bind {with {db {asOf {#dur PT0S}}}}
[{name Ada} {name Bob} {name Cy} {name Dan} {name Eve}]asOf, from, and to also accept input variables such as ?timestamp.
Supply a transaction id, UTC instant, or duration through parameters.
You can save the query before you supply the value.
Execution without the required value gives Missing_Parameter.
An asOf query can use a schema clause.
The current schema validates each entity's document at the selected transaction.
The query excludes entities that do not exist at that transaction.
interval reads a window of transactions instead. It has from and to, and
both ends are inside the window. Each end accepts a transaction id, a UTC
instant, or a duration. An instant window that resolves inverted gives no
facts.
bind {with {db {interval {from {#utc 2026-08-01T00:00:00Z}
to 29}}}}
[{name Ada} {name Bob} {name Cy} {name Dan} {name Eve}]A fourth term in a fact clause binds the transaction of that fact.
find [?name ?tx]
where [[?e name ?name ?tx]]
orderBy [?name]
[{name Ada
tx {#link 6}}
{name Bob
tx {#link 6}}
{name Cy
tx {#link 6}}
{name Dan
tx {#link 6}}
{name Eve
tx {#link 6}}]When a temporal read is rejected
db accepts asOf or interval, and it needs one of them.
| Condition | Reason |
|---|---|
asOf 0 | Transaction ids start at 1 |
A malformed #utc or #dur value | The bound is not a valid temporal value |
interval with from above to, as transaction ids | The window is empty |
asOf and interval together | The two forms exclude each other |
A member that is not asOf or interval | db accepts no other member |
An interval read with a schema clause | Schema matching requires a single snapshot |
Each of these conditions gives one error. This query reads transaction 0:
find [?name]
where [[?e salary ?sal]
[?e name ?name]]
bind {with {db {asOf 0}}}
Error
Invalid_Temporal
Facts for one query
bind.with.facts adds facts that the database does not hold. Each key is a UTC
timestamp, and its value is a merge patch below entities, keyed by entity
id, as Patches describes. Ada has ID 1 in this fixture.
find [?name ?sal]
where [[?e salary ?sal]
[?e name ?name]]
orderBy [?name]
bind {with {facts {'2020-10-30T23:20:20Z' {entities {1 {salary 200}}}}}}
[{name Ada
sal 200}
{name Bob
sal 100}
{name Cy
sal 90}
{name Dan
sal 70}
{name Eve
sal 80}]A value replaces the stored field, and null retracts it. A reference field
uses {#link <id>}. An id that the database does not hold becomes a new
entity for this query alone. Stardust writes none of these facts.