fix(docs): preserve database column comments

Closes #6212
This commit is contained in:
t8y2 committed 2026-08-14 05:26:21 +00:00
1 parent 799bdd1a62
commit 2d47c94992
2 files changed
+91 -4

No files matched your search

+31 -1
View File
@@ -655,7 +655,7 @@ mod tests {
assert!(!error.contains("unknown field"), "should not surface a raw serde error, got: {error}");
}
use crate::docs::{DocTable, NoteSource, ProjectMeta, SchemaSnapshot, TableKind};
use crate::docs::{ColumnNote, DocTable, NoteSource, ProjectMeta, SchemaSnapshot, TableKind};
use crate::models::connection::DatabaseType;
fn snapshot_with(tables: Vec<DocTable>) -> SchemaSnapshot {
@@ -738,8 +738,13 @@ mod tests {
table.columns.push(crate::types::ColumnInfo {
name: "status".to_string(),
data_type: "text".to_string(),
comment: Some("Database lifecycle state.".to_string()),
..Default::default()
});
table.column_notes.insert(
"status".to_string(),
ColumnNote { note: "Database lifecycle state.".to_string(), source: NoteSource::Database, shadowed: None },
);
let mut snapshot = snapshot_with(vec![table]);
let annotations: AnnotationFile = serde_json::from_str(SAMPLE).expect("parse");
@@ -748,6 +753,31 @@ mod tests {
let note = snapshot.tables[0].column_notes.get("status").expect("column note");
assert_eq!(note.note, "State machine.");
assert_eq!(note.source, NoteSource::Local);
assert_eq!(note.shadowed.as_deref(), Some("Database lifecycle state."));
}
#[test]
fn database_column_note_survives_without_a_local_override() {
let mut table = table_named("core", "users", None);
table.columns.push(crate::types::ColumnInfo {
name: "email".to_string(),
data_type: "text".to_string(),
comment: Some("Primary contact address.".to_string()),
..Default::default()
});
table.column_notes.insert(
"email".to_string(),
ColumnNote { note: "Primary contact address.".to_string(), source: NoteSource::Database, shadowed: None },
);
let mut snapshot = snapshot_with(vec![table]);
let annotations: AnnotationFile = serde_json::from_str(SAMPLE).expect("parse");
apply_annotations(&mut snapshot, &annotations, DatabaseType::Postgres);
let note = snapshot.tables[0].column_notes.get("email").expect("database column note");
assert_eq!(note.note, "Primary contact address.");
assert_eq!(note.source, NoteSource::Database);
assert_eq!(note.shadowed, None);
}
#[test]
+60 -3
View File
@@ -6,8 +6,8 @@ use futures::stream::{self, StreamExt};
use crate::connection::AppState;
use crate::docs::dbml::{enum_type_name, is_inline_enum_spelling};
use crate::docs::{
build_relationships, DocEnum, DocTable, NoteSource, ProjectMeta, Relationship, SchemaSnapshot, SnapshotWarning,
TableKind,
build_relationships, ColumnNote, DocEnum, DocTable, NoteSource, ProjectMeta, Relationship, SchemaSnapshot,
SnapshotWarning, TableKind,
};
use crate::models::connection::ConnectionConfig;
use crate::schema;
@@ -106,6 +106,19 @@ fn any_comment_collected(tables: &[DocTable]) -> bool {
})
}
fn database_column_notes(columns: &[ColumnInfo]) -> BTreeMap<String, ColumnNote> {
columns
.iter()
.filter_map(|column| {
let comment = column.comment.as_ref().filter(|value| !value.trim().is_empty())?;
Some((
column.name.clone(),
ColumnNote { note: comment.clone(), source: NoteSource::Database, shadowed: None },
))
})
.collect()
}
/// True when the `CommentsUnsupported` warning belongs in the snapshot: the
/// engine's DDL capability flag says it can't do comments, and collection
/// found none to contradict it.
@@ -210,6 +223,7 @@ pub async fn collect_snapshot(
table: format!("{schema_name}.{}", info.name),
reason: error,
})?;
let column_notes = database_column_notes(&columns);
let mut table_warnings = Vec::new();
@@ -270,7 +284,7 @@ pub async fn collect_snapshot(
NoteSource::None
},
shadowed_note: None,
column_notes: BTreeMap::new(),
column_notes,
estimated_rows: None,
view_definition: None,
},
@@ -478,6 +492,49 @@ mod tests {
}
}
#[test]
fn database_column_comments_become_database_notes() {
let columns = vec![
crate::types::ColumnInfo {
name: "customer_name".to_string(),
comment: Some("Customer-facing name".to_string()),
..Default::default()
},
crate::types::ColumnInfo {
name: "status".to_string(),
comment: Some("Lifecycle state".to_string()),
..Default::default()
},
];
let notes = database_column_notes(&columns);
assert_eq!(notes.len(), 2);
let name = notes.get("customer_name").expect("customer name note");
assert_eq!(name.note, "Customer-facing name");
assert_eq!(name.source, NoteSource::Database);
assert_eq!(name.shadowed, None);
let status = notes.get("status").expect("status note");
assert_eq!(status.note, "Lifecycle state");
assert_eq!(status.source, NoteSource::Database);
assert_eq!(status.shadowed, None);
}
#[test]
fn database_column_notes_ignore_empty_and_whitespace_comments() {
let columns = vec![
crate::types::ColumnInfo { name: "empty".to_string(), comment: Some(String::new()), ..Default::default() },
crate::types::ColumnInfo {
name: "whitespace".to_string(),
comment: Some(" \t\n".to_string()),
..Default::default()
},
crate::types::ColumnInfo { name: "missing".to_string(), comment: None, ..Default::default() },
];
assert!(database_column_notes(&columns).is_empty());
}
fn sample_relationship() -> Relationship {
Relationship {
id: "orders.customer_id->customers.id".to_string(),