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>
52 lines
2.3 KiB
Rust
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}");
|
|
}
|