Exact predicate and order families
RiffQL language V6 freezes a provider-independent semantic model for bounded, exact operational result sets. It is a compiler surface, not a public query object. Applications still invoke named generated operations; callers never submit fields, operators, Boolean trees, order expressions, provider choices, cost hints, or fallback flags.
The compiler accepts the following closed predicate vocabulary inside one
partition-routed many binding:
==,!=,<,<=,>, and>=over one exact scalar type;- bounded typed
inandnot_insets; is null,is not null, andexistsstate tests;- binary UTF-8
prefix,starts_with,ends_with, andcontains; - conjunction and compiler-capped disjunction; and
when $optional { ... }guards whose complete presence family is compiled before deployment.
Value predicates are two-valued. Missing and explicit-null fields satisfy none
of the value or complement operators. Use a state test when state is relevant.
An empty in set matches nothing; an empty not_in set matches every present,
non-null value. Submitted sets are sorted and deduplicated canonically before
they enter execution or identity. Text uses exact binary UTF-8 semantics; no
locale, regex, wildcard, or implicit normalization exists.
An exact family also declares a nonempty total order independent from its
search fields. The order ends with the complete canonical entity key in
ascending direction. Order fields must be present and non-null for the whole
installed lineage. The query combines that order with exact_count() and a
bounded take $limit offset $offset window:
query SearchUsers(
$organization_id: User.organization_id,
$needle: User.email,
$states: Set<User.state>,
$after: User.created_at?,
$limit: Limit<499>,
$offset: u64
) {
many users from User
where organization_id == $organization_id
&& email contains $needle
&& state not_in $states
&& when $after { created_at >= $after }
order by created_at desc, user_id asc
take $limit offset $offset
aggregate totals from users { exact_count() as total }
return Found {
users: users { user_id email state created_at }
totals: totals { total }
}
outcomes Found
}
Nullable order placement
RiffQL V7 adds explicit placement for an order field that may be missing in an installed schema version or may contain null:
order by subtitle asc nulls last, created_at desc nulls first, record_id asc
Only nulls first and nulls last are accepted. They are part of the named
query and its plan/module identity; an application request cannot choose or
override them. Omission is valid only when the compiler proves every admitted
row and installed schema version has a present, non-null value. The complete
ascending entity-key suffix is always present-only and cannot carry a null
placement.
For ordering, a missing field and an explicit null are one NoValue class.
They compare equal for that term, so later order terms and finally the complete
entity key resolve the tie. Direction changes only value-versus-value order:
desc nulls last still places every NoValue after all present values. RiffDB
does not inherit SQL, storage-engine, locale, or host-language null defaults and
does not substitute a sentinel scalar.
The compiler makes the V7 source and its provider-independent V10 semantic/module artifacts compilable. Rebuildable provider-state V5 activates its internal exact-count/direct-ordinal execution lane and connects that lane through the shared service, gRPC, CLI, MCP, and generated Rust, Go, TypeScript, and Python methods. There is no scan, request-time sort, page walk, or older- provider fallback.
Declare every nullable placement in the checked-in named .riffq document,
then regenerate the application lock and typed clients. Generated callers pass
only the operation’s domain parameters, limit, and offset; there is no sort
descriptor to construct. The framework-neutral InventoryRecord corpus in
fixtures/adapters/operational-conformance demonstrates optional text and
timestamp orders in both directions and placements, later-term NoValue ties,
exact totals, direct ordinal boundaries, and missing/null/value transitions.
Every predicate and order field requires a declared partition-routed index. One missing index, invalid type, incomplete key tie-breaker, excessive Boolean shape, or unsupported family member rejects the complete named query with a source-spanned diagnostic. There is no partial family and no runtime scan.
Current activation status
RiffQL V6 source, semantic IR V9, and query-module V9 are compilable and reproducible. Rebuildable provider-state format V4 is active through the shared application service, gRPC, CLI, MCP, and generated Rust, Go, TypeScript, and Python methods. The compiler seals optional-family members, comparison profiles, policy mode, exact-count and ordinal requirements, independent order layouts, and static work/state bounds into its provider identity. Existing V1–V8 source, plan, module, and V1 through V3 provider identities remain byte-exact.
Explicit nullable placement selects the additive V7/V10/V10 identities even when the current field is required. Sources without placement continue to emit V6/V9/V9 when the complete present/non-null proof succeeds; earlier bytes and hashes are not reinterpreted.
For a successor contract, compilation that omits placement must receive the
complete contiguous lineage beginning at version 1. A current bundle by
itself, an out-of-order lineage, or a lineage with a version gap is not proof
and fails closed with a diagnostic suggesting nulls first or nulls last.
V4 builds one policy-aligned partition in the background. Equality, range, set, state, and exact-text postings combine inside that provider. Each declared order has its own bounded ordinal index; exact count and offset selection use bitset rank/select rather than walking skipped rows or pages. Bounded row-level policy admission happens before a row can enter the provider universe, so a denied row cannot affect complements, counts, ordering, ordinals, diagnostics, or lifecycle selection.
V5 uses the same partition-scoped lifecycle and admission boundary for nullable
orders. It stores missing and explicit null in one NoValue state class, with
the compiler-selected first/last rank, and stores present values under the
existing canonical scalar comparison. Optional text and timestamp orders are
covered by the same state-aware index. Every V5 checkpoint binds the compiled
program, descriptor, policy shape, partition, history incarnation, generation,
frontier, and checksum; V4 refuses V5 bytes rather than reinterpreting them.
Descriptor/layout validation is paid when a provider generation is bound, and
the epoch proof is paid once for the opened result set—not once per row, term,
probe, or returned item.
Callers submit only the generated typed values for one named operation. They do not submit a predicate, field, operator, order, provider, plan member, policy, or cost. The service materializes and type-checks values before authorization, selects the compiler-enumerated optional-presence member, and reauthorizes both before provider execution and before releasing values. A V6 query never degrades to an authoritative request-time scan, client-side filter or sort, page walk, materialized full population, cross-provider identifier transfer, or a nearby older provider.
The initial request for a new plan/policy/partition slot waits for a fixed,
bounded readiness window while background construction remains the sole owner
of authoritative scans and provider publication. It can still return
RDB-QUERY-0102 when construction does not finish within that window. The same
typed lifecycle, freshness (RDB-PROJECTION-0103), retired-snapshot,
divergence, and revocation failures cross every public transport. The
application may use a bounded retry budget; it may not emulate the result while
the provider is unavailable.
Healthy binary-text and exact-predicate providers replay retained V3 receipt post-images in bounded passes of at most 64 application commits and one source byte page. Missing receipt history, oversized indivisible groups, and changes to indexed policy dependencies can require a complete snapshot rebuild. Attached followers currently use that rebuild path because they retain applied progress without the primary’s receipt history. Provider checkpoints keep their existing complete encoding and validation.
Background rebuilds read rows, catalog evidence, and row-policy evidence from one captured authoritative view. A healthy prior provider stays selected while its replacement is prepared, and the replacement checkpoint is reopened and validated before selection. The provider reports its actual captured frontier; Latest requests still require that frontier to equal their captured application head and may receive a typed freshness refusal while it lags.
Numeric offsets are bounded direct ordinal selections inside one provider epoch. Use generated cursor pagination instead when a user journey spans multiple requests and must retain snapshot identity across concurrent writes; do not turn successive numeric offsets into a client-side page walk.
The immutable generic capability receipt is
fixtures/riffql/operational-query-capability-v5.json. It preserves V1
through V4 byte-for-byte, so the V6/V7 exact-result plans, generated-language
artifacts, and strict TypeScript helper-reachability gate retain their prior
evidence. V5 adds binary UTF-8 interval/complement operations to the separately
governed ordinary access-path corpus and binds one value-free external source-
compilation and Go-only package receipt. It does not claim an external runtime,
framework route, or full application has passed: that evidence remains the
responsibility of the repository owning the adapter.
The separately checksum-bound
fixtures/riffql/wp775-external-binary-text-interval-execution-v1.json is
terminal evidence for one much narrower external execution. It records that
the owning consumer invoked one compiler-generated strict-lower/strict-upper
binary-text query twice, received original logical strings in bytewise
ascending order, and reused one returned string as its consumer-owned next
lower endpoint. Its closed value-free facts exclude client shaping, a duplicate
index, database-owned token conversion, and framework or full-application
claims. It does not alter or advance V5, and neither external values nor
consumer vocabulary enter this repository.