Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Version>9.47.0</Version>
<Version>9.47.1</Version>
<LangVersion>13.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
81 changes: 81 additions & 0 deletions docs/release-9-47.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,84 @@ delegates everything except that marker, rather than a subclass.
| --- | --- |
| `QuickEventStorageDescriptor.AppendEventSqlReturnsSequence` | `false` |
| `QuickWithServerTimestampsEventStorageDescriptor.AppendEventSqlReturnsSequence` | `false` |

## 9.47.1

One PostgreSQL fix. Nothing public was added, removed or changed.

### Indexes are read back in a schema whose name has to be quoted

[#750](https://github.com/JasperFx/weasel/issues/750) /
[#751](https://github.com/JasperFx/weasel/pull/751), contributed by
[@marc-RocketRez](https://github.com/marc-RocketRez), with
[#756](https://github.com/JasperFx/weasel/pull/756) adding coverage.

The index introspection query identified each row's table by `regclassout`'s rendering of it, and
matched that against four spellings of the table Weasel was asking about — bare, schema-qualified,
quoted, and schema-qualified-with-the-table-quoted. **None of the four quoted the schema.**
`regclassout` quotes whatever has to be quoted, so any schema that needs quoting rendered to
something no candidate matched:

| schema | `regclassout` renders | matched |
| --- | --- | --- |
| `public`, in the session's `search_path` | `docs` | yes, the bare candidate |
| `reporting`, not in `search_path` | `reporting.docs` | yes, the qualified candidate |
| `order` | `"order".docs` | **no** |
| `MySchema` | `"MySchema".docs` | **no** |

(All four measured on PostgreSQL 15.3, for a table `docs`.)

So in a schema named for a reserved word — `order`, `user` — or in one carrying uppercase, **no
index was read back at all**, and from there it is
[#504](https://github.com/JasperFx/weasel/issues/504) again. With `actual.Indexes` empty every
declared index is `Missing` rather than `Different`, so the patch is a bare `create index` with no
preceding drop. The first run creates the table and its indexes; every run after it re-issues the
create and PostgreSQL answers `42P07: relation already exists`. A delta executes as one command, so
that first failure aborts everything ordered behind it, the schema never converges, and `db-assert`
can never pass.

Found in Wolverine, which stores its envelope tables in the service's own schema
([wolverine#5215](https://github.com/JasperFx/wolverine/issues/5215)): once
[wolverine#5216](https://github.com/JasperFx/wolverine/pull/5216) quoted the schema in Wolverine's own
SQL, an `order` or a `user` service started once and never again.

The query now returns the table's bare `relname`, from a join on `pg_class` by `idx.indrelid`, and
matches it against `Identifier.Name`. `nspname = :schema` already pinned the query to one schema, so
the name is all that was left to compare, and there is no longer a rendering whose quoting anyone has
to anticipate. That replaces the three extra candidates rather than joining them — they existed only
because `regclassout` quotes, and any spelling it chose that nobody had thought of dropped every index
of the table.

### The primary key constraint's name, in the same schemas

The primary key's name arrives on those same rows, so it was not read back either — and nothing
noticed, because `PrimaryKeyName` falls back to the provider's derived default whenever nothing
assigned it. For a table using the default name the stale actual and the expected agree by
construction, which is why this hid behind the index failure rather than beside it.

Name the constraint yourself and it surfaces. The delta compares the declared name against the derived
default, calls it an `Update`, and emits a rename off a name that was never in the database:

```sql
alter table "order".docs rename constraint pkey_docs_id to docs_pk;
```

```
42704: constraint "pkey_docs_id" for table "docs" does not exist
```

Both are fixed by the one change, and `indexes_in_a_mixed_case_schema` and the two added facts on
`indexes_in_a_reserved_word_schema` hold them.

### What changes for you

- **If any of your schemas is a reserved word or carries uppercase, upgrade.** Nothing else is
affected: for a schema that needs no quoting, `relname` and `regclassout`'s rendering name the same
table, and the comparison lands where it always did.
- **Index readback no longer depends on `search_path`.** `regclassout` omitted the schema when the
schema was in the session's `search_path` and included it otherwise, so which of the four candidates
matched varied with session state. `relname` is the same string either way.
- **weasel#224 is unaffected.** That is a mixed-case *table* in a lower-case schema, and the
quoted-name candidates were added for it. It keeps working for a better reason than they were:
Weasel quotes a mixed-case name when it creates the table, so `relname` is already the
case-preserved name the configuration asks for.
Loading