From 3c625d83ff0ee9b8cd0d26033989fb625228bed5 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Sun, 6 Oct 2019 20:33:23 +0300 Subject: [PATCH] Add support for PostgreSQL 12 PostgreSQL 12 brings changes to FunctionCallInfoData (the way arguments are passed to functions). Upstream commit: https://github.com/postgres/postgres/commit/a9c35cf85ca1ff72f16f0f10d7ddee6e582b62b8 --- pg-extend/Cargo.toml | 1 + pg-extend/build.rs | 3 ++- pg-extend/src/lib.rs | 47 +++++++++++++++++++++++++++++---------- pg-extend/src/pg_datum.rs | 8 +++++++ pg-extend/src/pg_fdw.rs | 2 +- pg-extern-attr/src/lib.rs | 19 +++++++--------- 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/pg-extend/Cargo.toml b/pg-extend/Cargo.toml index 3fbba4e1..f618f590 100644 --- a/pg-extend/Cargo.toml +++ b/pg-extend/Cargo.toml @@ -22,6 +22,7 @@ fdw = [] postgres-9 = ["fdw"] postgres-10 = ["fdw"] postgres-11 = [] +postgres-12 = [] [dependencies] diff --git a/pg-extend/build.rs b/pg-extend/build.rs index 2d7df28b..063d1287 100644 --- a/pg-extend/build.rs +++ b/pg-extend/build.rs @@ -60,7 +60,7 @@ fn main() { .expect("Couldn't write bindings!"); let feature_version = get_postgres_feature_version(pg_include); - println!("cargo:rustc-cfg=feature=\"{}\"", feature_version) + println!("cargo:rustc-cfg=feature=\"{}\"", feature_version); } fn include_dir() -> Result { @@ -122,6 +122,7 @@ fn get_postgres_feature_version(pg_include: String) -> &'static str { ["9", _] => "postgres-9", ["10"] => "postgres-10", ["11"] => "postgres-11", + ["12"] => "postgres-12", val => panic!("unknown Postgres version {:?}", val), } } diff --git a/pg-extend/src/lib.rs b/pg-extend/src/lib.rs index 9baa1dcd..9affd99f 100644 --- a/pg-extend/src/lib.rs +++ b/pg-extend/src/lib.rs @@ -60,21 +60,43 @@ macro_rules! pg_magic { }; } -/// Returns the slice of Datums, and a parallel slice which specifies if the Datum passed in is (SQL) NULL +#[cfg(feature = "postgres-12")] +type FunctionCallInfoData = pg_sys::FunctionCallInfoBaseData; +#[cfg(not(feature = "postgres-12"))] +type FunctionCallInfoData = pg_sys::FunctionCallInfoData; + +/// Returns an iterator of argument Datums pub fn get_args<'a>( - func_call_info: &'a pg_sys::FunctionCallInfoData, -) -> ( - impl 'a + Iterator, - impl 'a + Iterator, -) { + func_call_info: &'a FunctionCallInfoData, +) -> impl 'a + Iterator> { let num_args = func_call_info.nargs as usize; - let args = func_call_info.arg[..num_args].iter(); - let args_null = func_call_info.argnull[..num_args] + // PostgreSQL 12+: Convert from pg_sys::NullableDatum + #[cfg(feature = "postgres-12")] + return unsafe { func_call_info.args.as_slice(num_args) } .iter() - .map(|b| pg_bool::Bool::from(*b)); - - (args, args_null) + .map(|nullable| { + if nullable.isnull { + None + } else { + Some(nullable.value) + } + }); + + // Older versions store two separate arrays for 'isnull' and datums + #[cfg(not(feature = "postgres-12"))] + return { + let args = &func_call_info.arg[..num_args]; + let args_null = &func_call_info.argnull[..num_args]; + + args.iter().zip(args_null.iter()).map(|(value, isnull)| { + if pg_bool::Bool::from(*isnull).into() { + None + } else { + Some(*value) + } + }) + }; } /// Information for a longjmp @@ -126,7 +148,8 @@ pub fn register_panic_handler() { pub(crate) unsafe fn guard_pg R>(f: F) -> R { // setup the check protection let original_exception_stack: *mut pg_sys::sigjmp_buf = pg_sys::PG_exception_stack; - let mut local_exception_stack: mem::MaybeUninit = mem::MaybeUninit::uninit(); + let mut local_exception_stack: mem::MaybeUninit = + mem::MaybeUninit::uninit(); let jumped = pg_sys::sigsetjmp( // grab a mutable reference, cast to a mutabl pointr, then case to the expected erased pointer type local_exception_stack.as_mut_ptr() as *mut pg_sys::sigjmp_buf as *mut _, diff --git a/pg-extend/src/pg_datum.rs b/pg-extend/src/pg_datum.rs index 0cde9e9b..e4fad6f6 100644 --- a/pg-extend/src/pg_datum.rs +++ b/pg-extend/src/pg_datum.rs @@ -35,6 +35,14 @@ impl<'mc> PgDatum<'mc> { PgDatum(datum, PhantomData) } + /// Returns a new PgDatum wrapper if you already have Option + pub unsafe fn from_option( + _memory_context: &'mc PgAllocator, + datum: Option, + ) -> PgDatum<'mc> { + PgDatum(datum, PhantomData) + } + /// Return true if this Datum is None /// /// # Notes diff --git a/pg-extend/src/pg_fdw.rs b/pg-extend/src/pg_fdw.rs index 3ae3f89b..66a25396 100644 --- a/pg-extend/src/pg_fdw.rs +++ b/pg-extend/src/pg_fdw.rs @@ -8,7 +8,7 @@ // FDW on PostgreSQL 11+ is not supported. :( // If anyone tries to enable "fdw" feature with newer Postgres, throw error. -#[cfg(feature = "postgres-11")] +#[cfg(any(feature = "postgres-11", feature = "postgres-12"))] compile_error!("pg-extend-rs does not support FDW on PostgreSQL 11 or newer. See https://github.com/bluejekyll/pg-extend-rs/issues/49"); use std::boxed::Box; diff --git a/pg-extern-attr/src/lib.rs b/pg-extern-attr/src/lib.rs index d266fdd2..0c4d2c07 100644 --- a/pg-extern-attr/src/lib.rs +++ b/pg-extern-attr/src/lib.rs @@ -10,11 +10,9 @@ extern crate proc_macro; extern crate proc_macro2; #[macro_use] -extern crate syn; -#[macro_use] extern crate quote; - -mod lifetime; +#[macro_use] +extern crate syn; use proc_macro2::{Ident, Span, TokenStream}; use quote::ToTokens; @@ -23,6 +21,8 @@ use syn::spanned::Spanned; use syn::token::Comma; use syn::Type; +mod lifetime; + /// A type that represents that PgAllocator is an argument to the Rust function. type HasPgAllocatorArg = bool; @@ -104,14 +104,11 @@ fn extract_arg_data(arg_types: &[Type]) -> (TokenStream, HasPgAllocatorArg) { let arg_error = format!("unsupported function argument type for {}", arg_name); let get_arg = quote_spanned!( arg_type.span()=> + let datum = args.next().expect("wrong number of args passed into get_args for args?"); let #arg_name: #arg_type = unsafe { pg_extend::pg_datum::TryFromPgDatum::try_from( &memory_context, - pg_extend::pg_datum::PgDatum::from_raw( - &memory_context, - *args.next().expect("wrong number of args passed into get_args for args?"), - args_null.next().expect("wrong number of args passed into get_args for args_null?") - ), + pg_extend::pg_datum::PgDatum::from_option(&memory_context, datum), ) .expect(#arg_error) }; @@ -337,7 +334,7 @@ fn impl_info_for_fn(item: &syn::Item) -> TokenStream { // All params will be in the "current" memory context at the call-site let memory_context = PgAllocator::current_context(); - let func_info: &mut pg_extend::pg_sys::FunctionCallInfoData = unsafe { + let func_info = unsafe { func_call_info .as_mut() .expect("func_call_info was unexpectedly NULL") @@ -346,7 +343,7 @@ fn impl_info_for_fn(item: &syn::Item) -> TokenStream { // guard the Postgres process against the panic, and give us an oportunity to cleanup let panic_result = panic::catch_unwind(|| { // extract the argument list - let (mut args, mut args_null) = pg_extend::get_args(func_info); + let mut args = pg_extend::get_args(func_info); // arbitrary Datum conversions occur here, and could panic // so this is inside the catch unwind