Repository navigation
feat(postgres): infer greatest and least non-null by their arguments - #47
Merged
Merged
Conversation
8000
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
ℹ️ No issues found — one optional cleanup inline.
Reviewed changes
I reviewed the one commit that makes Postgres type greatest/least by their arguments, plus the oracle case, the docs line and the rebuilt wasm.
MinMaxExprread asExpr::Any: on Postgres alone,greatest/leastare non-null when any argument is, matching Postgres's "NULL only if all arguments are NULL". TheDialect::Postgresguard keeps MySQL's NULL-if-any reading.- Aggregate scan descends into
MinMaxExpr:(select greatest(count(*), 1) from t)now reads as one row. A set-returning call nested in the arguments is still caught, becauseone_valuewalks the whole node tree generically. - Oracle, docs, wasm: the new
greatest_least.sqlcase covers non-null, nullable, text and aggregate-subquery arguments.tests/fingerprint.test.tspasses for the committedpackages/postgres/engine.wasmat7679b61.
claude-opus-5-5 | 𝕏
colinhacks
force-pushed
the
greatest-least
branch
3 times, most recently
from
October 23, 2026 00:06
3c28254 to
b8f3c6a
Compare
Contributor
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
I reviewed b8f3c6a, which removes the old function-call path for greatest/least now that MinMaxExpr handles them.
- Removed
POSTGRES_ANY: itsFuncCallarm and its entry inpostgres_function_names()are gone. With no catalog, a quoted"greatest"(…)used to be read as the built-in. Now it falls through as an unknown function and is typed nullable, andone_valuetreats it conservatively. The trust query also stops asking about names that have nopg_procentry. - Added a quoted-name assertion:
greatest_and_least_need_one_non_null_argument_on_postgres_alonenow pins"greatest"(NULL, 0)as nullable. - Rebuilt
engine.wasm:tests/fingerprint.test.tspasses for all three committed artifacts atb8f3c6a.
claude-opus-5-5 | 𝕏
colinhacks
force-pushed
the
greatest-least
branch
from
October 23, 2026 00:29
b8f3c6a to
2896b7d
Compare
colinhacks
force-pushed
the
greatest-least
branch
from
October 23, 2026 03:10
2896b7d to
b295efc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Postgres parses
greatest(…)andleast(…)as their own syntax (MinMaxExpr), never as a function call, so the rule that types them by their arguments never fired, and both were always typed nullable. Each returnsNULLonly when every argument is, so either is now non-null when one argument is.GREATESTandLEASTreturnNULLwhen any argument is, and the same reader parses MySQL and SQLite text, so they are left as they were. A unit test pins both.(select greatest(count(*), 1) from member)reads as an aggregate and stays one row.precision_rulesgainsgreatest_least.sql, and every other expected line is unchanged.greatestandleastbesidecoalesce.Tests: the engine's unit tests, the oracle with
--strict-coverage, the wasm fingerprint, the typecheck, the package build and the docs site. The loader tests fail one SQLite test that fails onmaintoo on Node 26 (tests/representation.test.ts, "SQLite: JSON a statement builds…",node:sqlitewithreadBigIntsfalse); this branch touches nothing on that path.