// Date-truncation range rewrite: a WHERE conjunct of the shape // date(<var>.<key>) = <constant Date> tests day membership, which over an // epoch-millis integer column is exactly the half-open range // [day, day + 24h) on the RAW value. Rewriting the conjunct to two // property-vs-constant comparisons lets the pushdown machinery place and // batch-sweep them like any other integer filter, instead of evaluating a // temporal constructor per row -- the dominant filter shape in date-heavy // analytic queries. The rewrite fires on generic structure (a resolved // date() call over an integer-or-absent column against a constant Date), // never on query identity.
package exec import ( chickpeas ) // dateRangeRewrites counts fired rewrites -- the engagement oracle: a // date(prop) = const conjunct over an integer column must rewrite exactly // once (two range conjuncts replace it); 0 on a qualifying shape means // the rewrite is dead. var dateRangeRewrites int // disableDateRangeRewrite pins result identity in tests: the general // per-row evaluation must produce exactly the rows the rewrite does. var disableDateRangeRewrite bool // rewriteDateEqRanges maps each qualifying conjunct to its two range // conjuncts, leaving everything else untouched. func rewriteDateEqRanges( *eval.Ctx, []ast.Expr, map[string]int) []ast.Expr { if disableDateRangeRewrite { return } , := .G.(graph.Native) if ! { return } := .Snapshot() := make([]ast.Expr, 0, len()) for , := range { if , , := dateEqRange(, , , ); { = append(, , ) continue } = append(, ) } return } // dateEqRange matches date(<prop>) = <const Date> (either operand order) // and returns the equivalent raw-value range conjuncts. The property's // resolvable columns must read integer-or-absent -- an integer value // floors to the day exactly as the range tests it, an absent value nulls // out of both forms identically, and any other column type keeps the // general evaluation. func dateEqRange( *eval.Ctx, *chickpeas.Snapshot, ast.Expr, map[string]int) (, ast.Expr, bool) { , := .(*ast.Binary) if ! || .Op != ast.OpEq { return nil, nil, false } , , := datePropAndConst(.LHS, .RHS) if ! { , , = datePropAndConst(.RHS, .LHS) } if ! || !i64OrAbsentColumns(, .Key) { return nil, nil, false } , := compile.ConstValue(, , , ) if ! { return nil, nil, false } , , := .AsTemporal() if ! || != value.Date { return nil, nil, false } // The constant must sit exactly at midnight: date() output always // does, but Date-kind values built by temporal arithmetic // (date(...) + duration({hours: 5})) need not -- and for those the // TRUE predicate is unsatisfiable (a truncated value never equals a // non-aligned one) while the range form would select a day of rows // starting at that time. Kind and constness both pass such a value; // only alignment catches it (rustychickpeas' eefe831 gate). if %eval.MSPerDay != 0 { return nil, nil, false } dateRangeRewrites++ return &ast.Binary{Op: ast.OpGte, LHS: , RHS: &ast.Lit{Value: ast.IntLit()}}, &ast.Binary{Op: ast.OpLt, LHS: , RHS: &ast.Lit{Value: ast.IntLit( + eval.MSPerDay)}}, true } // datePropAndConst matches one operand orientation: a resolved date() // call over a bare property read, paired with the other side as the // constant candidate. func datePropAndConst(, ast.Expr) (*ast.Prop, ast.Expr, bool) { , := .(*ast.Func) if ! || .Distinct || .Star || len(.Args) != 1 { return nil, nil, false } if , := eval.ResolveFuncOp(.Name); ! || != eval.FuncDate { return nil, nil, false } , := .Args[0].(*ast.Prop) if ! { return nil, nil, false } return , , true } // i64OrAbsentColumns reports whether every column that could serve a read // of key yields an integer or absent: a missing column reads absent for // every position, and an integer column is exactly the epoch-millis shape // date() floors. A float, boolean, or string column declines the rewrite. func i64OrAbsentColumns( *chickpeas.Snapshot, string) bool { if , := .ColIndexed(); && .Dtype() != chickpeas.DtypeI64 { return false } if , := .RelColIndexed(); && .Dtype() != chickpeas.DtypeI64 { return false } return true }