Repository navigation
fix(mysql): type a window aggregate over the current row as not null - #115
Conversation
|
No configured credential can serve this run. Your Claude Pro/Max subscription has hit its usage limit, which resets at 2026-10-26 08:00 UTC. Re-trigger after it resets, or add an API key for this provider, which Pullfrog falls back to automatically. The providers' responses
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
9a2058a to
ffd0767
Compare
There was a problem hiding this comment.
ℹ️ No critical issues. Two minor suggestions inline.
Reviewed changes
Pullfrog has not reviewed this PR before, so this covers the whole PR, including the new commit that keeps the server's flags under a ROLLUP.
- Frame rule:
frame_holds_current_rowaccepts no frame clause,ROWSframes that start at or before the current row and end at or after it, andRANGEframes whose bounds are unbounded orCURRENT ROW. It follows named windows through chains of definitions. - Probe:
window_probereplaces eachsum/avg/min/maxwindow call with its argument, andwindow_flagstakes the argument'sNOT NULLflag, or theWHERE's proof, for that column. It guards positions by comparing column counts. - ROLLUP exclusion (new commit):
window_probenow returnsNoneunderWITH ROLLUP, so the server's flags stand there. The commit also rebuildsengine.wasm. - JSON shape:
json_arrayaggover a frame that holds the current row is a non-null array at the top level. - Oracle and docs: adds the new
mysql_window_aggregatescase, loosens other cases'expected.txtonly for frames that hold the current row, and adds a captured Window functions entry to the MySQL page.
claude-opus-5-5 | 𝕏
There was a problem hiding this comment.
✅ No new issues found. Both points from the last review are addressed.
Reviewed changes
Since the last Pullfrog review, two commits address its two inline points.
ROLLUPpinned in the oracle:rollup_window.sqlinmysql_window_aggregatesis blessed with the server's own flags (running: numeric?,top_user: int4|int8?) undergroup by p.user_id with rollup, so the guard inwindow_probenow has an oracle case behind it.- Docs wording narrowed: the Window functions entry on the MySQL page now says only
sum,avg,minandmaxfollow their argument, and namesjson_arrayaggandjson_objectaggas never null. This matchesWINDOW_FOLLOWINGandWINDOW_NEVER_NULLinmysql_nulls.rs.
claude-opus-5-5 | 𝕏

MySQL typed every aggregate used as a window function nullable but
countand the bit functions: the server flagssum,avg,min,max,json_arrayaggandjson_objectaggnullable underOVERwhatever the frame. A frame that always holds the current row has that row's input, so now such an aggregate follows its argument there, and the JSON aggregates are never NULL.sum(count(*)) over (order by date_format(published_at, '%Y-%m')), grouped by the monthsum(p.id) over (order by p.id),over (),over (partition by p.user_id)max(p.title) over wwithwindow w as (partition by p.user_id order by p.id)json_arrayagg(p.note) over (partition by p.user_id)max(p.note) over (partition by p.user_id)withwhere p.note is not nullsum(p.id) over (order by p.id rows between 1 following and 2 following)sum(p.id) over (order by p.published_at range between interval 1 day preceding and interval 1 day following)sum(p.id) over (partition by u.id)overusers u left join posts pFrames that hold the current row. No frame clause (the whole partition, or with
ORDER BYevery row through the current row's last peer); aROWSframe that starts atUNBOUNDED PRECEDING,n PRECEDINGorCURRENT ROWand ends atCURRENT ROW,n FOLLOWINGorUNBOUNDED FOLLOWING; and aRANGEframe whose bounds are unbounded orCURRENT ROW.Frames that can leave it out, which stay nullable: a start
n FOLLOWING, an endn PRECEDING, and anyRANGEframe with an offset. Measured on 8.4, over aDATETIMEcolumn that holds a zero date,range between interval 1 day preceding and interval 1 day followingfinds no row for every row whose date is not NULL, so aRANGEoffset claims nothing.Named windows.
OVER wandOVER (w …)take the frame ofwwhere the call gives none, through a chain of definitions (w2 as (w1 order by …)); a window theWINDOWclause does not define claims nothing.How.
window_probeinengine/mysql/src/mysql_nulls.rswrites the statement with each such call replaced by its argument, under the sameFROM,WHEREandGROUP BY;window_flagsinengine/mysql/src/mysql.rsprepares it and takes the argument's NOT NULL flag, or theWHERE's proof of a bare column, for the call's column.json_arrayagg's JSON shape takes the same frame rule (mysql_shape.rs). Onlysum,avg,minandmaxfollow their argument:var_sampandstddev_sampare NULL over one row, so the variance family is left as the server flags it.oracle/cases/mysql_window_aggregates(23 statements, blessed on MySQL 8.4), with NULL and edge values in every ordering column. UnderWITH ROLLUPthe server's flags stand (rollup_window.sql).expected.txtlines that lost a?, each a window aggregate over a frame holding the current row:mysql_json_shapes(json_arrayagg(id) over (),json_arrayagg(id = 1) over ()),mysql_row_counts(sum(count(*)) over ()),mysql_types(sum(whole) over ()underwhere whole between 0 and 2), andmysql_server_flags(sum,max,min,avgandjson_arrayaggoverNOT NULLcolumns, andmax(day) over ()over aDATE NOT NULLthat holds zero dates). The frames1 preceding and 1 precedingand1 following and 2 followingthere stay nullable.Checks:
cargo build --workspace --all-targetswith no warning,cargo testof every crate, the whole oracle with--strict-coverage(132 cases, 0 failed),engine/build.sh(only the MySQL artifact changes),nub run typecheck,nub run testwith both databases (254 passed, 0 skipped),nub run build, and the site's capture and build.