1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! A shared "attribute target": the thing a comment or grant attaches to.
//!
//! Both comments and grants point at either a whole object or a sub-object (a
//! column). `DbObjectId` is the pure identity; `AttrTarget` is the render target
//! that pairs an object identity with an optional sub-object. The keyword and
//! SQL reference are derived per-renderer (see `render::comment` and
//! `render::sql::grant`), since the same object renders differently in a COMMENT
//! vs a GRANT.
use super::id::DbObjectId;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SubObject {
/// A column of a relation (table, view) or attribute of a composite type.
/// Keyed by name only — `attnum` is a physical coordinate that is not stable
/// across databases, so it never enters the model.
Column { name: String },
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct AttrTarget {
pub object: DbObjectId,
pub sub: Option<SubObject>,
}
impl AttrTarget {
/// Target a whole object.
pub fn object(object: DbObjectId) -> Self {
Self { object, sub: None }
}
/// Target a column/attribute of a relation or composite type. `parent` is
/// the owning object's id (table, view, or type).
pub fn column(parent: DbObjectId, name: impl Into<String>) -> Self {
Self {
object: parent,
sub: Some(SubObject::Column { name: name.into() }),
}
}
/// The identity this target depends on / orders after. Column targets
/// resolve to their parent object (the column lives inside it).
pub fn db_object_id(&self) -> DbObjectId {
self.object.clone()
}
// The following accessors are part of the public API and exercised by the
// integration test crate; the binary target re-declares the modules and so
// does not see that usage (matching the project's existing dead_code allows).
/// The owning object's schema (empty for schema-less objects).
#[allow(dead_code)]
pub fn schema(&self) -> String {
self.schema_and_name().0
}
/// The owning object's own name.
#[allow(dead_code)]
pub fn name(&self) -> String {
self.schema_and_name().1
}
/// The column name, if this targets a column/attribute.
#[allow(dead_code)]
pub fn column_name(&self) -> Option<&str> {
match &self.sub {
Some(SubObject::Column { name }) => Some(name.as_str()),
None => None,
}
}
/// The owning table, for objects that live on a table (constraint, trigger,
/// policy) or for a column target.
#[allow(dead_code)]
pub fn table(&self) -> Option<&str> {
match &self.object {
DbObjectId::Constraint { table, .. }
| DbObjectId::Trigger { table, .. }
| DbObjectId::Policy { table, .. }
| DbObjectId::Column { table, .. } => Some(table.as_str()),
_ => None,
}
}
/// The (schema, name) of the owning object, for routing/grouping. Column
/// targets report their parent relation. Objects without a schema (extensions)
/// report an empty schema.
pub fn schema_and_name(&self) -> (String, String) {
match &self.object {
DbObjectId::Schema { name } => (name.clone(), name.clone()),
DbObjectId::Table { schema, name }
| DbObjectId::View { schema, name }
| DbObjectId::Type { schema, name }
| DbObjectId::Domain { schema, name }
| DbObjectId::Sequence { schema, name }
| DbObjectId::Index { schema, name } => (schema.clone(), name.clone()),
DbObjectId::Function { schema, name, .. }
| DbObjectId::Procedure { schema, name, .. }
| DbObjectId::Aggregate { schema, name, .. }
| DbObjectId::Operator { schema, name, .. } => (schema.clone(), name.clone()),
DbObjectId::Constraint { schema, name, .. }
| DbObjectId::Trigger { schema, name, .. }
| DbObjectId::Policy { schema, name, .. } => (schema.clone(), name.clone()),
DbObjectId::Extension { name } => (String::new(), name.clone()),
// Casts have no schema; report the source→target pair as the name.
DbObjectId::Cast { source, target } => (String::new(), format!("{source} AS {target}")),
DbObjectId::Grant { .. } | DbObjectId::Comment { .. } | DbObjectId::Column { .. } => {
(String::new(), String::new())
}
}
}
}