DRAFTThis page is not published. Only visible in development mode.

The with Clause

with { ... } attaches an options block to the construct directly before it: a create statement, a column definition, or a pipeline operator. It is not an operator itself - it never stands alone as a pipeline step. The keys inside the braces belong to whatever the clause is attached to, and each construct accepts a fixed set of them.

Not SQL's WITH
RQL has no common table expressions. To name an intermediate result, assign it to a variable with let and pipe from $name, or persist it as a view.

Where with appears

  • --Create statements. After the column block of a table, ring buffer, series, or view: storage options like capacity or key, row settings, and partitioning.
  • --Column definitions. After a column's type: auto_increment and dictionary.
  • --Pipeline operators. At the end of join, distinct, and window: state lifetime and behavior options for the engine that keeps views up to date.

Storage options at creation

Each storage shape has its own keys. A ring buffer declares its capacity; a series declares its key column and optionally precision and tag. Views place the clause between the column block and as - a ring-buffer-backed view takes capacity, a series-backed view takes precision. Unknown keys are rejected, so a typo fails at parse time instead of being silently ignored:

$ Configure Storage at Creation
$ ctrl+enter to run

Here the clause is the whole reason the shape works: two rows of capacity, so the third insert evicts the first. See ring buffers for partitioning with partition.

Row settings

Tables, ring buffers, series, and views all accept a row block that controls the lifetime of their rows: ttl with a duration such as '30m' or '30d' and an optional mode (drop or delete), plus persistent: false for rows that live in memory only. A non-persistent shape must also declare a TTL - rows that never reach disk have to expire.

$ Row Settings on a Table
$ ctrl+enter to run

The full expiry semantics - what anchors the clock, what each mode means for views and subscriptions - are covered on the TTL page.

Column options

A with block after a column's type configures that column. auto_increment makes the engine fill the column from a sequence when an insert omits it; dictionary: ns::dict stores the column dictionary-encoded through a dictionary:

$ Options on a Column
$ ctrl+enter to run

Operator options

On pipeline operators, with configures the state the flow engine keeps when the pipeline runs inside a view. join accepts three keys: snapshot: true turns the joined-in side into a passive lookup - right-side changes update the join's state but emit no revisions to rows already joined; latest: true keeps only the newest right-side row per key instead of every match; and ttl: { left: { duration: '1h' } } (and/or right) evicts join state that has not been touched within the duration.

$ Options on a Join
$ ctrl+enter to run

distinct takes a ttl the same way: an entry the operator has not seen within the duration is evicted, so the value counts as new when it next appears.

$ Options on Distinct
$ ctrl+enter to run

These options exist for incremental view maintenance. An ad-hoc query like the ones above accepts them, but since it computes its result in one shot, there is no long-lived state for them to manage. window is configured the same way (with { interval: '5s' }) and only runs inside deferred views; newer engine builds also accept with { ttl: ... } on apply, though the sandbox build on this site does not.

Keys are validated

The with block is not a free-form bag of settings. Every construct checks the keys it is given - a table accepts row and partition and nothing else:

$ Unknown Keys Are Rejected
$ ctrl+enter to run