Skip to content

Commit 80ea231

Browse files
C enums as module consts + deprecation (#1748)
I tried to do without the deprecation warning code but I found it simply so annoying that rustc couldn't find the right names (its Levenshtein-derived algorithm doesn't reason about pathnames quite that way) that I figured it would be easiest to simply add the relevant codegen. That made it possible to distinguish between disappearing types and now-outdated names. Closes #1370.
1 parent 2c1d388 commit 80ea231

File tree

15 files changed

+198
-119
lines changed

15 files changed

+198
-119
lines changed

pgrx-examples/bad_ideas/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,11 @@ fn random_abort() {
110110
#[pg_guard]
111111
pub unsafe extern "C" fn _PG_init() {
112112
#[pg_guard]
113-
extern "C" fn random_abort_callback(event: pg_sys::XactEvent, _arg: *mut std::os::raw::c_void) {
114-
if event == pg_sys::XactEvent_XACT_EVENT_PRE_COMMIT && rand::random::<bool>() {
113+
extern "C" fn random_abort_callback(
114+
event: pg_sys::XactEvent::Type,
115+
_arg: *mut std::os::raw::c_void,
116+
) {
117+
if event == pg_sys::XactEvent::XACT_EVENT_PRE_COMMIT && rand::random::<bool>() {
115118
// panic!("aborting transaction");
116119
}
117120
}

pgrx-pg-sys/build.rs

Lines changed: 107 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@
77
//LICENSE All rights reserved.
88
//LICENSE
99
//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
10-
use bindgen::callbacks::{DeriveTrait, ImplementsTrait, MacroParsingBehavior};
10+
use bindgen::callbacks::{DeriveTrait, EnumVariantValue, ImplementsTrait, MacroParsingBehavior};
1111
use bindgen::NonCopyUnionStyle;
1212
use eyre::{eyre, WrapErr};
1313
use pgrx_pg_config::{
1414
is_supported_major_version, prefix_path, PgConfig, PgConfigSelector, Pgrx, SUPPORTED_VERSIONS,
1515
};
1616
use quote::{quote, ToTokens};
17+
use std::cell::RefCell;
1718
use std::cmp::Ordering;
1819
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
1920
use std::fs;
2021
use std::path::{self, Path, PathBuf}; // disambiguate path::Path and syn::Type::Path
2122
use std::process::{Command, Output};
23+
use std::rc::Rc;
2224
use std::sync::OnceLock;
2325
use syn::{ForeignItem, Item, ItemConst};
2426

@@ -30,44 +32,49 @@ mod build {
3032
}
3133

3234
#[derive(Debug)]
33-
struct PgrxOverrides(HashSet<String>);
35+
struct BindingOverride {
36+
ignore_macros: HashSet<&'static str>,
37+
enum_names: InnerMut<EnumMap>,
38+
}
39+
40+
type InnerMut<T> = Rc<RefCell<T>>;
41+
type EnumMap = BTreeMap<String, Vec<(String, EnumVariantValue)>>;
3442

35-
impl PgrxOverrides {
36-
fn default() -> Self {
43+
impl BindingOverride {
44+
fn new_from(enum_names: InnerMut<EnumMap>) -> Self {
3745
// these cause duplicate definition problems on linux
3846
// see: https://github.com/rust-lang/rust-bindgen/issues/687
39-
PgrxOverrides(
40-
vec![
41-
"FP_INFINITE".into(),
42-
"FP_NAN".into(),
43-
"FP_NORMAL".into(),
44-
"FP_SUBNORMAL".into(),
45-
"FP_ZERO".into(),
46-
"IPPORT_RESERVED".into(),
47+
BindingOverride {
48+
ignore_macros: HashSet::from_iter([
49+
"FP_INFINITE",
50+
"FP_NAN",
51+
"FP_NORMAL",
52+
"FP_SUBNORMAL",
53+
"FP_ZERO",
54+
"IPPORT_RESERVED",
4755
// These are just annoying due to clippy
48-
"M_E".into(),
49-
"M_LOG2E".into(),
50-
"M_LOG10E".into(),
51-
"M_LN2".into(),
52-
"M_LN10".into(),
53-
"M_PI".into(),
54-
"M_PI_2".into(),
55-
"M_PI_4".into(),
56-
"M_1_PI".into(),
57-
"M_2_PI".into(),
58-
"M_SQRT2".into(),
59-
"M_SQRT1_2".into(),
60-
"M_2_SQRTPI".into(),
61-
]
62-
.into_iter()
63-
.collect(),
64-
)
56+
"M_E",
57+
"M_LOG2E",
58+
"M_LOG10E",
59+
"M_LN2",
60+
"M_LN10",
61+
"M_PI",
62+
"M_PI_2",
63+
"M_PI_4",
64+
"M_1_PI",
65+
"M_2_PI",
66+
"M_SQRT2",
67+
"M_SQRT1_2",
68+
"M_2_SQRTPI",
69+
]),
70+
enum_names,
71+
}
6572
}
6673
}
6774

68-
impl bindgen::callbacks::ParseCallbacks for PgrxOverrides {
75+
impl bindgen::callbacks::ParseCallbacks for BindingOverride {
6976
fn will_parse_macro(&self, name: &str) -> MacroParsingBehavior {
70-
if self.0.contains(name) {
77+
if self.ignore_macros.contains(name) {
7178
bindgen::callbacks::MacroParsingBehavior::Ignore
7279
} else {
7380
bindgen::callbacks::MacroParsingBehavior::Default
@@ -90,6 +97,45 @@ impl bindgen::callbacks::ParseCallbacks for PgrxOverrides {
9097
};
9198
Some(implements_trait)
9299
}
100+
101+
// FIXME: alter types on some int macros to the actually-used types so we can stop as-casting them
102+
fn int_macro(&self, _name: &str, _value: i64) -> Option<bindgen::callbacks::IntKind> {
103+
None
104+
}
105+
106+
// FIXME: implement a... C compiler?
107+
fn func_macro(&self, _name: &str, _value: &[&[u8]]) {}
108+
109+
/// Intentionally doesn't do anything, just updates internal state.
110+
fn enum_variant_behavior(
111+
&self,
112+
enum_name: Option<&str>,
113+
variant_name: &str,
114+
variant_value: bindgen::callbacks::EnumVariantValue,
115+
) -> Option<bindgen::callbacks::EnumVariantCustomBehavior> {
116+
enum_name.inspect(|name| match name.strip_prefix("enum").unwrap_or(name).trim() {
117+
// specifically overridden enum
118+
"NodeTag" => return,
119+
name if name.contains("unnamed at") => return,
120+
// to prevent problems with BuiltinOid
121+
_ if variant_name.contains("OID") => return,
122+
name => self
123+
.enum_names
124+
.borrow_mut()
125+
.entry(name.to_string())
126+
.or_insert(Vec::new())
127+
.push((variant_name.to_string(), variant_value)),
128+
});
129+
None
130+
}
131+
132+
// FIXME: hide nodetag fields and default them to appropriate values
133+
fn field_visibility(
134+
&self,
135+
_info: bindgen::callbacks::FieldInfo<'_>,
136+
) -> Option<bindgen::FieldVisibilityKind> {
137+
None
138+
}
93139
}
94140

95141
fn main() -> eyre::Result<()> {
@@ -718,12 +764,15 @@ fn run_bindgen(
718764
let builtin_includes = includes.iter().filter_map(|p| Some(format!("-I{}", p.to_str()?)));
719765
binder = binder.clang_args(builtin_includes);
720766
};
767+
let enum_names = Rc::new(RefCell::new(BTreeMap::new()));
768+
let overrides = BindingOverride::new_from(Rc::clone(&enum_names));
721769
let bindings = binder
722770
.header(include_h.display().to_string())
723771
.clang_args(extra_bindgen_clang_args(pg_config)?)
724772
.clang_args(pg_target_include_flags(major_version, pg_config)?)
725773
.detect_include_paths(autodetect)
726-
.parse_callbacks(Box::new(PgrxOverrides::default()))
774+
.parse_callbacks(Box::new(overrides))
775+
.default_enum_style(bindgen::EnumVariation::ModuleConsts)
727776
// The NodeTag enum is closed: additions break existing values in the set, so it is not extensible
728777
.rustified_non_exhaustive_enum("NodeTag")
729778
.size_t_is_usize(true)
@@ -733,8 +782,33 @@ fn run_bindgen(
733782
.default_non_copy_union_style(NonCopyUnionStyle::ManuallyDrop)
734783
.generate()
735784
.wrap_err_with(|| format!("Unable to generate bindings for pg{major_version}"))?;
785+
let mut binding_str = bindings.to_string();
786+
drop(bindings); // So the Rc::into_inner can unwrap
787+
788+
// FIXME: do this for the Node graph instead of reparsing?
789+
let enum_names: EnumMap = Rc::into_inner(enum_names).unwrap().into_inner();
790+
binding_str.extend(enum_names.into_iter().flat_map(|(name, variants)| {
791+
const MIN_I32: i64 = i32::MIN as _;
792+
const MAX_I32: i64 = i32::MAX as _;
793+
const MAX_U32: u64 = u32::MAX as _;
794+
variants.into_iter().map(move |(variant, value)| {
795+
let (ty, value) = match value {
796+
EnumVariantValue::Boolean(b) => ("bool", b.to_string()),
797+
EnumVariantValue::Signed(v @ MIN_I32..=MAX_I32) => ("i32", v.to_string()),
798+
EnumVariantValue::Signed(v) => ("i64", v.to_string()),
799+
EnumVariantValue::Unsigned(v @ 0..=MAX_U32) => ("u32", v.to_string()),
800+
EnumVariantValue::Unsigned(v) => ("u64", v.to_string()),
801+
};
802+
format!(
803+
r#"
804+
#[deprecated(since = "0.12.0", note = "you want pg_sys::{module}::{variant}")]
805+
pub const {module}_{variant}: {ty} = {value};"#,
806+
module = &*name, // imprecise closure capture
807+
)
808+
})
809+
}));
736810

737-
Ok(bindings.to_string())
811+
Ok(binding_str)
738812
}
739813

740814
fn add_blocklists(bind: bindgen::Builder) -> bindgen::Builder {

pgrx/src/bgworkers.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ bitflags! {
6161
/// The various points in which a BackgroundWorker can be started by Postgres
6262
#[derive(Copy, Clone)]
6363
pub enum BgWorkerStartTime {
64-
PostmasterStart = pg_sys::BgWorkerStartTime_BgWorkerStart_PostmasterStart as isize,
65-
ConsistentState = pg_sys::BgWorkerStartTime_BgWorkerStart_ConsistentState as isize,
66-
RecoveryFinished = pg_sys::BgWorkerStartTime_BgWorkerStart_RecoveryFinished as isize,
64+
PostmasterStart = pg_sys::BgWorkerStartTime::BgWorkerStart_PostmasterStart as isize,
65+
ConsistentState = pg_sys::BgWorkerStartTime::BgWorkerStart_ConsistentState as isize,
66+
RecoveryFinished = pg_sys::BgWorkerStartTime::BgWorkerStart_RecoveryFinished as isize,
6767
}
6868

6969
/// Static interface into a running Background Worker
@@ -254,7 +254,7 @@ impl BackgroundWorker {
254254

255255
unsafe extern "C" fn worker_spi_sighup(_signal_args: i32) {
256256
GOT_SIGHUP.store(true, Ordering::SeqCst);
257-
pg_sys::ProcessConfigFile(pg_sys::GucContext_PGC_SIGHUP);
257+
pg_sys::ProcessConfigFile(pg_sys::GucContext::PGC_SIGHUP);
258258
pg_sys::SetLatch(pg_sys::MyLatch);
259259
}
260260

@@ -298,13 +298,14 @@ pub enum BackgroundWorkerStatus {
298298
},
299299
}
300300

301-
impl From<pg_sys::BgwHandleStatus> for BackgroundWorkerStatus {
302-
fn from(s: pg_sys::BgwHandleStatus) -> Self {
301+
impl From<pg_sys::BgwHandleStatus::Type> for BackgroundWorkerStatus {
302+
fn from(s: pg_sys::BgwHandleStatus::Type) -> Self {
303+
use pg_sys::BgwHandleStatus::*;
303304
match s {
304-
pg_sys::BgwHandleStatus_BGWH_STARTED => BackgroundWorkerStatus::Started,
305-
pg_sys::BgwHandleStatus_BGWH_NOT_YET_STARTED => BackgroundWorkerStatus::NotYetStarted,
306-
pg_sys::BgwHandleStatus_BGWH_STOPPED => BackgroundWorkerStatus::Stopped,
307-
pg_sys::BgwHandleStatus_BGWH_POSTMASTER_DIED => BackgroundWorkerStatus::PostmasterDied,
305+
BGWH_STARTED => BackgroundWorkerStatus::Started,
306+
BGWH_NOT_YET_STARTED => BackgroundWorkerStatus::NotYetStarted,
307+
BGWH_STOPPED => BackgroundWorkerStatus::Stopped,
308+
BGWH_POSTMASTER_DIED => BackgroundWorkerStatus::PostmasterDied,
308309
_ => unreachable!(),
309310
}
310311
}

pgrx/src/callbacks.rs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,17 @@ pub enum PgXactCallbackEvent {
5757
}
5858

5959
impl PgXactCallbackEvent {
60-
fn translate_pg_event(pg_event: pg_sys::XactEvent) -> Self {
60+
fn translate_pg_event(pg_event: pg_sys::XactEvent::Type) -> Self {
61+
use pg_sys::XactEvent::*;
6162
match pg_event {
62-
pg_sys::XactEvent_XACT_EVENT_ABORT => PgXactCallbackEvent::Abort,
63-
pg_sys::XactEvent_XACT_EVENT_COMMIT => PgXactCallbackEvent::Commit,
64-
pg_sys::XactEvent_XACT_EVENT_PARALLEL_ABORT => PgXactCallbackEvent::ParallelAbort,
65-
pg_sys::XactEvent_XACT_EVENT_PARALLEL_COMMIT => PgXactCallbackEvent::ParallelCommit,
66-
pg_sys::XactEvent_XACT_EVENT_PARALLEL_PRE_COMMIT => {
67-
PgXactCallbackEvent::ParallelPreCommit
68-
}
69-
pg_sys::XactEvent_XACT_EVENT_PREPARE => PgXactCallbackEvent::Prepare,
70-
pg_sys::XactEvent_XACT_EVENT_PRE_COMMIT => PgXactCallbackEvent::PreCommit,
71-
pg_sys::XactEvent_XACT_EVENT_PRE_PREPARE => PgXactCallbackEvent::PrePrepare,
63+
XACT_EVENT_ABORT => PgXactCallbackEvent::Abort,
64+
XACT_EVENT_COMMIT => PgXactCallbackEvent::Commit,
65+
XACT_EVENT_PARALLEL_ABORT => PgXactCallbackEvent::ParallelAbort,
66+
XACT_EVENT_PARALLEL_COMMIT => PgXactCallbackEvent::ParallelCommit,
67+
XACT_EVENT_PARALLEL_PRE_COMMIT => PgXactCallbackEvent::ParallelPreCommit,
68+
XACT_EVENT_PREPARE => PgXactCallbackEvent::Prepare,
69+
XACT_EVENT_PRE_COMMIT => PgXactCallbackEvent::PreCommit,
70+
XACT_EVENT_PRE_PREPARE => PgXactCallbackEvent::PrePrepare,
7271
unknown => panic!("Unrecognized XactEvent: {unknown}"),
7372
}
7473
}
@@ -164,7 +163,10 @@ where
164163

165164
// internal function that we register as an XactCallback
166165
#[pg_guard]
167-
unsafe extern "C" fn callback(event: pg_sys::XactEvent, _arg: *mut ::std::os::raw::c_void) {
166+
unsafe extern "C" fn callback(
167+
event: pg_sys::XactEvent::Type,
168+
_arg: *mut ::std::os::raw::c_void,
169+
) {
168170
let which_event = PgXactCallbackEvent::translate_pg_event(event);
169171

170172
let hooks = match which_event {
@@ -252,14 +254,13 @@ pub enum PgSubXactCallbackEvent {
252254
}
253255

254256
impl PgSubXactCallbackEvent {
255-
fn translate_pg_event(event: pg_sys::SubXactEvent) -> Self {
257+
fn translate_pg_event(event: pg_sys::SubXactEvent::Type) -> Self {
258+
use pg_sys::SubXactEvent::*;
256259
match event {
257-
pg_sys::SubXactEvent_SUBXACT_EVENT_ABORT_SUB => PgSubXactCallbackEvent::AbortSub,
258-
pg_sys::SubXactEvent_SUBXACT_EVENT_COMMIT_SUB => PgSubXactCallbackEvent::CommitSub,
259-
pg_sys::SubXactEvent_SUBXACT_EVENT_PRE_COMMIT_SUB => {
260-
PgSubXactCallbackEvent::PreCommitSub
261-
}
262-
pg_sys::SubXactEvent_SUBXACT_EVENT_START_SUB => PgSubXactCallbackEvent::StartSub,
260+
SUBXACT_EVENT_ABORT_SUB => PgSubXactCallbackEvent::AbortSub,
261+
SUBXACT_EVENT_COMMIT_SUB => PgSubXactCallbackEvent::CommitSub,
262+
SUBXACT_EVENT_PRE_COMMIT_SUB => PgSubXactCallbackEvent::PreCommitSub,
263+
SUBXACT_EVENT_START_SUB => PgSubXactCallbackEvent::StartSub,
263264
_ => panic!("Unrecognized SubXactEvent: {event}"),
264265
}
265266
}
@@ -317,7 +318,7 @@ where
317318

318319
#[pg_guard]
319320
unsafe extern "C" fn callback(
320-
event: pg_sys::SubXactEvent,
321+
event: pg_sys::SubXactEvent::Type,
321322
my_subid: pg_sys::SubTransactionId,
322323
parent_subid: pg_sys::SubTransactionId,
323324
_arg: *mut ::std::os::raw::c_void,

pgrx/src/callconv.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,7 @@ impl<'fcx> FcInfo<'fcx> {
723723
pub unsafe fn srf_return_next(&mut self) {
724724
unsafe {
725725
self.deref_fcx().call_cntr += 1;
726-
self.get_result_info().set_is_done(pg_sys::ExprDoneCond_ExprMultipleResult);
726+
self.get_result_info().set_is_done(pg_sys::ExprDoneCond::ExprMultipleResult);
727727
}
728728
}
729729

@@ -733,7 +733,7 @@ impl<'fcx> FcInfo<'fcx> {
733733
pub unsafe fn srf_return_done(&mut self) {
734734
unsafe {
735735
pg_sys::end_MultiFuncCall(self.0, self.deref_fcx());
736-
self.get_result_info().set_is_done(pg_sys::ExprDoneCond_ExprEndResult);
736+
self.get_result_info().set_is_done(pg_sys::ExprDoneCond::ExprEndResult);
737737
}
738738
}
739739

@@ -872,23 +872,23 @@ impl<'fcx> ReturnSetInfoWrapper<'fcx> {
872872
*/
873873
// These four fields are, in-practice, owned by the callee.
874874
/// Status for ValuePerCall mode.
875-
pub fn set_is_done(&mut self, value: pg_sys::ExprDoneCond) {
875+
pub fn set_is_done(&mut self, value: pg_sys::ExprDoneCond::Type) {
876876
unsafe {
877877
(*self.0).isDone = value;
878878
}
879879
}
880880
/// Status for ValuePerCall mode.
881-
pub fn get_is_done(&self) -> pg_sys::ExprDoneCond {
881+
pub fn get_is_done(&self) -> pg_sys::ExprDoneCond::Type {
882882
unsafe { (*self.0).isDone }
883883
}
884884
/// Actual return mode.
885-
pub fn set_return_mode(&mut self, return_mode: pgrx_pg_sys::SetFunctionReturnMode) {
885+
pub fn set_return_mode(&mut self, return_mode: pgrx_pg_sys::SetFunctionReturnMode::Type) {
886886
unsafe {
887887
(*self.0).returnMode = return_mode;
888888
}
889889
}
890890
/// Actual return mode.
891-
pub fn get_return_mode(&self) -> pgrx_pg_sys::SetFunctionReturnMode {
891+
pub fn get_return_mode(&self) -> pgrx_pg_sys::SetFunctionReturnMode::Type {
892892
unsafe { (*self.0).returnMode }
893893
}
894894
/// Holds the complete returned tuple set.

pgrx/src/enum_helper.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{ereport, pg_sys, PgLogLevel, PgSqlErrorCode};
1515
pub fn lookup_enum_by_oid(enumval: pg_sys::Oid) -> (String, pg_sys::Oid, f32) {
1616
let tup = unsafe {
1717
pg_sys::SearchSysCache(
18-
pg_sys::SysCacheIdentifier_ENUMOID as i32,
18+
pg_sys::SysCacheIdentifier::ENUMOID as i32,
1919
pg_sys::Datum::from(enumval),
2020
pg_sys::Datum::from(0),
2121
pg_sys::Datum::from(0),
@@ -61,7 +61,7 @@ pub fn lookup_enum_by_label(typname: &str, label: &str) -> pg_sys::Datum {
6161
let label =
6262
alloc::ffi::CString::new(label).expect("failed to convert enum typname to a CString");
6363
pg_sys::SearchSysCache(
64-
pg_sys::SysCacheIdentifier_ENUMTYPOIDNAME as i32,
64+
pg_sys::SysCacheIdentifier::ENUMTYPOIDNAME as i32,
6565
pg_sys::Datum::from(enumtypoid),
6666
pg_sys::Datum::from(label.as_ptr()),
6767
pg_sys::Datum::from(0usize),

0 commit comments

Comments
 (0)