SIGN IN SIGN UP

Require SHOW COLUMNS on an Alias target in hasColumnInTable

`hasColumnInTable` checks `SHOW COLUMNS` on the database and table name it
is given, then reads that storage's metadata. For an `ENGINE = Alias` row
`StorageAlias::getInMemoryMetadataPtr` forwards to the target, so the
columns the caller learns belong to a table the check never covered: a user
holding `SHOW COLUMNS` on the alias and nothing on the target got `1` for a
column that exists on the target and `0` for one that does not, and could
enumerate the target's columns by guessing. `DESCRIBE` of the same alias is
denied, so the function was weaker than the surface it documents itself
against.

Add the second-stage check the rest of the tree already uses, in the shape
of `InterpreterDescribeQuery`, reusing the existing immediate-hop predicate
`StorageAlias::isTargetTableGranted`. This becomes the fourth call site
that throws on it, after `InterpreterDescribeQuery`,
`InterpreterShowCreateQuery` and `TableFunctionLoop`. The invariant is an
equality: this function discloses no more about an object's columns than
`DESCRIBE` of the same object, which is what its `FunctionDocumentation`
already promised and what #108464 set out to achieve.

The first-stage check stays on the raw names before resolution, so a denial
still does not depend on the table or database existing.

Guarding `StorageAlias::getInMemoryMetadataPtr` itself was rejected: the
read path legitimately consumes the target's metadata without
`SHOW COLUMNS` on the target, so a user holding only `SELECT(col)` on both
the alias and the target would have had `SELECT col FROM alias` refused.
Resolving the target through `getTargetTable({context, SHOW_COLUMNS})` was
also rejected, because `Context::checkAccess` builds its message from the
target's database and table, which would trade a column oracle for a
target-identity disclosure; `isTargetTableGranted` plus a local throw names
only the alias.

`alias` is `nullptr` for every other storage, so no other engine is
affected, and `hasPhysical` and `hasAlias` both read the one metadata
handle, so the single guard covers both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
G
Groene AI committed
c6f26969e948f3e62dfb5e273a8cc6619558e3aa
Parent: b20da91