Skip to content

Commit 0b6301a

Browse files
authored
adapter: clean up semantics of CatalogItem::desc/desc_opt (#34294)
Now it only returns a `RelationDesc` for types that are actually queryable relations, as initially intended.
1 parent 9cac88b commit 0b6301a

File tree

8 files changed

+95
-35
lines changed

8 files changed

+95
-35
lines changed

src/adapter/src/catalog/apply.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -759,11 +759,6 @@ impl CatalogState {
759759
item_type.details.array_id = Some(item_id);
760760
}
761761

762-
// Assert that no built-in types are record types so that we don't
763-
// need to bother to build a description. Only record types need
764-
// descriptions.
765-
let desc = None;
766-
assert!(!matches!(typ.details.typ, CatalogType::Record { .. }));
767762
let schema_id = self.resolve_system_schema(typ.schema);
768763

769764
self.insert_item(
@@ -780,7 +775,6 @@ impl CatalogState {
780775
create_sql: None,
781776
global_id,
782777
details: typ.details.clone(),
783-
desc,
784778
resolved_ids: ResolvedIds::empty(),
785779
}),
786780
MZ_SYSTEM_ROLE_ID,

src/adapter/src/catalog/state.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,14 +1419,15 @@ impl CatalogState {
14191419
cluster_id: in_cluster,
14201420
}),
14211421
Plan::CreateType(CreateTypePlan { typ, .. }) => {
1422-
let desc = match typ.inner.desc(&session_catalog) {
1423-
Ok(desc) => desc,
1424-
Err(err) => return Err((err.into(), cached_expr)),
1425-
};
1422+
// Even if we don't need the `RelationDesc` here, error out
1423+
// early and eagerly, as a kind of soft assertion that we _can_
1424+
// build the `RelationDesc` when needed.
1425+
if let Err(err) = typ.inner.desc(&session_catalog) {
1426+
return Err((err.into(), cached_expr));
1427+
}
14261428
CatalogItem::Type(Type {
14271429
create_sql: Some(typ.create_sql),
14281430
global_id,
1429-
desc,
14301431
details: CatalogTypeDetails {
14311432
array_id: None,
14321433
typ: typ.inner,

src/adapter/src/coord/sequencer/inner.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1479,10 +1479,14 @@ impl Coordinator {
14791479
) -> Result<ExecuteResponse, AdapterError> {
14801480
let id_ts = self.get_catalog_write_ts().await;
14811481
let (item_id, global_id) = self.catalog().allocate_user_id(id_ts).await?;
1482+
// Validate the type definition (e.g., composite columns) before storing.
1483+
plan.typ
1484+
.inner
1485+
.desc(&self.catalog().for_session(session))
1486+
.map_err(AdapterError::from)?;
14821487
let typ = Type {
14831488
create_sql: Some(plan.typ.create_sql),
14841489
global_id,
1485-
desc: plan.typ.inner.desc(&self.catalog().for_session(session))?,
14861490
details: CatalogTypeDetails {
14871491
array_id: None,
14881492
typ: plan.typ.inner,

src/catalog/src/memory/objects.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ use mz_sql::ast::{
4040
};
4141
use mz_sql::catalog::{
4242
CatalogClusterReplica, CatalogError as SqlCatalogError, CatalogItem as SqlCatalogItem,
43-
CatalogItemType as SqlCatalogItemType, CatalogItemType, CatalogSchema, CatalogTypeDetails,
44-
DefaultPrivilegeAclItem, DefaultPrivilegeObject, IdReference, RoleAttributes, RoleMembership,
45-
RoleVars, SystemObjectType,
43+
CatalogItemType as SqlCatalogItemType, CatalogItemType, CatalogSchema, CatalogType,
44+
CatalogTypeDetails, DefaultPrivilegeAclItem, DefaultPrivilegeObject, IdReference,
45+
RoleAttributes, RoleMembership, RoleVars, SystemObjectType,
4646
};
4747
use mz_sql::names::{
4848
Aug, CommentObjectId, DatabaseId, DependencyIds, FullItemName, QualifiedItemName,
@@ -1486,7 +1486,6 @@ pub struct Type {
14861486
pub global_id: GlobalId,
14871487
#[serde(skip)]
14881488
pub details: CatalogTypeDetails<IdReference>,
1489-
pub desc: Option<RelationDesc>,
14901489
/// Other catalog objects referenced by this type.
14911490
pub resolved_ids: ResolvedIds,
14921491
}
@@ -1706,6 +1705,14 @@ impl CatalogItem {
17061705
}
17071706
}
17081707

1708+
/// Returns the [`RelationDesc`] for items that yield rows, at the requested
1709+
/// version.
1710+
///
1711+
/// Some item types honor `version` so callers can ask for the schema that
1712+
/// matches a specific [`GlobalId`] or historical definition. Other relation
1713+
/// types ignore `version` because they have a single shape. Non-relational
1714+
/// items ( for example functions, indexes, sinks, secrets, and connections)
1715+
/// return [`SqlCatalogError::InvalidDependency`].
17091716
pub fn desc(
17101717
&self,
17111718
name: &FullItemName,
@@ -1727,13 +1734,13 @@ impl CatalogItem {
17271734
CatalogItem::MaterializedView(mview) => {
17281735
Some(Cow::Owned(mview.desc.at_version(version)))
17291736
}
1730-
CatalogItem::Type(typ) => typ.desc.as_ref().map(Cow::Borrowed),
17311737
CatalogItem::ContinualTask(ct) => Some(Cow::Borrowed(&ct.desc)),
17321738
CatalogItem::Func(_)
17331739
| CatalogItem::Index(_)
17341740
| CatalogItem::Sink(_)
17351741
| CatalogItem::Secret(_)
1736-
| CatalogItem::Connection(_) => None,
1742+
| CatalogItem::Connection(_)
1743+
| CatalogItem::Type(_) => None,
17371744
}
17381745
}
17391746

@@ -2434,7 +2441,12 @@ impl CatalogEntry {
24342441

24352442
/// Reports if the item has columns.
24362443
pub fn has_columns(&self) -> bool {
2437-
self.desc_opt_latest().is_some()
2444+
match self.item() {
2445+
CatalogItem::Type(Type { details, .. }) => {
2446+
matches!(details.typ, CatalogType::Record { .. })
2447+
}
2448+
_ => self.desc_opt_latest().is_some(),
2449+
}
24382450
}
24392451

24402452
/// Returns the [`mz_sql::func::Func`] associated with this `CatalogEntry`.

src/sql/src/names.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
//! Structured name types for SQL objects.
1111
12+
use std::borrow::Cow;
1213
use std::collections::{BTreeMap, BTreeSet};
1314
use std::fmt;
1415
use std::str::FromStr;
@@ -1761,20 +1762,50 @@ impl<'a> Fold<Raw, Aug> for NameResolver<'a> {
17611762
print_id: _,
17621763
} => {
17631764
let item = self.catalog.get_item(id).at_version(*version);
1764-
let desc = match item.desc(full_name) {
1765-
Ok(desc) => desc,
1766-
Err(e) => {
1767-
if self.status.is_ok() {
1768-
self.status = Err(e.into());
1765+
let name = normalize::column_name(column_name.column.clone());
1766+
let desc = match item.item_type() {
1767+
CatalogItemType::Type => {
1768+
let details = item
1769+
.type_details()
1770+
.expect("type items must carry type details");
1771+
match details.typ.desc(self.catalog) {
1772+
Ok(Some(desc)) => Cow::Owned(desc),
1773+
Ok(None) => {
1774+
if self.status.is_ok() {
1775+
self.status = Err(PlanError::TypeWithoutColumns {
1776+
type_name: full_name.clone().into(),
1777+
});
1778+
}
1779+
return ast::ColumnName {
1780+
relation: ResolvedItemName::Error,
1781+
column: ResolvedColumnReference::Error,
1782+
};
1783+
}
1784+
Err(e) => {
1785+
if self.status.is_ok() {
1786+
self.status = Err(e);
1787+
}
1788+
return ast::ColumnName {
1789+
relation: ResolvedItemName::Error,
1790+
column: ResolvedColumnReference::Error,
1791+
};
1792+
}
17691793
}
1770-
return ast::ColumnName {
1771-
relation: ResolvedItemName::Error,
1772-
column: ResolvedColumnReference::Error,
1773-
};
17741794
}
1795+
_ => match item.desc(full_name) {
1796+
Ok(desc) => desc,
1797+
Err(e) => {
1798+
if self.status.is_ok() {
1799+
self.status = Err(e.into());
1800+
}
1801+
return ast::ColumnName {
1802+
relation: ResolvedItemName::Error,
1803+
column: ResolvedColumnReference::Error,
1804+
};
1805+
}
1806+
},
17751807
};
17761808

1777-
let name = normalize::column_name(column_name.column.clone());
17781809
let Some((index, _typ)) = desc.get_by_name(&name) else {
17791810
if self.status.is_ok() {
17801811
let similar = desc.iter_similar_names(&name).cloned().collect();

src/sql/src/plan/error.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ pub enum PlanError {
7070
table: Option<PartialItemName>,
7171
column: ColumnName,
7272
},
73+
TypeWithoutColumns {
74+
type_name: PartialItemName,
75+
},
7376
WrongJoinTypeForLateralColumn {
7477
table: Option<PartialItemName>,
7578
column: ColumnName,
@@ -531,6 +534,11 @@ impl fmt::Display for PlanError {
531534
"column {} must appear in the GROUP BY clause or be used in an aggregate function",
532535
ColumnDisplay { table, column },
533536
),
537+
Self::TypeWithoutColumns { type_name } => write!(
538+
f,
539+
"type {} does not have addressable columns",
540+
type_name.item.quoted(),
541+
),
534542
Self::WrongJoinTypeForLateralColumn { table, column } => write!(
535543
f,
536544
"column {} cannot be referenced from this part of the query: \

src/sql/src/pure.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,20 @@ pub(crate) fn purify_create_sink_avro_doc_on_options(
398398
// Attach comment for each column in the item, if the user has
399399
// not already provided an overriding `DOC ON` option for the
400400
// column.
401-
if let Ok(desc) = item.desc(&full_name) {
401+
let column_descs: Result<Option<RelationDesc>, PlanError> = match item.item_type() {
402+
CatalogItemType::Type => item
403+
.type_details()
404+
.and_then(|details| details.typ.desc(catalog).transpose())
405+
.transpose(),
406+
_ => item
407+
.desc(&full_name)
408+
.map(|desc| Some(desc.into_owned()))
409+
.map_err(Into::into),
410+
};
411+
412+
if let Ok(Some(desc)) = column_descs {
402413
for (pos, column_name) in desc.iter_names().enumerate() {
403-
let comment = comments_map.get(&Some(pos + 1));
404-
if let Some(comment_str) = comment {
414+
if let Some(comment_str) = comments_map.get(&Some(pos + 1)) {
405415
let doc_on_column_key = AvroDocOn {
406416
identifier: DocOnIdentifier::Column(ColumnName {
407417
relation: full_resolved_name.clone(),

test/sqllogictest/comment.slt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ CREATE TYPE custom_map_type AS MAP (KEY TYPE = text, VALUE TYPE = custom_type)
378378
statement ok
379379
COMMENT ON TYPE custom_map_type IS 'custom_map_type_comment';
380380

381-
statement error cannot be depended upon
381+
statement error does not have addressable columns
382382
COMMENT ON COLUMN custom_map_type.key IS 'comment_on_key';
383383

384384
query TT rowsort
@@ -394,10 +394,10 @@ CREATE TYPE custom_list_type AS LIST (ELEMENT TYPE = custom_type)
394394
statement ok
395395
COMMENT ON TYPE custom_list_type IS 'custom_list_type_comment';
396396

397-
statement error cannot be depended upon
397+
statement error does not have addressable columns
398398
COMMENT ON COLUMN custom_list_type.element IS 'comment_on_element';
399399

400-
statement error cannot be depended upon
400+
statement error does not have addressable columns
401401
COMMENT ON COLUMN custom_map_type.key IS 'comment_on_key';
402402

403403
statement ok

0 commit comments

Comments
 (0)