Documentation
¶
Overview ¶
Package rowdiff is the RFC-182 generative row-soundness differential harness: seeded random (schema, data, query) cases executed through the full production path (sqldriver → Cascades → executor → real FDB), rows diffed against Oracle M — a brute-force in-memory full-scan evaluation over the generator's own authoritative row set, reusing the engine's predicate evaluation (predicates.Comparison.Eval) so the planner is the only component removed. Any mismatch is a plan-soundness finding.
The generator deliberately over-weights the matrix cell that hid the pk-intersection residual-drop bug (audit 2026-07-18): two or more indexed columns bound PLUS an unindexed residual, executed under every projection variant (`SELECT *` vs narrow — projection flips cost winners and masked that bug).
Index ¶
- func AggResultOverflows(rows []Row) bool
- func BigintColsForTest(t TableDef) []string
- func CheckTemplateFamily(tpl Template) error
- func IsNestedPath(entry string) bool
- func JoinQualifiers() []string
- func NotNullSortColForTest(t TableDef) string
- func NullableSortColsForTest(t TableDef) []string
- func NumericColsForTest(t TableDef) []string
- func OutputAlias(col string) string
- func PathSegments(entry string) int
- func PredicateReadsANestedPath(n *BoolNode) bool
- func PredicateSQL(n *BoolNode) string
- func StripJoinQualifier(entry string) string
- type AggFunc
- type AggSpec
- type BoolNode
- type Case
- type CaseSpec
- type CastSpec
- type ColType
- type ColumnDef
- type DerivedSpec
- type ExistsSpec
- type IndexDef
- type JoinSpec
- type Mismatch
- type NullsPlacement
- type NumFnKind
- type NumFnSpec
- type OrderKey
- type OutcomeKind
- type Pred
- type Query
- type Row
- type ScalarSubSpec
- type SeedResult
- func RunCase(ctx context.Context, setupDB *sql.DB, dbPath, clusterFile string, c *Case, ...) *SeedResult
- func RunSeed(ctx context.Context, setupDB *sql.DB, dbPath, clusterFile string, seed uint64) *SeedResult
- func RunSeedPaged(ctx context.Context, setupDB *sql.DB, dbPath, clusterFile string, seed uint64, ...) *SeedResult
- type StrFnKind
- type StrFnSpec
- type StructCol
- type StructField
- type TableDef
- type Template
- type ThreeWayJoinSpec
- type UnionSpec
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AggResultOverflows ¶
AggResultOverflows reports whether the oracle's expected result for this query is an int64 SUM overflow — in which case the engine's 22003 is the CORRECT outcome and any row it returned instead would be the finding.
func BigintColsForTest ¶
The pool accessors below exist for the flat-equivalence pin in nested_test.go, which is an external test package: the pins have to read the same functions the generator draws from, or they pin a copy.
func CheckTemplateFamily ¶
CheckTemplateFamily asserts the template's single query plans into its required family via the typed plan tree. Returns nil on success.
func IsNestedPath ¶
IsNestedPath reports whether a projection entry reaches INTO a struct.
This is NOT the same question as "does the entry contain a dot", and the difference is the whole reason the helper exists. A JOIN projection entry is ALIAS-QUALIFIED, so its first dot is the table qualifier: the keys-only projection {"L.ID", "R.ID"} is entirely FLAT while every entry in it is dotted. Reading the dot alone counts every join projection as nested, which reports a nesting axis as covered by scenarios that carry no nesting at all — a coverage claim about the empty set, which is the failure mode this corpus's whole census exists to make impossible.
func JoinQualifiers ¶
func JoinQualifiers() []string
JoinQualifiers are the table-alias qualifiers a JOIN projection entry can carry: joinProjections mints L and R, threeWayProjections mints L, M and R.
The list lives beside the strip helper rather than at each reader because a second copy is how the readers drifted apart in the first place — three sites each decided "is this nested?" for themselves and all three decided it wrong in the same way.
func NotNullSortColForTest ¶
func NullableSortColsForTest ¶
func NumericColsForTest ¶
func OutputAlias ¶
OutputAlias is the UNIQUE output column name a projected column is given.
A nested projection is always aliased, and that is a correctness requirement of the harness rather than a style choice: `SELECT a, n.a FROM t` comes back from the engine labelled `[A, A]` — legal SQL, and Java's yaml-tests dialect keys result rows BY COLUMN NAME, so the two columns collapse into one and the comparison silently stops checking half the projection. The join projections alias for exactly this reason; a nested leaf colliding with a flat column of the same name is the same hazard reached one level down, and it is the shape a wrong-column read has already hidden in.
A flat column is its own alias, so nothing about the flat corpus changes.
func PathSegments ¶
PathSegments counts a projection entry's segments BELOW its table: 1 for a flat column, 2 for `N.A`, 3 for `N.DP.A` — with any join qualifier stripped first, so `L.N.A` counts 2 and not 3.
func PredicateReadsANestedPath ¶
PredicateReadsANestedPath reports whether any leaf of a predicate tree reads a column that is a struct path.
It walks the typed spec rather than the rendered SQL. The rendering-based form it replaces asked `strings.Contains(PredicateSQL(where), "n.")`, which is a text match on generated SQL: it happens to be right over today's domain (every qualifier is l/m/r, the only struct root is N, and no literal in the domain contains a dot) and it becomes wrong the first time a LIKE pattern, a CONCAT suffix or a new alias grows a dot. The spec struct already carries every column the leaf reads, so asking it is both cheaper and exact.
func PredicateSQL ¶
PredicateSQL renders a boolean predicate tree as a SQL expression, the same rendering `Case.SQL` puts in the WHERE clause. Exported so the RFC-201 §5.5 ternary-logic-partitioning oracle can build the three branches of the partition — `p`, `NOT (p)`, `(p) IS NULL` — from the one tree that produced the query under test, rather than re-deriving the predicate from SQL text. A nil tree renders as the empty string.
func StripJoinQualifier ¶
StripJoinQualifier removes a leading table-alias qualifier from a projection entry, yielding the column path RELATIVE TO ITS OWN TABLE. "L.N.A" becomes "N.A"; "L.ID" becomes "ID"; an unqualified entry is returned unchanged.
No table this generator builds has a column named L, M or R, so the strip is unambiguous — and TestJoinQualifiersAreNotColumnNames measures that rather than trusting it, because the day a schema grows an `R` column this helper starts silently eating a real column's first segment.
Types ¶
type AggFunc ¶
type AggFunc int
AggFunc is a supported aggregate. AVG is deliberately absent: its result type and rounding are their own semantic axis (integer vs floating division), and getting that wrong in the oracle would produce false findings rather than real ones.
type AggSpec ¶
type AggSpec struct {
Func AggFunc
Col string // aggregated column ("" for COUNT(*))
GroupBy []string // empty = scalar aggregate over the whole input; 1+ = grouped
Having *Pred // optional filter on the aggregate result
HavingOn bool
}
AggSpec is one aggregate query: optional GROUP BY key plus one aggregate over a BIGINT column.
HONESTY NOTE (RFC-182 §7 / §12): unlike every other oracle path, the aggregate evaluation here is a REIMPLEMENTATION — aggregation is not expressible through the engine's per-row Comparison evaluation, so this is the one place the oracle restates SQL semantics rather than sharing the engine's. The restated rules are the well-defined ones (SUM/MIN/MAX ignore NULLs and yield NULL for an all-NULL or empty group; COUNT(*) counts rows; COUNT(col) counts non-NULLs; a NULL grouping key is its own group), and a divergence here is investigated on BOTH sides before it is called an engine bug.
type BoolNode ¶
BoolNode is the AND/OR tree over leaves. Exactly one of Leaf / Kids is set. Not negates the whole node (Kleene NOT: NOT UNKNOWN stays UNKNOWN).
type Case ¶
type Case struct {
Seed uint64
Table TableDef
Rows []Row
Queries []Query
// Nested marks a case built by GenerateNested: its table carries a struct
// column and its projections name dotted paths.
Nested bool
}
Case is everything one seed produces.
func GenerateNested ¶
GenerateNested builds a NESTED Case for a seed: the same query mix as Generate, over a table whose columns are partly leaves of a struct column.
The query generators are shared verbatim. They read `ColumnDef.Name` and never parse it, so a dotted name flows through predicate leaves, ORDER BY keys, aggregate arguments, join keys, EXISTS correlations and scalar subqueries with no change — which is the point. Deciding per construct which ones "support" nesting is precisely the blind spot that let nested defects ship: the clauses diverged from each other, so the generator must reach them all and let the ENGINE decide what it can answer.
func (*Case) Projections ¶
Projections returns the projection variants every query runs under: star, pk-only, a narrow subset, and the narrow subset reversed.
func (*Case) ProjectionsFor ¶
ProjectionsFor returns the projection variants appropriate to the query (join queries never use `SELECT *` — see JoinSpec; an aggregate's output list is fixed by its own shape).
type CaseSpec ¶
type CaseSpec struct {
When *Pred // WHEN condition (col op literal; Qual empty)
ThenCol string // THEN reads this column; "" ⇒ ThenLit
ThenLit int64
ElseCol string // ELSE reads this column; "" ⇒ ElseLit
ElseLit int64
}
CaseSpec is a searched CASE used as a predicate LHS:
(CASE WHEN <When> THEN <then-arm> ELSE <else-arm> END) <Op> <Lit>
A single WHEN (the common shape). Branch selection is SQL's: the WHEN arm is taken only when its condition is TRUE — a FALSE or UNKNOWN (NULL-operand) condition falls through to ELSE. Each arm is a BIGINT column or literal; a selected NULL column makes the CASE result NULL, so the outer comparison is UNKNOWN. The condition and the outer comparison both go through the shared Comparison eval, so only the trivial branch-pick is restated in the oracle.
type CastSpec ¶
type CastSpec struct {
Col string // BIGINT or BOOLEAN column
FromInt bool // true = BIGINT source (FormatInt); false = BOOLEAN source ("true"/"false")
}
CastSpec is a `CAST(Col AS STRING) <Op> Lit` predicate LHS over a BIGINT or BOOLEAN column. Only the STRING target is generated: int→string is Go's strconv.FormatInt and bool→string is "true"/"false" (both verified to match the engine over the domain). CAST(string AS BIGINT) is excluded — it raises a plan-order-dependent parse error on non-numeric input, which the full-scan oracle cannot model (the arithmetic-overflow constraint).
type ColType ¶
type ColType int
ColType is the P1 column-type universe. DOUBLE and FLOAT close RFC-182 OQ-3: no generator in this harness could previously emit a floating-point column at all, which is exactly the blind spot that let an indexed FLOAT column matching NOTHING for any predicate (and a cross-type numeric comparison against an index building the wrong tuple-type range) ship undetected. LIKE/IN/BETWEEN arrive with P2.
type DerivedSpec ¶
type DerivedSpec struct {
Inner *BoolNode // WHERE inside the subquery
Outer *BoolNode // WHERE over the derived table (nil = none)
// Cte renders the subquery as a single-reference common table expression
// (`WITH d AS (…) SELECT … FROM d …`) instead of an inline derived table.
// A single-reference CTE is semantically identical (same flattening, same
// oracle) but exercises the WITH scope-resolution path.
Cte bool
}
DerivedSpec is a subquery in FROM (a derived table):
SELECT <proj> FROM (SELECT * FROM t WHERE <Inner>) d [WHERE <Outer>]
The inner is SELECT * so the derived table is a pass-through equal to the flattened `WHERE Inner AND Outer` — which is exactly what it tests: whether the planner FLATTENS the derived table and pushes the outer predicate through the subquery scope. A flattening or scope-resolution bug is a wrong-rows divergence. Outer references the derived columns unqualified (d is the only source).
type ExistsSpec ¶
type ExistsSpec struct {
CorrCol string // correlation column: r.CorrCol <CorrOp> t.CorrCol
CorrOp predicates.ComparisonType // correlation operator (zero value = equals)
Inner *Pred // optional extra filter on r (Qual empty; col op literal only)
Negated bool // NOT EXISTS
}
ExistsSpec is a CORRELATED [NOT] EXISTS subquery appended to a single-table query's WHERE, over the SAME table under alias `r`:
[NOT] EXISTS (SELECT 1 FROM t AS r WHERE r.<CorrCol> <CorrOp> t.<CorrCol> [AND <Inner>])
The correlation equality and the optional simple inner comparison go through the engine's own Comparison eval in the oracle (evalLeaf / EvalAgainst), so scalar/NULL semantics are shared by construction — a `r.c = t.c` with a NULL on either side is UNKNOWN, so a NULL correlation value matches no inner row. What is under test is the PLANNER's correlated-EXISTS handling (semi-join, decorrelation, correlation binding), and — since EXISTS is a WHERE filter with no output-schema change — it composes with the paging sweep to test EXISTS continuation soundness too.
type JoinSpec ¶
type JoinSpec struct {
LeftCol string
RightCol string
// Inner renders `JOIN … ON …`; when false the join is expressed as a
// comma cross-join with the equality moved into the WHERE — the same
// logical query, a different parse path into the planner.
Inner bool
// LeftOuter makes the ON-join a LEFT OUTER JOIN: unmatched left rows are
// kept, NULL-extended on the right. Implies Inner (LEFT OUTER needs the ON
// form — a comma join has no outer semantics). This reaches the
// NULL-extension wrong-rows surface INNER never does, and pins the
// WHERE-vs-ON subtlety: an R-side filter in the WHERE drops the
// NULL-extended rows (r.col is NULL → predicate UNKNOWN), collapsing a
// LEFT JOIN back toward inner semantics — the oracle applies the WHERE
// post-join over the NULL-extended row, exactly as SQL does.
LeftOuter bool
// RightOuter makes the ON-join a RIGHT OUTER JOIN: the mirror of LeftOuter,
// preserving every RIGHT row and NULL-extending unmatched LEFT. Implies
// Inner and is mutually exclusive with LeftOuter. `A RIGHT JOIN B` is
// semantically `B LEFT JOIN A`, so this exercises the engine's RIGHT→LEFT
// normalization — a rewrite bug there is a wrong-rows divergence.
RightOuter bool
}
JoinSpec is a SELF-join of the case's table under aliases L and R, joined on `L.<LeftCol> = R.<RightCol>`. A self-join exercises the whole join planner (NLJ, join ordering, correlated index access) without needing a second table, and keeps the oracle a plain nested loop over one row set.
Join queries always project EXPLICITLY ALIASED columns (l_id, r_a, …): unaliased `l.id, r.id` would yield two output columns both named ID, and the harness keys rows by column name, so the duplicate would collapse and silently weaken the comparison.
type Mismatch ¶
type Mismatch struct {
Seed uint64
DDL string
InsertSQL string
SQL string
EngineRows []Row
OracleRows []Row
Detail string
}
Mismatch is one confirmed row divergence, carrying everything needed to reproduce and pin it: the failure output IS the ready-to-paste scenario.
type NullsPlacement ¶
type NullsPlacement int
NullsPlacement is an ORDER BY key's NULL position. Default follows the engine's Java/FDB-parity rule: NULLS FIRST ascending, NULLS LAST descending (tuple order). Explicit placements render as NULLS FIRST/LAST.
const ( NullsDefault NullsPlacement = iota NullsFirst NullsLast )
type NumFnKind ¶
type NumFnKind int
NumFnKind is a scalar numeric function whose semantics match Go's over the generator's BIGINT domain (verified by probe): ABS never overflows (no MinInt64 in {-1, 2^62, 0..9}), and MOD follows the dividend's sign exactly as Go's % does. MOD's divisor is always a nonzero literal, so there is no division-by-zero path.
type NumFnSpec ¶
type NumFnSpec struct {
Fn NumFnKind
Col string // BIGINT column
Mod int64 // nonzero divisor for MOD (unused otherwise)
Default int64 // COALESCE fallback when the column is NULL (never NULL itself)
}
NumFnSpec is a numeric-function predicate LHS: `ABS(col) <Op> Lit`, `MOD(col, Mod) <Op> Lit`, or `COALESCE(col, Default) <Op> Lit`.
type OrderKey ¶
type OrderKey struct {
Col string
Desc bool
Nulls NullsPlacement
// Qual is the table-alias qualifier in a JOIN query ("L"/"R"), empty
// otherwise.
Qual string
}
OrderKey is one ORDER BY component. Nullable sort keys are allowed; keys are always suffixed with ID by the generator so the total order stays unique and the ordered comparator exact.
type OutcomeKind ¶
type OutcomeKind int
OutcomeKind is the RFC-182 §4 typed classification. A seed resolves to exactly one kind; INFRA never masquerades as a soundness finding.
const ( OutcomeOK OutcomeKind = iota OutcomeMismatch OutcomeInfra )
type Pred ¶
type Pred struct {
Col string
Op predicates.ComparisonType
Lit any // nil for IS NULL / IS NOT NULL; the pattern for LIKE; lo for BETWEEN
// InList holds the membership list for Op == ComparisonIn.
InList []any
// BetweenHi holds the inclusive upper bound when IsBetween (rendered as
// BETWEEN; the oracle evaluates it as >=Lit AND <=BetweenHi with the
// engine's own comparisons, which is the SQL desugaring).
BetweenHi any
IsBetween bool
// RhsCol, when non-empty, makes this a COLUMN-vs-COLUMN comparison
// (`a < b`) instead of column-vs-literal. Non-sargable, so it forces
// residual filters and different plan shapes than a literal comparison.
RhsCol string
// Qual / RhsQual are the table-alias qualifiers in a JOIN query ("L" or
// "R"); empty in single-table queries. The oracle keys joined rows by
// "<QUAL>.<COL>", so these select the side each operand reads.
Qual string
RhsQual string
// HasArith makes the leaf's LHS an arithmetic expression `Col <ArithOp>
// ArithCol2` (both read from the leaf's own Qual side) instead of a bare
// column: `(a - b) <Op> Lit`. Only subtraction is generated — over the
// value domain ({-1, 2^62, 0..9}) |a-b| < 2^63, so it never overflows and
// there is no 22003 error path. A NULL in either operand propagates to a
// NULL LHS, so the comparison is UNKNOWN.
HasArith bool
ArithOp values.ArithmeticOp
ArithCol2 string
// Bitwise makes the leaf's LHS a bitwise expression `(Col <op> BitCol2)`
// (op ∈ &,|,^; BitOp names the engine function BITAND/BITOR/BITXOR) instead
// of a bare column: `(a & b) <Op> Lit`. Bitwise ops never overflow (unlike
// +/*), so the value is order-independent and oracle-safe. A NULL in either
// operand yields a NULL LHS → the comparison is UNKNOWN. The oracle folds it
// through the engine's own ScalarFunctionValue, so the bitwise/NULL
// semantics are shared — the leaf tests the PLANNER's handling of a bitwise
// predicate expression (pushdown, index residual), not the eval.
Bitwise bool
BitOp string // "BITAND" | "BITOR" | "BITXOR"
BitCol2 string
// Case, when non-nil, makes the leaf's LHS a searched CASE expression
// `(CASE WHEN … THEN … ELSE … END) <Op> Lit` instead of a bare column.
// Single-table only (unqualified columns).
Case *CaseSpec
// StrFn, when non-nil, makes the leaf's LHS a string-function call
// `<Fn>(<StrCol>) <Op> Lit` (UPPER/LOWER vs a string Lit, LENGTH vs an int
// Lit). Single-table only. A NULL column makes the result NULL → UNKNOWN.
StrFn *StrFnSpec
// NumFn, when non-nil, makes the leaf's LHS a numeric-function call
// `ABS(col) <Op> Lit` or `MOD(col, k) <Op> Lit`. Single-table only. A NULL
// column makes the result NULL → UNKNOWN.
NumFn *NumFnSpec
// Cast, when non-nil, makes the leaf's LHS a `CAST(col AS STRING) <Op> Lit`
// over a BIGINT or BOOLEAN column. Single-table only. A NULL column makes
// the result NULL → UNKNOWN.
Cast *CastSpec
// Negated renders `NOT (…)` around the leaf (and `NOT IN` for IN).
Negated bool
}
Pred is one comparison leaf: COL <op> literal, COL IS [NOT] NULL, COL IN (…), COL BETWEEN lo AND hi, or COL LIKE pattern.
type Query ¶
type Query struct {
Agg *AggSpec // nil = not an aggregate query
Join *JoinSpec // nil = single-table query
ThreeWay *ThreeWayJoinSpec // non-nil = 3-way self-join
Union *UnionSpec // non-nil = UNION [ALL] of two single-table branches
Derived *DerivedSpec // non-nil = subquery in FROM (derived table)
Exists *ExistsSpec // non-nil = append a correlated [NOT] EXISTS to WHERE
ScalarSub *ScalarSubSpec // non-nil = append a scalar-subquery comparison to WHERE
Where *BoolNode // nil = no WHERE
OrderBy []OrderKey
Limit int // 0 = no LIMIT
Offset int // 0 = no OFFSET (only emitted alongside LIMIT + ORDER BY)
Distinct bool // SELECT DISTINCT (ORDER BY keys ⊆ projection enforced by generator)
// WhereOverride, when non-nil, REPLACES the rendered WHERE conjunct list
// with this literal SQL expression; the empty string means "no WHERE at
// all". It exists for the RFC-201 §5.5 ternary-logic-partitioning oracle,
// which needs `WHERE NOT (p)` and `WHERE (p) IS NULL` rendered from the
// SAME predicate tree as `WHERE p` — forms no BoolNode can express (Kleene
// NOT is not SQL NOT, and "the predicate evaluated to UNKNOWN" is not a
// boolean node at all).
//
// Oracle M cannot evaluate an overridden query: the override is opaque SQL
// and the oracle walks the typed tree. OracleRows therefore REFUSES such a
// query with an error rather than silently evaluating q.Where and reporting
// a divergence that is really a harness bug. The TLP oracle needs no Oracle
// M — its property is the partition across the four renderings.
WhereOverride *string
}
Query is one generated query body; the runner executes it under every projection variant and cross-checks row identity via ID.
type Row ¶
Row maps UPPER-CASE column name (incl. "ID") to a driver-typed value: int64, string, bool, or nil.
func OracleRows ¶
OracleRows evaluates a query naively over the case's authoritative rows: full scan → predicate evaluation → ORDER BY → projection. No planner, no indexes. Leaf comparisons reuse the ENGINE's evaluation (predicates.NewLiteralComparison(...).Eval) so scalar/NULL semantics are shared by construction and the planner is the only component under test (RFC-182 §3). AND/OR combine with Kleene three-valued logic — the same algebra the engine's conjunct evaluation applies; a row qualifies only when the WHERE evaluates to TRUE (UNKNOWN drops the row, SQL semantics).
The returned rows are projected copies in oracle order: sorted per OrderBy when present (P1 sort keys are NOT NULL and suffixed with ID, so the expected sequence is total), original insertion order otherwise (the caller compares as a multiset in that case).
type ScalarSubSpec ¶
type ScalarSubSpec struct {
OuterCol string // outer column compared against the scalar
Op predicates.ComparisonType // comparison operator
Func AggFunc // AggMin / AggMax / AggCountStar / AggCountCol
Col string // aggregated inner column ("" for COUNT(*))
Filter *Pred // optional inner WHERE (Qual empty; col op literal); nil = whole table
}
ScalarSubSpec is a NON-correlated aggregate scalar subquery in a WHERE comparison:
<OuterCol> <Op> (SELECT <Func>(<Col>) FROM t [WHERE <Filter>])
This is a Go read-side extension — Java's grammar has no scalar subquery in an expressionAtom (RelationalParser.g4) — so the oracle is the sole authority on correctness, exactly the deep coverage the extension needs. The aggregate makes the subquery single-valued (no cardinality trap); an optional Filter lets the subquery be EMPTY, so MIN/MAX yield NULL and the outer comparison `col <op> NULL` is UNKNOWN → the row drops. That NULL-when-empty path is a documented past defect here (`id = (SELECT MIN(id) …)` once built `id=NULL`), so it is the axis most worth pinning. Func is restricted to MIN/MAX/COUNT so the oracle never hits SUM's int64-overflow sentinel.
type SeedResult ¶
type SeedResult struct {
Seed uint64
Kind OutcomeKind
Mismatches []*Mismatch
InfraErr error
Histogram map[string]int // plan family → query count
PlanErrors []string // first few embedded-planner failures (diagnostics for the plan-error bucket)
Declines []string // documented known-gap declines (RFC-182 §4), never silent
Executed int // query×projection executions compared
}
SeedResult is one seed's outcome plus its plan-family telemetry.
func RunCase ¶
func RunCase(ctx context.Context, setupDB *sql.DB, dbPath, clusterFile string, c *Case, id string, scanLimit int) *SeedResult
RunCase is RunSeed for a pre-built case (template seeds use this). id must be unique per case within the database (it names the schema). scanLimit > 0 pins a single connection with OptExecutionScannedRowsLimit so every query internally pages (see RunSeedPaged); 0 uses the pooled DB unpaged.
func RunSeed ¶
func RunSeed(ctx context.Context, setupDB *sql.DB, dbPath, clusterFile string, seed uint64) *SeedResult
RunSeed generates the seed's case, materializes it through the given sqldriver DB (which must already point at a database; RunSeed creates a seed-unique schema inside it), executes every query under every projection variant, and diffs each result against Oracle M.
setupDB executes DDL (CREATE SCHEMA TEMPLATE / CREATE SCHEMA); dbPath is the database path the caller created (e.g. "/testdb_rowdiff"); clusterFile connects the per-schema query DB.
func RunSeedPaged ¶
func RunSeedPaged(ctx context.Context, setupDB *sql.DB, dbPath, clusterFile string, seed uint64, scanLimit int) *SeedResult
RunSeedPaged is RunSeed with a per-statement scanned-rows limit, forcing the engine to internally page every query. This exercises CONTINUATION SOUNDNESS generatively — resume across a scanned-rows page boundary must return exactly the rows a single-pass run does — the class BUG C (paginated DISTINCT re-admission) belonged to, which the un-paged sweep cannot reach.
type StrFnKind ¶
type StrFnKind int
StrFnKind is a scalar string function whose semantics are unambiguous over the generator's ASCII string domain (so the oracle's Go implementation and the engine agree by construction — verified by probe).
const ( StrFnUpper StrFnKind = iota // UPPER(s) → string StrFnLower // LOWER(s) → string StrFnLength // LENGTH(s) → int (character count == byte count for ASCII) StrFnSubstr // SUBSTR(s, Start, Length) → string (1-based, clamped) StrFnConcat // CONCAT(s, 'Suffix') → string (NULL operand treated as "") StrFnTrim // TRIM(s) → string (strips leading/trailing spaces) )
type StrFnSpec ¶
type StrFnSpec struct {
Fn StrFnKind
Col string // the STRING column (only "S" today)
Start int64 // SUBSTR: 1-based start position (>=1)
Length int64 // SUBSTR: length (>=0); the result is clamped to the string bounds
Suffix string // CONCAT: the literal appended to the column
}
StrFnSpec is a string-function predicate LHS: `<Fn>(<Col>) <Op> Lit`, or for SUBSTR `SUBSTR(<Col>, Start, Length) <Op> Lit`, or for CONCAT `CONCAT(<Col>, 'Suffix') <Op> Lit`.
type StructCol ¶
type StructCol struct {
// Name is the physical column, e.g. "N"; empty for an INNER type, which is
// reached through a member rather than being a column of its own.
Name string
// TypeName is the struct type declared by CREATE TYPE AS STRUCT.
TypeName string
// Fields are the members in DECLARATION order, which is also the order a
// positional struct literal in an INSERT must supply them.
Fields []StructField
}
StructCol is the struct-typed column a nested case's dotted columns live in.
The generator carries the struct as a property of the TABLE rather than as a property of each column because that is the direction the information flows: a dotted column name (`N.A`) is enough for every consumer downstream of generation — the predicate renderer, the ORDER BY renderer, the oracle's row map — and only the two places that speak physical schema (DDL and INSERT) need to know that `N.A` and `N.B` are two leaves of ONE column. Putting the nesting in the column names is what makes this extension cheap: nothing that composes a query had to learn about structs, so no query construct is silently excluded from the nested axis by having been forgotten in a walker.
type StructField ¶
StructField is one member of a struct type: either a scalar leaf or, when Nested is non-nil, another struct.
The recursion is what makes the DEPTH axis reachable. `walkColumnRef` used to refuse a 3-segment reference outright, so a struct inside a struct could not be named and there was no point generating one; RFC-204 4.4 landed the Identifier model and the arity cap is gone, so `n.dp.a` and the four-segment `l.n.dp.a` both resolve. A generator capped at one level would now be capped below the engine — testing the shapes that already worked and none of the ones the depth fix opened.
type TableDef ¶
type TableDef struct {
Name string
Cols []ColumnDef // excludes ID
Indexes []IndexDef
// Struct, when non-nil, is the struct-typed column whose leaves the dotted
// entries of Cols name. See nested.go; nil for a flat table, which is
// every table the flat generator builds.
Struct *StructCol
}
TableDef is the generated table: fixed pk column "ID" plus value columns.
type Template ¶
Template is one directed seed: a deterministic case CONSTRUCTED to plan into a required family, asserted per-seed against the typed plan. This is the RFC-182 §5 hard coverage gate — a template failure means the family became unreachable (or the template rotted), deterministically, never a sampling flake. Random seeds carry no per-family gate at smoke scale.
type ThreeWayJoinSpec ¶
type ThreeWayJoinSpec struct {
LMLeft string // L column of the L↔M key
LMRight string // M column of the L↔M key
MRMid string // M column of the M↔R key
MRRight string // R column of the M↔R key
// Comma expresses the joins as a 3-way comma cross-join with both
// equalities in the WHERE; otherwise chained `JOIN … ON`. Same logical
// query, a different parse path into the planner.
Comma bool
// ROuter makes the THIRD leg a LEFT OUTER join
// (`l JOIN m ON … LEFT JOIN r ON …`): a (l,m) pair with no matching r is
// NULL-extended rather than dropped. Only meaningful for the JOIN…ON form
// (a comma cross-join cannot express OUTER), so the generator sets it only
// when !Comma. This exercises the composition of the two ordinal-binding
// fixes — outer-join multi-leg rows AND 3-way shared-column ordinals — the
// space where a residual of either would surface.
ROuter bool
}
ThreeWayJoinSpec is a 3-way self-join over the case's table under aliases L, M, R, chained `L.LMLeft = M.LMRight` and `M.MRMid = R.MRRight`. A 3-way join exercises JOIN ORDERING — the planner picks among 3! build orders and must keep every join key correct through the chosen order — which the 2-way self-join cannot reach. INNER only: the outer-leg 3-way shapes are simply not generated here yet (the 2-way LEFT/RIGHT generator already covers outer-join row soundness, incl. the InJoin+sort leg-window path fixed in TestFDB_LeftJoinPkOrdinal_InJoinSortRegression). The oracle stays a plain triple nested loop over the same authoritative rows.
type UnionSpec ¶
type UnionSpec struct {
Left *BoolNode // WHERE of the left branch (nil = no filter)
Right *BoolNode // WHERE of the right branch
All bool // UNION ALL (no dedup) vs UNION (dedup)
}
UnionSpec is a set operation between two single-table branches over the same table with the same projection:
SELECT <proj> FROM t WHERE <Left> UNION [ALL] SELECT <proj> FROM t WHERE <Right>
UNION dedups the combined output (SQL set semantics); UNION ALL keeps every row. Overlapping branch predicates make a row appear in both branches, so the dedup does real work — a bug there (over- or under-deduping, or the wrong dedup key) is a wrong-rows divergence.