sql-499: print ids for types in DOC ON positions - #38612
Conversation
QA LLM Review1. HIGH --
|
|
I've kicked off the |
ggevay
left a comment
There was a problem hiding this comment.
Some comments.
(Should also rebase on main to get rid of some unrelated CI flakes.)
| schema.unwrap_or_else(|| panic!("missing schema in doc on reference: {name:?}")); | ||
| let item = tx | ||
| .get_items() | ||
| .find(|i| i.name == item_name.as_str() && i.schema_id == schema.id); |
There was a problem hiding this comment.
Should-fix, reproduced. A type may share (schema, name) with a secret, connection, sink, or function (conflicts_with_type is false for those, and the durable uniqueness check, plan_create_sink, and plan_create_connection all allow it), and get_items() is id-sorted, so this binds to whichever same-named item is older. I upgraded a v26.40.0-rc.3 catalog holding CREATE SECRET point created before a commented CREATE TYPE point, plus an Avro sink over an MV using it, to this branch: with only the type commented, the reference became DOC ON TYPE [u11 AS ...] with u11 the secret and the sink's edge moved to the secret; with a column comment as well, the new version panics at catalog open on every boot (ItemWithoutColumns { .., item_type: Secret } from apply.rs, the rewrite never commits), so under 0dt the upgrade is stuck and without 0dt it is an outage. Add && i.item_type() == CatalogItemType::Type as the last conjunct (it parses create_sql): a bare name in a DOC ON position can only be a type, since relations always carried ids there and functions cannot resolve there.
| let parts = &name.0; | ||
| let (db_name, schema_name, item_name) = match parts.len() { | ||
| 3 => (&parts[0], &parts[1], &parts[2]), | ||
| 2 => return None, |
There was a problem hiding this comment.
Nit: builtin types are durable too, as GidMapping rows (tx.get_system_object_mappings(), description.{schema_name, object_type, object_name} and unique_identifier.catalog_id), so the same lookup works here and would close the last gap: an old sink with an explicit DOC ON TYPE int4 keeps the bare name and gets no edge, a new one prints [sNN AS pg_catalog.int4] and does. Reachable only through an explicit DOC ON (a superuser cannot comment on a builtin type, only mz_system can), hence nit level; the doc comment's "not durable items" should go either way.
| /// as a bare qualified name, unlike a relation in the same position, because | ||
| /// name resolution suppressed ids for types. Resolution now prints the id | ||
| /// (see `NameResolver::resolve_doc_on_name`); this rewrites stored statements | ||
| /// to match, so the reference survives renames and `create_sql` reference |
There was a problem hiding this comment.
Today no rename strands a bare name: ALTER TYPE only parses OWNER TO, databases have no rename, and ALTER SCHEMA RENAME rewrites bare names too (CreateSqlRewriteSchema::visit_unresolved_item_name_mut). Fine as future-proofing, so please phrase it that way here and in the commit message and PR body ("would survive renames if those were ever added") rather than as a present bug.
|
|
||
| def initialize(self) -> Testdrive: | ||
| return Testdrive(dedent(""" | ||
| > CREATE TYPE sink_comments_point AS (x integer, y integer) |
There was a problem hiding this comment.
Cheap end-to-end cover for the collision above: > CREATE SECRET sink_comments_point AS 'x' before this line, so the upgrade scenarios must bind the type rather than the secret (the edge assertion in validate catches a wrong binding, which is how my reproduction failed). Also keep the 2604100 guards in step with the version this actually lands in.
bd71692 to
7f8f84e
Compare
DOC ON TYPE x and DOC ON COLUMN x.c resolve a name that may denote a type or a relation, and both spellings persist in the sink's create_sql. A relation in these positions prints as [id AS name], but resolution suppressed ids for types, so a type persisted as a bare qualified name. A bare name strands the sink if the type, its schema, or its database is renamed, and it hides the sink's dependency on the type from create_sql reference extraction: mz_object_dependencies files the bare name under named_relations, whose join excludes Type rows, so the edge silently drops out of the view even though the in-memory dependency graph (which blocks DROP TYPE) still records it. Force ids in the two places that construct DOC ON references: the name resolver's DOC ON folds, and sink purification, which injects DOC ON options for every commented item the sink references. An AST migration in the next commit rewrites existing catalogs to match. Functions keep printing without ids, preserving the convention that function references never persist ids. No function can reach the injection today anyway: COMMENT ON FUNCTION does not resolve, so no function carries a comment.
Name resolution now prints ids for types in DOC ON positions, but existing catalogs still hold sink create_sql with bare qualified type names. Rewrite those to [id AS name] form, resolving the name against the durable catalog, so the reference survives renames and create_sql reference extraction (mz_object_dependencies) recovers the sink's edge to the type. References that already carry an id are skipped, so the rewrite is idempotent and safe to run every boot. Names without a database part denote items in ambient (system) schemas: those are not durable items and builtin names are stable, so they are left resolving by name.
Assert in kafka-avro-sinks-doc-comments.td that a sink over a relation with a commented custom type records a dependency edge on the type, and add a KafkaSinkCommentsOnType platform check so the edge also survives restarts and upgrades. In upgrade scenarios the check's initial sink is created by the old version with a bare type name in its DOC ON reference, so validating the edge on the new version exercises the AST migration end to end.
7f8f84e to
83fac0f
Compare
Context:
DOC ON TYPE x and DOC ON COLUMN x.c resolve a name that may denote a
type or a relation, and both spellings persist in the sink's create_sql.
A relation in these positions prints as [id AS name], but resolution
suppressed ids for types, so a type persisted as a bare qualified name.
A bare name strands the sink if the type, its schema, or its database is
renamed, and it hides the sink's dependency on the type from create_sql
reference extraction: mz_object_dependencies files the bare name under
named_relations, whose join excludes Type rows, so the edge silently
drops out of the view even though the in-memory dependency graph (which
blocks DROP TYPE) still records it.
Force ids in the two places that construct DOC ON references: the name
resolver's DOC ON folds, and sink purification, which injects DOC ON
options for every commented item the sink references. An AST migration
rewrites existing catalogs to match
Motivation
Closes sql-499
Verification
Added platform check to test the upgrade and modified a td to assert it.