Repository navigation
The companion-object remedy told 23 clusters to UPDATE an extension none of them had: the pg_extension row now ARRIVES at the fault mapping, and the remedy is the row's rather than an inference's (#3830) - #3839
Merged
Conversation
…one of them had: the pg_extension row now ARRIVES at the fault mapping, and the remedy is the row's rather than an inference's (#3830) #3818 gave a missing companion object one remedy, ALTER EXTENSION pg_stat_statements UPDATE, and reached it by inference: the base object resolved, therefore the extension is present, therefore it is present below 1.9. The middle step does not survive the Aurora flavor. There the base object that resolved is aurora_stat_statements(), which is not the extension's and needs none, so "it resolved" licenses nothing about pg_extension - and the store-side read of pg_extension_availability settled what the 23 clusters actually were: installed_version EMPTY in every database, the extension never created anywhere, the collector productive throughout. They were told to update something they did not have. The state now arrives instead. The PostgreSQL detection query - one statement already being made at every connect - gains one scalar for pg_stat_statements' extversion, NULL when pg_extension has no row. That lands on ServerRuntime as a PgExtensionRowObservation: the row, with the database it was read in, and a default that means NOT OBSERVED rather than no row. The fault mapping takes it as a parameter beside the flavor, scopes it to the database the fault came from (pg_extension is per database) and asks the declared companion for a verdict. Four verdicts, four sentences. No row on Aurora: nothing to alter, the collector not waiting on anything, CREATE EXTENSION optional and what it buys named. No row on vanilla: CREATE EXTENSION, with the preload sentence back because the module has to be loaded first. Below 1.9: #3818's remedy, with the version read out of the catalog rather than guessed. At or above: no update is owed and it would change nothing - 1.10 is the case that decides this, since an ordinal string compare ranks it below 1.9 and would send a fleet two releases ahead back to run the update twice. Nothing observed, or a version nothing can rank: #3818's two-possibilities sentence, byte for byte, which is all the fault alone supports. The flavor half is declared, not guessed: PgExtensionDependency.AuroraNativeAlternative says what the collector reads on Aurora instead of the extension's base object, and a collector that leaves it null gets the required-install sentence. The three comments attributing the 1.8 state to those 23 clusters now say which state was measured and which was assumed. No rung; no collector query changed.
…reader's ordinal": both numbers are now derived and compared, and appending a column is allowed again (#3830) #2228's tripwire pin asserted EndsWith("connected_database") on both detection queries, with a doc paragraph saying what it was actually protecting: the readers index by number, so a column inserted ahead of it shifts five other fields onto the wrong values. "Last in the list" is not that claim. It forbids APPENDING, which cannot shift anything - this change appended one scalar and the pin went red - and it would have passed a column inserted BEFORE connected_database with the reader updated to match, which is the shift it exists to catch. The pin now parses each select list into its trailing aliases and reads the ordinal that method's own body passes to IsDBNull, and compares the two. Neither number is written down. The parse handles the two shapes both queries carry - a comma inside a function call and a comma inside a comment, in both comment spellings - and a control on an arranged input asserts it counts columns rather than commas, because a splitter that got either wrong would compare two meaningless numbers and pass. Verified red-first by inserting a column ahead of connected_database in the PostgreSQL query: the PostgreSQL half fails, the SQL Server half stays green.
This was referenced Sep 21, 2026
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.
pg_statement_statsdeclarespg_stat_statements_infoas a companion the extension gains at 1.9, and since #3818 a 42P01 naming it is recorded as a companion fault with one remedy:ALTER EXTENSION pg_stat_statements UPDATE. That remedy was reached by inference — the base object resolved, therefore the extension is present, therefore it is present below 1.9 — and the middle step does not survive the Aurora flavor, where the base object that resolved isaurora_stat_statements(), which is not the extension's and needs no extension at all. The store-side read ofpg_extension_availabilitysettled what the 23 clusters #3818 counted actually were:installed_versionempty in every database, the extension never created anywhere, the collector productive throughout through the Aurora function. The sentence told them to update something they did not have.The row arrives instead of being inferred
PostgresDetectionQueryText— the statement every PostgreSQL connect already runs — gains one scalar:pg_stat_statements'extversionin the connected database, NULL whenpg_extensionhas no row for it. No second query, no extra round trip, no grant beyond what the connect already needs. The extension name is a literal because aconstcannot interpolate, and a pin compares it againstPgStatementStatsCollector's ownRequiredPgExtensionsdeclaration so the two cannot drift.It lands on
ServerRuntimeas aPgExtensionRowObservation: the row, plus the database it was read in, plus anObservedflag whosedefaultis not observed rather than no row — a SQL Server target, a hand-built runtime and a host that never ran the read all producedefault, and those two readings are opposite remedies. It rides on the runtime rather than onCollectorTargetInfobecause nothing dispatches on it, andCollectorTargetInfois the type whose facts the engine-capability sweep has to vary or fix.PostgresFaultOutcometakes the observation and the flavor as two optional parameters, scopes the observation to the database the fault came from (pg_extensionis per database, and aRunsPerDatabasecollector faults in databases the connect-time read never saw), and asks the declared companion for a verdict. The verdict is computed at the mapping from the stored row, never stored itself — the same divisionCollectorMeasurementstates, for the same reason.Four verdicts, four sentences
ALTER EXTENSIONto update; the collector's source is the declaredaurora_stat_statements(), which needs no extension, so it is not waiting on one;CREATE EXTENSIONis optional and what it buys is the companion and nothing else.CREATE EXTENSION, with theshared_preload_librariessentence back, because the declaration'sInstallKindsays the module has to be loaded before the create does anything.1.10is the case that decides whether the comparison is a comparison: an ordinal string compare ranks1.10below1.9and would send a fleet two releases ahead back to run an update it has already run twice, which is this defect one version further on.extversionis free text the extension's author chooses, so a version that cannot be ranked answersUndeterminedrather than guessing a side. This is the hedge, kept only where there is no answer to prefer over it.What the flavor half rests on
PgExtensionDependency.AuroraNativeAlternativedeclares what the collector reads on Aurora instead of the extension's base object. Nothing else reads it; its only effect is that an absent extension can be called optional. A collector that reads its extension on Aurora too leaves it null and gets the required-install sentence, and a pin asserts the declared name is a surface the generated Aurora query actually calls and the vanilla query does not.Corrections carried
Three comments in #3824's file set attributed the 1.8 state to those 23 clusters as fact. They now say which state was measured (
pg_extension_availability: no row) and which was assumed. Below-1.9 remains a real state with a real remedy; it was not theirs, and the live rig that proves the gate holds at 1.8 proves it for one of the two absences rather than for the fleet.Pins
PostgresFaultOutcomeTestsasserts each of the four sentences as a whole string, not as fragments: the defect was a sentence whose clauses were individually plausible and wrong together. The fallback is asserted as equal to the no-argument call, so "the hedge is #3824's text" is pinned rather than commented.PgStatementStatsFlavorTestspins the declaration, the verdict over a fake row set (no row, 1.8, 1.9, 1.10, blank, unrankable), the per-database guard, and the version comparison on the pairs a string compare gets wrong. A source pin covers the wiring — read, carried, passed — because a revert that dropped the argument would compile, run, and go back to one remedy for three states with every sentence pin still green.One pin repaired, not worked around
#2228's tripwire pin asserted
EndsWith("connected_database")on both detection queries while its own doc said what it was protecting: the readers index by number, so a column inserted ahead of it shifts five other fields onto the wrong values. "Last in the list" is not that claim — it forbids appending, which cannot shift anything, and it would have passed a column inserted beforeconnected_databasewith the reader updated to match. It now parses each select list into its trailing aliases and reads the ordinal that method's own body passes toIsDBNull, and compares the two; neither number is written down. A control on an arranged input asserts the parse counts columns rather than commas, since both queries carry a comma inside a function call and a comma inside a comment, in both comment spellings. Verified red-first by inserting a column ahead ofconnected_database.No migration rung. No collector query changed. Lite has no PostgreSQL connect path and maps no PostgreSQL fault text, so there is no twin to keep in step.
Closes #3830