Skip to content

Commit c2dbbd3

Browse files
committed
fix(data): resolve strict schema for AS OF SYSTEM TIME NULL predicates
AS OF SYSTEM TIME NULL (all-versions audit) with a WHERE predicate on a document_strict collection returned zero rows: the audit fetch matched the predicate against the raw stored Binary Tuple body with a MessagePack-only matcher, which never matches a Binary Tuple. The fetch now resolves the strict schema before matching, mirroring the AS OF <cutoff> arm. Fixes #170.
1 parent e84ff9a commit c2dbbd3

2 files changed

Lines changed: 69 additions & 2 deletions

File tree

nodedb/src/data/executor/handlers/document/read/fetch.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@ impl CoreLoop {
129129
// normalized to MessagePack and gets the synthetic `_ts_*`
130130
// temporal columns injected BEFORE the shared downstream runs,
131131
// so a user can `SELECT` / `ORDER BY` / project on them.
132-
let predicate =
133-
|body: &[u8]| filter_predicates.iter().all(|f| f.matches_binary(body));
132+
let predicate = |body: &[u8]| {
133+
matches_with_resolved_schema(strict_schema, filter_predicates, body)
134+
};
134135
let scan_limit = offset.saturating_add(limit);
135136
let raw = self.sparse.versioned_scan_all(
136137
task.request.database_id.as_u64(),

nodedb/tests/bitemporal_asof_scan_parity.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,72 @@ async fn asof_audit_null_honours_order_by() {
167167
);
168168
}
169169

170+
/// Regression for issue #170: `AS OF SYSTEM TIME NULL` (all-versions audit) with
171+
/// a `WHERE` predicate on a strict (`document_strict`) collection returned zero
172+
/// rows. The audit fetch applied the predicate against the raw stored Binary
173+
/// Tuple body via a MessagePack-only matcher (`matches_binary`), which never
174+
/// matches a Binary Tuple — so every predicated audit query silently dropped
175+
/// every version. The fetch now resolves the strict schema before matching
176+
/// (`matches_with_resolved_schema`), mirroring the `AS OF <cutoff>` arm.
177+
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
178+
async fn asof_audit_null_strict_filters_by_predicate() {
179+
let srv = TestServer::start().await;
180+
181+
srv.exec(
182+
"CREATE COLLECTION btp (id STRING PRIMARY KEY, v STRING) \
183+
WITH (engine='document_strict', bitemporal=true)",
184+
)
185+
.await
186+
.expect("create strict bitemporal collection");
187+
188+
srv.exec("INSERT INTO btp (id, v) VALUES ('a', 'one')")
189+
.await
190+
.expect("insert a=one");
191+
srv.exec("UPDATE btp SET v = 'two' WHERE id = 'a'")
192+
.await
193+
.expect("update a=two");
194+
srv.exec("INSERT INTO btp (id, v) VALUES ('b', 'other')")
195+
.await
196+
.expect("insert b=other");
197+
198+
// Baseline: unfiltered audit returns every version — 'a' has two (one, two)
199+
// and 'b' has one, for three rows total.
200+
let all = srv
201+
.query_rows("SELECT * FROM btp AS OF SYSTEM TIME NULL")
202+
.await
203+
.expect("unfiltered audit scan");
204+
assert_eq!(
205+
all.len(),
206+
3,
207+
"audit must return all three versions (a x2, b x1), got: {all:?}"
208+
);
209+
210+
// Predicated audit: WHERE id='a' must return both versions of 'a', each with
211+
// id='a' and a real synthetic `_ts_system` column. Pre-fix this returned 0.
212+
let filtered = srv
213+
.query_named_rows("SELECT * FROM btp AS OF SYSTEM TIME NULL WHERE id = 'a'")
214+
.await
215+
.expect("predicated audit scan");
216+
assert_eq!(
217+
filtered.len(),
218+
2,
219+
"WHERE id='a' must return both versions of 'a' (pre-fix: 0), got: {filtered:?}"
220+
);
221+
for row in &filtered {
222+
assert_eq!(
223+
row.get("id").map(String::as_str),
224+
Some("a"),
225+
"every filtered audit row must be id='a', got: {row:?}"
226+
);
227+
assert!(
228+
row.get("_ts_system")
229+
.and_then(|s| s.parse::<i64>().ok())
230+
.is_some(),
231+
"each audit row must carry a real `_ts_system` column, got: {row:?}"
232+
);
233+
}
234+
}
235+
170236
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
171237
async fn asof_schemaless_orders_and_computes_like_current() {
172238
let srv = TestServer::start().await;

0 commit comments

Comments
 (0)