Skip to content

Commit a3ae94c

Browse files
committed
Implement PgNode for Value and ValUnion types
Postgres 13 and 14 use the `Value` struct to represent multiple possible value types: integer, float, string, etc. Each type has its own NodeTag, but they can't be automatically associated with the struct using the information in the bindgen bindings. This hard-codes the associations just before emitting all the `PgNode` implementations. Postgres 15 introduced separate structs for each value type. These fit nicely into the automatic struct-to-tag scheme, but we weren't handling the `ValUnion` union Postgres uses to represent these multiple types in a single `Node`. This extends the detection of parent-child types to implement `PgNode` for union nodes.
1 parent 38e0212 commit a3ae94c

9 files changed

Lines changed: 291 additions & 141 deletions

File tree

pgrx-bindgen/src/build.rs

Lines changed: 164 additions & 139 deletions
Large diffs are not rendered by default.

pgrx-pg-sys/src/include/pg13.rs

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pgrx-pg-sys/src/include/pg14.rs

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pgrx-pg-sys/src/include/pg15.rs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pgrx-pg-sys/src/include/pg16.rs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pgrx-pg-sys/src/include/pg17.rs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pgrx-pg-sys/src/include/pg18.rs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pgrx-pg-sys/src/include/pg19.rs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pgrx-pg-sys/src/node.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,70 @@ mod tests {
129129
let node = expr.try_as::<crate::Node>().expect("upcast to Node should succeed");
130130
assert_eq!(node.node_tag(), crate::NodeTag::T_AlternativeSubPlan, "tag remains");
131131
}
132+
133+
// In versions prior to pg15, the `Value` struct is used with tags T_Null, T_Integer, T_Float,
134+
// T_String, and T_BitString.
135+
#[cfg(any(feature = "pg13", feature = "pg14"))]
136+
#[test]
137+
fn value_struct_cast() {
138+
let value =
139+
crate::Value { type_: crate::NodeTag::T_Integer, val: crate::ValUnion { ival: 42 } };
140+
141+
let node = value.as_node(); // infallible upcast
142+
assert_eq!(node.node_tag(), crate::NodeTag::T_Integer, "tag remains");
143+
144+
let next = node.try_as::<crate::Value>().expect("downcast to self should succeed");
145+
assert_eq!(next.node_tag(), crate::NodeTag::T_Integer, "tag remains");
146+
assert_eq!(unsafe { next.val.ival }, 42);
147+
148+
for tag in &[
149+
crate::NodeTag::T_Float,
150+
crate::NodeTag::T_String,
151+
crate::NodeTag::T_BitString,
152+
crate::NodeTag::T_Null,
153+
] {
154+
let value = crate::Value {
155+
type_: *tag,
156+
val: crate::ValUnion { str_: c"something".as_ptr() as *mut _ },
157+
};
158+
159+
let node = value.as_node(); // infallible upcast
160+
assert_eq!(node.node_tag(), *tag, "tag remains");
161+
162+
let next = node.try_as::<crate::Value>().expect("downcast to self should succeed");
163+
assert_eq!(next.node_tag(), *tag, "tag remains");
164+
assert_eq!(unsafe { next.val.str_ }, c"something".as_ptr() as *mut _);
165+
}
166+
}
167+
168+
// Since pg15, Boolean, Integer, Float, String, and BitString are separate node structs.
169+
// `ValUnion` is a single node that can hold any one of these types. That union should implement
170+
// PgNode so we can cast to one of the above structs.
171+
#[cfg(any(
172+
feature = "pg15",
173+
feature = "pg16",
174+
feature = "pg17",
175+
feature = "pg18",
176+
feature = "pg19"
177+
))]
178+
#[test]
179+
fn value_union_cast() {
180+
let vu = crate::ValUnion {
181+
sval: crate::String {
182+
type_: crate::NodeTag::T_String,
183+
sval: c"something".as_ptr() as *mut _,
184+
},
185+
};
186+
187+
let node = vu.try_as::<crate::Node>().expect("upcast to Node should succeed");
188+
let next = node.try_as::<crate::String>().expect("downcast to self should succeed");
189+
190+
assert_eq!(next.type_, crate::NodeTag::T_String);
191+
assert_eq!(next.sval, c"something".as_ptr() as *mut _);
192+
193+
assert!(
194+
node.try_as::<crate::ValUnion>().is_none(),
195+
"downcast to untagged union should fail"
196+
);
197+
}
132198
}

0 commit comments

Comments
 (0)