Skip to content

Fold PostgresqlMigrator.AssertValidIdentifier onto the shared helper (weasel#416) - #422

Merged
jeremydmiller merged 1 commit into
masterfrom
fix/416-postgresql-shared-identifier-helper
Aug 2, 2026
Merged

Fold PostgresqlMigrator.AssertValidIdentifier onto the shared helper (weasel#416)#422
jeremydmiller merged 1 commit into
masterfrom
fix/416-postgresql-shared-identifier-helper

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Follow-on to #420, which left PostgreSQL on its own hand-rolled loop — the reasoning there was that folding it in would be a no-behaviour-change edit to code shipped in 9.23.0. That turns out to be half right, so the delta is worth stating plainly up front.

Unchanged: the rules PostgreSQL already had come back identical — null/empty/whitespace-only, whitespace anywhere, ", ; — with the same reason strings, so PostgresqlIdentifierInvalidException.Reason and the exception message are byte-for-byte what they were. The length check and PostgresqlIdentifierTooLongException stay in the provider, since NameDataLength is PostgreSQL's own and configurable.

Changed: the shared helper also rejects ', and PostgreSQL did not.

That is not uniformity for its own sake. Table.cs:584DefaultValueFromSequence — writes a sequence's DbObjectName into a string literal:

return DefaultValueByExpression($"nextval('{sequenceName}')");

so a sequence name carrying a ' closes that literal and the rest of the DEFAULT clause is whatever the name says next. PostgreSQL's introspection queries do parameterise names (Table.FetchExisting, Function), which is why the exposure here is narrower than on SQL Server, Oracle and SQLite, where the existence checks interpolate them — but it is not zero.

An apostrophe in a schema object name has no legitimate use that survives Weasel's quoting anyway: SchemaUtils.QuoteName only quotes keywords and names containing an uppercase character, so such a name is written out raw. Still, it is a tightening layered on a released one, and anyone with a ' in an object name will now get an exception where 9.23.0 gave them DDL.

The three single-quote cases are added to assert_identifier_rejects_quote_and_semicolon rather than to a new test, since it is the same rule.

Regression pass

Full suites on net9.0, PostgreSQL container from docker compose up:

Suite Result
Weasel.Postgresql 849 passed, 3 pre-existing skips (846 before, plus the three new cases)
Weasel.Core 75 passed
Weasel.EntityFrameworkCore 106 passed, 1 skip
Weasel.CommandLine 23 passed

The other four providers are untouched by this — their behaviour landed in #420.

🤖 Generated with Claude Code

…er (weasel#416)

The previous commits left PostgreSQL on its own hand-rolled loop on the grounds
that folding it in would be a no-behaviour-change edit to code shipped in 9.23.0.
That was half right: the loop is now IdentifierValidation.FindProblem, and the
rules it had -- null/empty/whitespace-only, whitespace anywhere, '"', ';' -- come
back identical, with the same reason strings, so PostgresqlIdentifierInvalidException
.Reason and its message are unchanged. The length check and
PostgresqlIdentifierTooLongException stay here, since NameDataLength is
PostgreSQL's own and configurable.

The behaviour delta is the single quote, which the shared helper rejects for every
provider and PostgreSQL did not. It is not uniformity for its own sake here either:
Table.ColumnExpression.DefaultValueFromSequence writes a sequence's name into a
string literal -- DEFAULT nextval('{name}') -- so a name carrying a quote closes
that literal and the rest of the DDL is whatever the name says next. PostgreSQL's
introspection queries parameterise names (Table.FetchExisting, Function), which is
why the exposure is narrower here than on the providers that interpolate them.

An apostrophe in a schema object name has no legitimate use that survives Weasel's
quoting anyway -- SchemaUtils.QuoteName only quotes keywords and names with an
uppercase character, so such a name is written out raw -- but it is a tightening on
top of a released one, and worth reading as such.

Full Weasel.Postgresql suite green (849 passed, 3 pre-existing skips -- 846 before,
plus the three single-quote cases added to assert_identifier_rejects_quote_and_semicolon).
Weasel.Core 75, Weasel.EntityFrameworkCore 106 passed/1 skip, Weasel.CommandLine 23.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jeremydmiller
jeremydmiller merged commit b491fa6 into master Aug 2, 2026
16 checks passed
@jeremydmiller
jeremydmiller deleted the fix/416-postgresql-shared-identifier-helper branch August 2, 2026 13:02
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.

1 participant