makepad/libs/sqlite_query/tests/zz_antijoin.rs
Admin 2522a19e4f libs: sqlite WITHOUT ROWID reads, an in-repo tar reader, packed git imports, a Haskell lexer, and the AI crate warning cleanup
libs/sqlite_query reads WITHOUT ROWID tables through the index cursor,
refuses to write them, and the pager releases its process write slot on
drop. libs/tar is an in-repo tar reader with gzip through fast_inflate.
libs/git imports packed objects with bounded checkout writers and its
HTTP file responses carry a trailer. libs/code_language gains a Haskell lexer with literate (Bird) dialect
detection. libs/loader_bundle and libs/search are new;
libs/app_module carries the super-app module surface; libs/workspace
adds Settings.infinite_zoom and RendererChoice::gpu_env_value; libs/ai
builds without warnings across the hub, llm, metal and model crates;
windows-rs job object handles are c_void.

Squashed from work (the libs parts of each):
- Restore retained renderer support for Scope (libs/search)
- Share Builder target across Makepad app builds (libs/loader_bundle)
- Index local Apple Mail with Gmail labels, attachments and reimport (libs/sqlite_query)
- libs/ai: warning cleanup across the hub, llm, metal and model crates
- code_language: a Haskell lexer with literate (Bird) dialect detection
- git: packed imports and bounded checkout writers; HTTP file responses carry a trailer
- builder: dedicated-folder installer checks, catalog and runtime fixes; Windows job objects hold c_void handles (libs/windows)
- workspace: Settings.infinite_zoom, the experimental prepared map inside the glyph
- wm: the Android super-app hosts apps as on-demand dylibs (libs/app_module)
- libs/tar: an in-repo tar reader; the super-app unpacks its archives with it
- workspace: RendererChoice::gpu_env_value follows the platform's runtime GPU choice

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:15:55 +02:00

52 lines
2.3 KiB
Rust

//! Does the engine answer an anti-join the way sqlite does, at real size?
mod common;
use common::*;
use makepad_sqlite::{Database, Value};
/// 61k items, the first 2k of them carrying ~20 terms each — the exact shape
/// the picture cache was in when the engine answered "nothing left to index".
fn setup(n_items: usize, n_indexed: usize) -> String {
let mut s = String::from(
"CREATE TABLE items(id INTEGER PRIMARY KEY, title TEXT NOT NULL, extra TEXT NOT NULL);
CREATE TABLE terms(term TEXT NOT NULL, item INTEGER NOT NULL, UNIQUE(term, item));
CREATE INDEX terms_term ON terms(term, item);
CREATE INDEX terms_item ON terms(item);
BEGIN;",
);
for i in 1..=n_items {
s.push_str(&format!("INSERT INTO items(id,title,extra) VALUES({i},'t{i}','{{}}');"));
}
for i in 1..=n_indexed {
for w in 0..20 {
s.push_str(&format!("INSERT INTO terms(term,item) VALUES('w{}',{i});", (i * 31 + w) % 5000));
}
}
s.push_str("COMMIT;");
s
}
#[test]
fn the_unindexed_rows_are_found_at_real_size() {
if !have_sqlite3() { eprintln!("no sqlite3 CLI; skipping"); return }
let s = Scratch::new("antijoin-big");
// Inside the scratch directory, so a second run never meets the first
// run's file.
let db = build_db(&s.path(""), "big2.sqlite", &setup(61_000, 2_000));
let sql = "SELECT COUNT(*) FROM items i LEFT JOIN terms t ON t.item = i.id WHERE t.item IS NULL";
let theirs = sqlite3_column(&db, sql);
let mut d = Database::open(&db).unwrap();
let mine: Vec<Value> = d.query(sql, &[]).unwrap().rows.iter().map(|r| r[0].clone()).collect();
assert_eq!(mine, theirs, "\nsqlite3 said {theirs:?}\nours said {mine:?}");
// And the actual batch query the indexer used.
let batch = "SELECT i.id, i.title, i.extra FROM items i LEFT JOIN terms t ON t.item = i.id \
WHERE t.item IS NULL LIMIT 2000";
let theirs_n = sqlite3(&db, batch).lines().count();
let mine_n = d
.query("SELECT i.id, i.title, i.extra FROM items i LEFT JOIN terms t ON t.item = i.id WHERE t.item IS NULL LIMIT ?", &[Value::Integer(2000)])
.unwrap()
.rows
.len();
assert_eq!(mine_n, theirs_n, "batch query: sqlite3 gave {theirs_n} rows, ours gave {mine_n}");
}