Skip to content

fix(sparql): CONSTRUCT set semantics (#54) + pre-ship review hardening#61

Merged
styk-tv merged 2 commits into
mainfrom
fix-54-construct-set
Jul 3, 2026
Merged

fix(sparql): CONSTRUCT set semantics (#54) + pre-ship review hardening#61
styk-tv merged 2 commits into
mainfrom
fix-54-construct-set

Conversation

@styk-tv

@styk-tv styk-tv commented Jul 3, 2026

Copy link
Copy Markdown
Owner

What

Closes #54, and folds in the pre-v0.6.19 code-review hardening (the review found genuine bugs before the tag — exactly what a pre-release review is for).

#54 — CONSTRUCT result is a set

CONSTRUCT emitted one row per (solution × template-triple), so a template producing the same triple across N solutions returned it N times. W3C §16.2 defines the result as an RDF graph — a set. Row-level dedup at the return (dedup_construct_rows); minted template blank nodes get a fresh label per solution (§16.2.1) so their rows correctly survive, only byte-identical ground triples collapse. The #17 oracle caught this on its first run (fixture 32 flipped known-divergenceeligible, now matches spareval).

Review hardening (findings, all verified fixed)

src/query/executor.rs (expression surface):

  • [2] math:pow overflow → SQL abort. power(1e200, 2) aborted the whole query, violating the tier's "never a SQL abort" contract. Added the overflow guard (y·ln|x| > 709.78 → UNBOUND) alongside the existing domain guards.
  • [1] INF/-INF/NaN xsd:double literal → SQL abort. These lowered to unquoted INF::numeric (a column reference → abort on a valid SPARQL literal). Now mapped to Postgres' quoted 'Infinity'/'-Infinity'/'NaN' numeric special-values; finite values unchanged. (Pre-existing bug, surfaced by the review.)
  • [8] math:exp guard too tight. exp(709.5) is a representable double but returned unbound; guard widened 709 → 709.78 (ln(f64::MAX)).
  • [9] dedup_construct_rows skips the serialize+hash pass for ≤1 row.

tests/oracle/ (differential harness):

  • [5] engine exit status ignored. An erroring zero-result fixture matched an empty golden and read green — defeating the harness. engine() now returns the psql exit status; a non-zero exit (SQL error under ON_ERROR_STOP=1) is a hard fixture FAIL.
  • [6] zero_elapsed_ms first-only. Restored global replace (the bash normalizer used sed -g).

Kept by design (not bugs)

Findings [3] (property-path truncation WARNs by default) and [4] (carve boundary NOTICE) are the #14 fail-closed visibility behavior — intentional, documented in the CHANGELOG.

Deferred with tracking

Finding [7] (oracle numeric_eq blinds "2" vs "2.0") → #62 (v0.6.20 soundness tightening). The oracle is net-positive as-is (it caught #54 and #55).

Verification (isolated container, this branch's .so)

gate result
pgrx suite 339/339 (3 new math tests + the #54 pair)
regression 100/100
W3C + differential oracle 53/53 · 38 oracle-match · 1 known-divergence (#55, documented) · 0 diverge
oracle crate 35 (30 unit + 5 CLI)
fmt + clippy -D warnings clean

CHANGELOG deliberately untouched — folds into the v0.6.19 release cut (#60), where the [0.6.19] entry gains the #54 + hardening lines.

styk-tv added 2 commits July 3, 2026 02:44
)

CONSTRUCT emitted one row per (solution × template-triple) pair, so a
template producing the same triple across N solutions returned it N
times. W3C SPARQL 1.1 §16.2 defines the result as an RDF *graph* — a
set — which cannot hold a triple twice; the solution sequence's
multiplicity does not survive into the output graph. The #17
differential oracle surfaced this on its first run (fixture 32:
engine 3 rows vs W3C reference 1).

Fix: row-level set-dedup at CONSTRUCT's return (dedup_construct_rows),
applied on all three emission paths (constant fast-path ×2 +
per-solution). Precise by construction — a template blank node mints
a FRESH label per solution (§16.2.1), so its rows differ in their
minted labels and correctly survive; only byte-identical rows (ground
triples, within-solution repeats) collapse. Variable templates that
bind distinct triples per solution are unaffected.

- Corrected the pg_test that had codified the bug
  (construct_constant_template_three_solutions asserted 3 →
  construct_constant_template_dedups_to_set asserts 1) + a new test
  locking the subtle half (ground collapses, bnodes survive).
- 100-construct-foundation invariant B golden 3 → 1, with the §16.2
  set-semantics rationale corrected (it had repeated the same
  'multiplicity matters' misreading).
- Flipped fixture 32's oracle marker known-divergence(#54) →
  eligible; it now MATCHES spareval (oracle: 38 match, 0 diverge).

336/336 pgrx · 100/100 regression · 53/53 w3c + differential oracle ·
fmt + clippy clean.

Closes #54
…rals, oracle harness

Confirmed findings from the pre-v0.6.19 code review, fixed before the
release tag:

executor.rs (expression surface #50/#51):
- math:pow now guards float8 OVERFLOW (y·ln|x| > 709.78 → UNBOUND),
  not just the 0^neg / neg^non-integer domain cases. pow(1e200,2)
  aborted the whole query, contradicting the tier's 'never a SQL
  abort' contract. [finding 2]
- A constant INF / -INF / NaN xsd:double literal in the numeric lane
  mapped to an UNQUOTED  (a column reference → query
  abort on a valid SPARQL literal). Now maps to Postgres' quoted
  'Infinity' / '-Infinity' / 'NaN' numeric special-values; finite
  values unchanged. [finding 1]
- math:exp guard widened 709 → 709.78 (ln(f64::MAX)); exp(709.5) is a
  representable double and was wrongly returning unbound. [finding 8]
- dedup_construct_rows skips the serialize+hash pass for <= 1 row. [9]

tests/oracle (differential harness #17):
- engine() now returns the psql exit status; run() treats a non-zero
  exit (SQL error under ON_ERROR_STOP=1) as a hard fixture FAIL —
  previously an erroring zero-result fixture matched an empty golden
  and read as green, defeating the harness. [finding 5]
- zero_elapsed_ms replaces EVERY occurrence (was first-only; the bash
  normalizer it replaced used sed -g). [finding 6]

Intended-behavior findings (property-path WARN default, carve
boundary NOTICE) are the #14 fail-closed visibility by design, kept.
The oracle numeric_eq soundness tightening is tracked as #62 (v0.6.20).

339/339 pgrx (3 new math tests) · 100/100 regression · 53/53 w3c +
oracle · oracle crate 35 · fmt + clippy clean.
@styk-tv styk-tv changed the title fix(construct): CONSTRUCT result is a set — dedup identical triples (#54) fix(sparql): CONSTRUCT set semantics (#54) + pre-ship review hardening Jul 3, 2026
@styk-tv styk-tv merged commit 2813191 into main Jul 3, 2026
4 checks passed
@styk-tv styk-tv deleted the fix-54-construct-set branch July 3, 2026 02:49
styk-tv added a commit that referenced this pull request Jul 3, 2026
…ELOG

Brings the post-cut work (merged via #61) into the [0.6.19] entry:
CONSTRUCT set semantics (#54), expression-lane robustness (math:pow
overflow, INF/NaN literals, math:exp guard), and the HAVING-alias
documented-extension resolution (#55). No version/schema change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CONSTRUCT emits duplicate triples per solution — W3C result graph is a set (oracle finding, fixture 32)

1 participant