Skip to content

Commit 3286c86

Browse files
committed
refactor GUC
1 parent 66aab7a commit 3286c86

File tree

4 files changed

+176
-142
lines changed

4 files changed

+176
-142
lines changed

pgrx-macros/src/lib.rs

Lines changed: 79 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extern crate proc_macro;
1111

1212
use proc_macro::TokenStream;
1313
use std::collections::HashSet;
14+
use std::ffi::CString;
1415

1516
use proc_macro2::Ident;
1617
use quote::{format_ident, quote, ToTokens};
@@ -1064,91 +1065,108 @@ fn impl_postgres_type(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream>
10641065
}
10651066

10661067
/// Derives the `GucEnum` trait, so that normal Rust enums can be used as a GUC.
1067-
#[proc_macro_derive(PostgresGucEnum, attributes(hidden))]
1068+
#[proc_macro_derive(PostgresGucEnum, attributes(name, hidden))]
10681069
pub fn postgres_guc_enum(input: TokenStream) -> TokenStream {
10691070
let ast = parse_macro_input!(input as syn::DeriveInput);
10701071

10711072
impl_guc_enum(ast).unwrap_or_else(|e| e.into_compile_error()).into()
10721073
}
10731074

10741075
fn impl_guc_enum(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
1075-
let mut stream = proc_macro2::TokenStream::new();
1076+
use std::str::FromStr;
1077+
use syn::parse::Parse;
10761078

1077-
// validate that we're only operating on an enum
1078-
let enum_data = match ast.data {
1079-
Data::Enum(e) => e,
1080-
_ => {
1081-
return Err(syn::Error::new(
1082-
ast.span(),
1083-
"#[derive(PostgresGucEnum)] can only be applied to enums",
1084-
))
1085-
}
1086-
};
1087-
let enum_name = ast.ident;
1088-
let enum_len = enum_data.variants.len();
1089-
1090-
let mut from_match_arms = proc_macro2::TokenStream::new();
1091-
for (idx, e) in enum_data.variants.iter().enumerate() {
1092-
let label = &e.ident;
1093-
let idx = idx as i32;
1094-
from_match_arms.extend(quote! { #idx => #enum_name::#label, })
1079+
enum GucEnumAttribute {
1080+
Name(CString),
1081+
Hidden(bool),
10951082
}
1096-
from_match_arms.extend(quote! { _ => panic!("Unrecognized ordinal ")});
10971083

1098-
let mut ordinal_match_arms = proc_macro2::TokenStream::new();
1099-
for (idx, e) in enum_data.variants.iter().enumerate() {
1100-
let label = &e.ident;
1101-
let idx = idx as i32;
1102-
ordinal_match_arms.extend(quote! { #enum_name::#label => #idx, });
1084+
impl Parse for GucEnumAttribute {
1085+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
1086+
let ident: Ident = input.parse()?;
1087+
let _: syn::token::Eq = input.parse()?;
1088+
match ident.to_string().as_str() {
1089+
"name" => input.parse::<syn::LitCStr>().map(|val| Self::Name(val.value())),
1090+
"hidden" => input.parse::<syn::LitBool>().map(|val| Self::Hidden(val.value())),
1091+
x => Err(syn::Error::new(input.span(), format!("unknown attribute {x}"))),
1092+
}
1093+
}
11031094
}
11041095

1105-
let mut build_array_body = proc_macro2::TokenStream::new();
1106-
for (idx, e) in enum_data.variants.iter().enumerate() {
1107-
let label = e.ident.to_string();
1108-
let mut hidden = false;
1109-
1110-
for att in e.attrs.iter() {
1111-
let att = quote! {#att}.to_string();
1112-
if att == "# [hidden]" {
1113-
hidden = true;
1114-
break;
1096+
// validate that we're only operating on an enum
1097+
let Data::Enum(data) = ast.data.clone() else {
1098+
return Err(syn::Error::new(
1099+
ast.span(),
1100+
"#[derive(PostgresGucEnum)] can only be applied to enums",
1101+
));
1102+
};
1103+
let ident = ast.ident.clone();
1104+
let mut config = Vec::new();
1105+
for (index, variant) in data.variants.iter().enumerate() {
1106+
let default_name = CString::from_str(&variant.ident.to_string())
1107+
.expect("the identifier contains a null character.");
1108+
let default_val = index as i32;
1109+
let default_hidden = false;
1110+
let mut name = None;
1111+
let mut hidden = None;
1112+
for attr in variant.attrs.iter() {
1113+
let tokens = attr.meta.require_name_value()?.to_token_stream();
1114+
let pair: GucEnumAttribute = syn::parse2(tokens)?;
1115+
match pair {
1116+
GucEnumAttribute::Name(value) => {
1117+
if name.replace(value).is_some() {
1118+
return Err(syn::Error::new(ast.span(), "too many #[name] attributes"));
1119+
}
1120+
}
1121+
GucEnumAttribute::Hidden(value) => {
1122+
if hidden.replace(value).is_some() {
1123+
return Err(syn::Error::new(ast.span(), "too many #[hidden] attributes"));
1124+
}
1125+
}
11151126
}
11161127
}
1117-
1118-
build_array_body.extend(quote! {
1119-
::pgrx::pgbox::PgBox::<_, ::pgrx::pgbox::AllocatedByPostgres>::with(&mut slice[#idx], |v| {
1120-
v.name = ::pgrx::memcxt::PgMemoryContexts::TopMemoryContext.pstrdup(#label);
1121-
v.val = #idx as i32;
1122-
v.hidden = #hidden;
1123-
});
1124-
});
1128+
let ident = variant.ident.clone();
1129+
let name = name.unwrap_or(default_name);
1130+
let val = default_val;
1131+
let hidden = hidden.unwrap_or(default_hidden);
1132+
config.push((ident, name, val, hidden));
11251133
}
1126-
1127-
stream.extend(quote! {
1128-
unsafe impl ::pgrx::guc::GucEnum<#enum_name> for #enum_name {
1129-
fn from_ordinal(ordinal: i32) -> #enum_name {
1134+
let config_idents = config.iter().map(|x| &x.0).collect::<Vec<_>>();
1135+
let config_names = config.iter().map(|x| &x.1).collect::<Vec<_>>();
1136+
let config_vals = config.iter().map(|x| &x.2).collect::<Vec<_>>();
1137+
let config_hiddens = config.iter().map(|x| &x.3).collect::<Vec<_>>();
1138+
1139+
Ok(quote! {
1140+
unsafe impl ::pgrx::guc::GucEnum for #ident {
1141+
fn from_ordinal(ordinal: i32) -> Self {
11301142
match ordinal {
1131-
#from_match_arms
1143+
#(#config_vals => Self::#config_idents,)*
1144+
_ => panic!("Unrecognized ordinal"),
11321145
}
11331146
}
11341147

11351148
fn to_ordinal(&self) -> i32 {
1136-
match *self {
1137-
#ordinal_match_arms
1149+
match self {
1150+
#(Self::#config_idents => #config_vals,)*
11381151
}
11391152
}
11401153

1141-
fn config_matrix() -> *const ::pgrx::pg_sys::config_enum_entry {
1142-
unsafe {
1143-
let slice = ::pgrx::memcxt::PgMemoryContexts::TopMemoryContext.palloc0_slice::<::pgrx::pg_sys::config_enum_entry>(#enum_len + 1usize);
1144-
#build_array_body
1145-
slice.as_ptr()
1146-
}
1147-
}
1154+
const CONFIG_ENUM_ENTRY: *const ::pgrx::pg_sys::config_enum_entry = [
1155+
#(
1156+
::pgrx::pg_sys::config_enum_entry {
1157+
name: #config_names.as_ptr(),
1158+
val: #config_vals,
1159+
hidden: #config_hiddens,
1160+
},
1161+
)*
1162+
::pgrx::pg_sys::config_enum_entry {
1163+
name: core::ptr::null(),
1164+
val: 0,
1165+
hidden: false,
1166+
},
1167+
].as_ptr();
11481168
}
1149-
});
1150-
1151-
Ok(stream)
1169+
})
11521170
}
11531171

11541172
#[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]

pgrx-pg-sys/include/pg18.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
#include "partitioning/partprune.h"
140140
#include "plpgsql.h"
141141
#include "postmaster/bgworker.h"
142+
#include "postmaster/interrupt.h"
142143
#include "postmaster/postmaster.h"
143144
#include "postmaster/syslogger.h"
144145
#include "replication/logical.h"

pgrx-tests/src/tests/guc_tests.rs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ mod tests {
2121
fn test_bool_guc() {
2222
static GUC: GucSetting<bool> = GucSetting::<bool>::new(true);
2323
GucRegistry::define_bool_guc(
24-
"test.bool",
25-
"test bool gucs",
26-
"test bool gucs",
24+
c"test.bool",
25+
c"test bool gucs",
26+
c"test bool gucs",
2727
&GUC,
2828
GucContext::Userset,
2929
GucFlags::default(),
@@ -41,9 +41,9 @@ mod tests {
4141
fn test_int_guc() {
4242
static GUC: GucSetting<i32> = GucSetting::<i32>::new(42);
4343
GucRegistry::define_int_guc(
44-
"test.int",
45-
"test int guc",
46-
"test int guc",
44+
c"test.int",
45+
c"test int guc",
46+
c"test int guc",
4747
&GUC,
4848
-1,
4949
42,
@@ -63,9 +63,9 @@ mod tests {
6363
fn test_mb_guc() {
6464
static GUC: GucSetting<i32> = GucSetting::<i32>::new(42);
6565
GucRegistry::define_int_guc(
66-
"test.megabytes",
67-
"test megabytes guc",
68-
"test megabytes guc",
66+
c"test.megabytes",
67+
c"test megabytes guc",
68+
c"test megabytes guc",
6969
&GUC,
7070
-1,
7171
42000,
@@ -82,9 +82,9 @@ mod tests {
8282
fn test_float_guc() {
8383
static GUC: GucSetting<f64> = GucSetting::<f64>::new(42.42);
8484
GucRegistry::define_float_guc(
85-
"test.float",
86-
"test float guc",
87-
"test float guc",
85+
c"test.float",
86+
c"test float guc",
87+
c"test float guc",
8888
&GUC,
8989
-1.0f64,
9090
43.0f64,
@@ -108,9 +108,9 @@ mod tests {
108108
static GUC: GucSetting<Option<CString>> =
109109
GucSetting::<Option<CString>>::new(Some(c"this is a test"));
110110
GucRegistry::define_string_guc(
111-
"test.string",
112-
"test string guc",
113-
"test string guc",
111+
c"test.string",
112+
c"test string guc",
113+
c"test string guc",
114114
&GUC,
115115
GucContext::Userset,
116116
GucFlags::default(),
@@ -129,9 +129,9 @@ mod tests {
129129
fn test_string_guc_null_default() {
130130
static GUC: GucSetting<Option<CString>> = GucSetting::<Option<CString>>::new(None);
131131
GucRegistry::define_string_guc(
132-
"test.string",
133-
"test string guc",
134-
"test string guc",
132+
c"test.string",
133+
c"test string guc",
134+
c"test string guc",
135135
&GUC,
136136
GucContext::Userset,
137137
GucFlags::default(),
@@ -152,12 +152,16 @@ mod tests {
152152
One,
153153
Two,
154154
Three,
155+
#[name = c"five"]
156+
Four,
157+
#[hidden = true]
158+
Six,
155159
}
156160
static GUC: GucSetting<TestEnum> = GucSetting::<TestEnum>::new(TestEnum::Two);
157161
GucRegistry::define_enum_guc(
158-
"test.enum",
159-
"test enum guc",
160-
"test enum guc",
162+
c"test.enum",
163+
c"test enum guc",
164+
c"test enum guc",
161165
&GUC,
162166
GucContext::Userset,
163167
GucFlags::default(),
@@ -169,6 +173,9 @@ mod tests {
169173

170174
Spi::run("SET test.enum = 'three'").expect("SPI failed");
171175
assert_eq!(GUC.get(), TestEnum::Three);
176+
177+
Spi::run("SET test.enum = 'five'").expect("SPI failed");
178+
assert_eq!(GUC.get(), TestEnum::Four);
172179
}
173180

174181
#[pg_test]
@@ -179,17 +186,17 @@ mod tests {
179186
static GUC_NO_SHOW: GucSetting<bool> = GucSetting::<bool>::new(true);
180187
static GUC_NO_RESET_ALL: GucSetting<bool> = GucSetting::<bool>::new(true);
181188
GucRegistry::define_bool_guc(
182-
"test.no_show",
183-
"test no show gucs",
184-
"test no show gucs",
189+
c"test.no_show",
190+
c"test no show gucs",
191+
c"test no show gucs",
185192
&GUC_NO_SHOW,
186193
GucContext::Userset,
187194
no_show_flag,
188195
);
189196
GucRegistry::define_bool_guc(
190-
"test.no_reset_all",
191-
"test no reset gucs",
192-
"test no reset gucs",
197+
c"test.no_reset_all",
198+
c"test no reset gucs",
199+
c"test no reset gucs",
193200
&GUC_NO_RESET_ALL,
194201
GucContext::Userset,
195202
GucFlags::NO_RESET_ALL,

0 commit comments

Comments
 (0)