diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index e7460632f..ca5e2e982 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -20,7 +20,7 @@ jobs: matrix: version: [10, 11, 12, 13, 14] os: ["ubuntu-latest"] - examples: ["aggregate", "arrays", "bad_ideas", "bgworker", "bytea", "custom_types", "custom_sql", "errors", "operators", "schemas", "shmem", "spi", "srf", "strings", "triggers"] + examples: ["aggregate", "arrays", "bad_ideas", "bgworker", "bytea", "custom_types", "custom_sql", "errors", "nostd", "operators", "schemas", "shmem", "spi", "srf", "strings", "triggers"] steps: - uses: actions/checkout@v2 diff --git a/Cargo.lock b/Cargo.lock index 85c588463..37c2f3f11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -541,6 +541,22 @@ dependencies = [ "generic-array", ] +[[package]] +name = "cstr_core" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644828c273c063ab0d39486ba42a5d1f3a499d35529c759e763a9c6cb8a0fb08" +dependencies = [ + "cty", + "memchr", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + [[package]] name = "debugid" version = "0.7.2" @@ -1405,6 +1421,7 @@ version = "0.2.6" dependencies = [ "atomic-traits", "bitflags", + "cstr_core", "enum-primitive-derive", "eyre", "hash32", diff --git a/Cargo.toml b/Cargo.toml index 60ff11a47..7a129ff84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,8 @@ exclude = [ "pgx-examples/custom_types", "pgx-examples/custom_sql", "pgx-examples/errors", + "pgx-examples/nostd", + "pgx-examples/operators", "pgx-examples/schemas", "pgx-examples/shmem", "pgx-examples/spi", diff --git a/pgx-examples/aggregate/src/lib.rs b/pgx-examples/aggregate/src/lib.rs index 0cca0e83d..fb6c5593f 100644 --- a/pgx-examples/aggregate/src/lib.rs +++ b/pgx-examples/aggregate/src/lib.rs @@ -1,7 +1,7 @@ -// use pgx::datum::sql_entity_graph::aggregate::{FinalizeModify, ParallelOption}; +use pgx::cstr_core::CStr; use pgx::*; use serde::{Deserialize, Serialize}; -use std::{ffi::CStr, str::FromStr}; +use std::str::FromStr; pg_module_magic!(); @@ -14,7 +14,10 @@ pub struct IntegerAvgState { impl IntegerAvgState { #[inline(always)] - fn state(mut current: ::State, arg: ::Args) -> ::State { + fn state( + mut current: ::State, + arg: ::Args, + ) -> ::State { current.sum += arg; current.n += 1; current @@ -65,7 +68,11 @@ impl Aggregate for IntegerAvgState { const INITIAL_CONDITION: Option<&'static str> = Some("0,0"); #[pgx(parallel_safe, immutable)] - fn state(current: Self::State, arg: Self::Args, _fcinfo: pg_sys::FunctionCallInfo) -> Self::State { + fn state( + current: Self::State, + arg: Self::Args, + _fcinfo: pg_sys::FunctionCallInfo, + ) -> Self::State { Self::state(current, arg) } @@ -130,10 +137,7 @@ mod tests { let avg_state = IntegerAvgState::state(avg_state, 1); let avg_state = IntegerAvgState::state(avg_state, 2); let avg_state = IntegerAvgState::state(avg_state, 3); - assert_eq!( - 2, - IntegerAvgState::finalize(avg_state), - ); + assert_eq!(2, IntegerAvgState::finalize(avg_state),); } #[pg_test] diff --git a/pgx-examples/custom_types/src/fixed_size.rs b/pgx-examples/custom_types/src/fixed_size.rs index eae81ba15..bcc9ed030 100644 --- a/pgx-examples/custom_types/src/fixed_size.rs +++ b/pgx-examples/custom_types/src/fixed_size.rs @@ -1,5 +1,5 @@ +use pgx::cstr_core::CStr; use pgx::*; -use std::ffi::CStr; use std::str::FromStr; #[derive(Copy, Clone, PostgresType)] diff --git a/pgx-examples/nostd/.cargo/config b/pgx-examples/nostd/.cargo/config new file mode 100644 index 000000000..3d5465ea3 --- /dev/null +++ b/pgx-examples/nostd/.cargo/config @@ -0,0 +1,16 @@ +# Auto-generated by pgx. You may edit this, or delete it to have a new one created. + +[target.x86_64-unknown-linux-gnu] +linker = "./.cargo/pgx-linker-script.sh" + +[target.aarch64-unknown-linux-gnu] +linker = "./.cargo/pgx-linker-script.sh" + +[target.x86_64-apple-darwin] +linker = "./.cargo/pgx-linker-script.sh" + +[target.aarch64-apple-darwin] +linker = "./.cargo/pgx-linker-script.sh" + +[target.x86_64-unknown-freebsd] +linker = "./.cargo/pgx-linker-script.sh" diff --git a/pgx-examples/nostd/.cargo/pgx-linker-script.sh b/pgx-examples/nostd/.cargo/pgx-linker-script.sh new file mode 100755 index 000000000..c432e149c --- /dev/null +++ b/pgx-examples/nostd/.cargo/pgx-linker-script.sh @@ -0,0 +1,19 @@ +#! /usr/bin/env bash +# Auto-generated by pgx. You may edit this, or delete it to have a new one created. + +if [[ $CARGO_BIN_NAME == "sql-generator" ]]; then + UNAME=$(uname) + if [[ $UNAME == "Darwin" ]]; then + TEMP=$(mktemp pgx-XXX) + echo "*_pgx_internals_*" > ${TEMP} + gcc -exported_symbols_list ${TEMP} $@ + rm -rf ${TEMP} + else + TEMP=$(mktemp pgx-XXX) + echo "{ __pgx_internals_*; };" > ${TEMP} + gcc -Wl,-dynamic-list=${TEMP} $@ + rm -rf ${TEMP} + fi +else + gcc -Wl,-undefined,dynamic_lookup $@ +fi diff --git a/pgx-examples/nostd/.gitignore b/pgx-examples/nostd/.gitignore new file mode 100644 index 000000000..3f8fd26b1 --- /dev/null +++ b/pgx-examples/nostd/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.idea/ +/target +*.iml +**/*.rs.bk +Cargo.lock +sql/nostd-1.0.sql diff --git a/pgx-examples/nostd/Cargo.toml b/pgx-examples/nostd/Cargo.toml new file mode 100644 index 000000000..5132f228a --- /dev/null +++ b/pgx-examples/nostd/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "nostd" +version = "0.0.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +# remove this empty 'workspace' declaration if compiling outside of 'pgx' +[workspace] + +[features] +default = ["pg13"] +pg10 = ["pgx/pg10", "pgx-tests/pg10" ] +pg11 = ["pgx/pg11", "pgx-tests/pg11" ] +pg12 = ["pgx/pg12", "pgx-tests/pg12" ] +pg13 = ["pgx/pg13", "pgx-tests/pg13" ] +pg14 = ["pgx/pg14", "pgx-tests/pg14" ] +pg_test = [] + +[dependencies] +pgx = { path = "../../../pgx/pgx/", default-features = false } +serde = "1.0.114" + +[dev-dependencies] +pgx-tests = { path = "../../../pgx/pgx-tests" } + +# uncomment these if compiling outside of 'pgx' +# [profile.dev] +# panic = "unwind" +# lto = "thin" + +# [profile.release] +# panic = "unwind" +# opt-level = 3 +# lto = "fat" +# codegen-units = 1 \ No newline at end of file diff --git a/pgx-examples/nostd/nostd.control b/pgx-examples/nostd/nostd.control new file mode 100644 index 000000000..1fb3fb6f8 --- /dev/null +++ b/pgx-examples/nostd/nostd.control @@ -0,0 +1,5 @@ +comment = 'nostd: Created by pgx' +default_version = '1.0' +module_pathname = '$libdir/nostd' +relocatable = false +superuser = false diff --git a/pgx-examples/nostd/src/bin/sql-generator.rs b/pgx-examples/nostd/src/bin/sql-generator.rs new file mode 100644 index 000000000..b15aebfd2 --- /dev/null +++ b/pgx-examples/nostd/src/bin/sql-generator.rs @@ -0,0 +1,2 @@ +/* Auto-generated by pgx. You may edit this, or delete it to have a new one created. */ +pgx::pg_binary_magic!(nostd); diff --git a/pgx-examples/nostd/src/lib.rs b/pgx-examples/nostd/src/lib.rs new file mode 100644 index 000000000..f4edd9662 --- /dev/null +++ b/pgx-examples/nostd/src/lib.rs @@ -0,0 +1,51 @@ +//! This exists just to make sure we can compile various things under `#![no_std]` + +#![no_std] +extern crate alloc; + +use pgx::*; +use serde::{Deserialize, Serialize}; + +use alloc::string::String; + +pg_module_magic!(); + +/// standard Rust equality/comparison derives +#[derive(Eq, PartialEq, Ord, Hash, PartialOrd)] + +/// Support using this struct as a Postgres type, which the easy way requires Serde +#[derive(PostgresType, Serialize, Deserialize)] + +/// automatically generate =, <> SQL operator functions +#[derive(PostgresEq)] + +/// automatically generate <, >, <=, >=, and a "_cmp" SQL functions +/// When "PostgresEq" is also derived, pgx also creates an "opclass" (and family) +/// so that the type can be used in indexes `USING btree` +#[derive(PostgresOrd)] + +/// automatically generate a "_hash" function, and the necessary "opclass" (and family) +/// so the type can also be used in indexes `USING hash` +#[derive(PostgresHash)] +pub struct Thing(String); + +#[derive(PostgresType, Serialize, Deserialize, Eq, PartialEq)] +pub struct MyType { + value: i32, +} + +#[pg_operator] +#[opname(=)] +fn my_eq(left: MyType, right: MyType) -> bool { + left == right +} + +#[pg_extern] +fn hello_nostd() -> &'static str { + "Hello, nostd" +} + +#[pg_extern] +fn echo(input: String) -> String { + input +} diff --git a/pgx-macros/src/lib.rs b/pgx-macros/src/lib.rs index f053aa150..7824fc416 100644 --- a/pgx-macros/src/lib.rs +++ b/pgx-macros/src/lib.rs @@ -294,12 +294,12 @@ extension_sql!(r#"\ ); #[pg_extern(immutable)] -fn complex_in(input: &std::ffi::CStr) -> PgBox { +fn complex_in(input: &pgx::cstr_core::CStr) -> PgBox { todo!() } #[pg_extern(immutable)] -fn complex_out(complex: PgBox) -> &'static std::ffi::CStr { +fn complex_out(complex: PgBox) -> &'static pgx::cstr_core::CStr { todo!() } @@ -735,12 +735,12 @@ fn impl_postgres_type(ast: DeriveInput) -> proc_macro2::TokenStream { impl #generics JsonInOutFuncs #inout_generics for #name #generics {} #[pg_extern(immutable,parallel_safe)] - pub fn #funcname_in #generics(input: &#lifetime std::ffi::CStr) -> #name #generics { + pub fn #funcname_in #generics(input: &#lifetime pgx::cstr_core::CStr) -> #name #generics { #name::input(input) } #[pg_extern(immutable,parallel_safe)] - pub fn #funcname_out #generics(input: #name #generics) -> &#lifetime std::ffi::CStr { + pub fn #funcname_out #generics(input: #name #generics) -> &#lifetime pgx::cstr_core::CStr { let mut buffer = StringInfo::new(); input.output(&mut buffer); buffer.into() @@ -751,12 +751,12 @@ fn impl_postgres_type(ast: DeriveInput) -> proc_macro2::TokenStream { // otherwise if it's InOutFuncs our _in/_out functions use an owned type instance stream.extend(quote! { #[pg_extern(immutable,parallel_safe)] - pub fn #funcname_in #generics(input: &#lifetime std::ffi::CStr) -> #name #generics { + pub fn #funcname_in #generics(input: &#lifetime pgx::cstr_core::CStr) -> #name #generics { #name::input(input) } #[pg_extern(immutable,parallel_safe)] - pub fn #funcname_out #generics(input: #name #generics) -> &#lifetime std::ffi::CStr { + pub fn #funcname_out #generics(input: #name #generics) -> &#lifetime pgx::cstr_core::CStr { let mut buffer = StringInfo::new(); input.output(&mut buffer); buffer.into() @@ -766,12 +766,12 @@ fn impl_postgres_type(ast: DeriveInput) -> proc_macro2::TokenStream { // otherwise if it's PgVarlenaInOutFuncs our _in/_out functions use a PgVarlena stream.extend(quote! { #[pg_extern(immutable,parallel_safe)] - pub fn #funcname_in #generics(input: &#lifetime std::ffi::CStr) -> pgx::PgVarlena<#name #generics> { + pub fn #funcname_in #generics(input: &#lifetime pgx::cstr_core::CStr) -> pgx::PgVarlena<#name #generics> { #name::input(input) } #[pg_extern(immutable,parallel_safe)] - pub fn #funcname_out #generics(input: pgx::PgVarlena<#name #generics>) -> &#lifetime std::ffi::CStr { + pub fn #funcname_out #generics(input: pgx::PgVarlena<#name #generics>) -> &#lifetime pgx::cstr_core::CStr { let mut buffer = StringInfo::new(); input.output(&mut buffer); buffer.into() diff --git a/pgx-tests/src/tests/postgres_type_tests.rs b/pgx-tests/src/tests/postgres_type_tests.rs index 94b397384..c5cc3e07b 100644 --- a/pgx-tests/src/tests/postgres_type_tests.rs +++ b/pgx-tests/src/tests/postgres_type_tests.rs @@ -1,6 +1,6 @@ +use pgx::cstr_core::CStr; use pgx::*; use serde::{Deserialize, Serialize}; -use std::ffi::CStr; use std::str::FromStr; #[derive(Copy, Clone, PostgresType)] diff --git a/pgx-utils/src/sql_entity_graph/extension_sql.rs b/pgx-utils/src/sql_entity_graph/extension_sql.rs index f4700f9d4..37969b7e2 100644 --- a/pgx-utils/src/sql_entity_graph/extension_sql.rs +++ b/pgx-utils/src/sql_entity_graph/extension_sql.rs @@ -90,6 +90,9 @@ impl ToTokens for ExtensionSqlFile { let inv = quote! { #[no_mangle] pub extern "C" fn #sql_graph_entity_fn_name() -> pgx::datum::sql_entity_graph::SqlGraphEntity { + extern crate alloc; + use alloc::vec::Vec; + use alloc::vec; let submission = pgx::datum::sql_entity_graph::ExtensionSqlEntity { sql: include_str!(#path), module_path: module_path!(), @@ -194,6 +197,9 @@ impl ToTokens for ExtensionSql { let inv = quote! { #[no_mangle] pub extern "C" fn #sql_graph_entity_fn_name() -> pgx::datum::sql_entity_graph::SqlGraphEntity { + extern crate alloc; + use alloc::vec::Vec; + use alloc::vec; let submission = pgx::datum::sql_entity_graph::ExtensionSqlEntity { sql: #sql, module_path: module_path!(), diff --git a/pgx-utils/src/sql_entity_graph/pg_extern/mod.rs b/pgx-utils/src/sql_entity_graph/pg_extern/mod.rs index 2ccf3fb05..227659f07 100644 --- a/pgx-utils/src/sql_entity_graph/pg_extern/mod.rs +++ b/pgx-utils/src/sql_entity_graph/pg_extern/mod.rs @@ -229,6 +229,9 @@ impl ToTokens for PgExtern { #[no_mangle] pub extern "C" fn #sql_graph_entity_fn_name() -> pgx::datum::sql_entity_graph::SqlGraphEntity { use core::any::TypeId; + extern crate alloc; + use alloc::vec::Vec; + use alloc::vec; let submission = pgx::datum::sql_entity_graph::PgExternEntity { name: #name, unaliased_name: stringify!(#ident), diff --git a/pgx-utils/src/sql_entity_graph/pg_schema.rs b/pgx-utils/src/sql_entity_graph/pg_schema.rs index 3e95097ca..5fc970637 100644 --- a/pgx-utils/src/sql_entity_graph/pg_schema.rs +++ b/pgx-utils/src/sql_entity_graph/pg_schema.rs @@ -64,7 +64,10 @@ impl ToTokens for Schema { updated_content.push(syn::parse_quote! { #[no_mangle] pub extern "C" fn #sql_graph_entity_fn_name() -> pgx::datum::sql_entity_graph::SqlGraphEntity { - let submission = pgx::datum::sql_entity_graph::SchemaEntity { + extern crate alloc; + use alloc::vec::Vec; + use alloc::vec; + let submission = pgx::datum::sql_entity_graph::SchemaEntity { module_path: module_path!(), name: stringify!(#ident), file: file!(), diff --git a/pgx-utils/src/sql_entity_graph/postgres_enum.rs b/pgx-utils/src/sql_entity_graph/postgres_enum.rs index 54df4fce9..6c43880a9 100644 --- a/pgx-utils/src/sql_entity_graph/postgres_enum.rs +++ b/pgx-utils/src/sql_entity_graph/postgres_enum.rs @@ -87,6 +87,9 @@ impl ToTokens for PostgresEnum { let inv = quote! { #[no_mangle] pub extern "C" fn #sql_graph_entity_fn_name() -> pgx::datum::sql_entity_graph::SqlGraphEntity { + extern crate alloc; + use alloc::vec::Vec; + use alloc::vec; let mut mappings = Default::default(); <#name #ty_generics as pgx::datum::WithTypeIds>::register_with_refs(&mut mappings, stringify!(#name).to_string()); pgx::datum::WithSizedTypeIds::<#name #ty_generics>::register_sized_with_refs(&mut mappings, stringify!(#name).to_string()); diff --git a/pgx-utils/src/sql_entity_graph/postgres_hash.rs b/pgx-utils/src/sql_entity_graph/postgres_hash.rs index 8c0df48a1..c06e50206 100644 --- a/pgx-utils/src/sql_entity_graph/postgres_hash.rs +++ b/pgx-utils/src/sql_entity_graph/postgres_hash.rs @@ -86,6 +86,9 @@ impl ToTokens for PostgresHash { #[no_mangle] pub extern "C" fn #sql_graph_entity_fn_name() -> pgx::datum::sql_entity_graph::SqlGraphEntity { use core::any::TypeId; + extern crate alloc; + use alloc::vec::Vec; + use alloc::vec; let submission = pgx::datum::sql_entity_graph::PostgresHashEntity { name: stringify!(#name), file: file!(), diff --git a/pgx-utils/src/sql_entity_graph/postgres_ord.rs b/pgx-utils/src/sql_entity_graph/postgres_ord.rs index 75cfe3b88..d270030d1 100644 --- a/pgx-utils/src/sql_entity_graph/postgres_ord.rs +++ b/pgx-utils/src/sql_entity_graph/postgres_ord.rs @@ -86,6 +86,9 @@ impl ToTokens for PostgresOrd { #[no_mangle] pub extern "C" fn #sql_graph_entity_fn_name() -> pgx::datum::sql_entity_graph::SqlGraphEntity { use core::any::TypeId; + extern crate alloc; + use alloc::vec::Vec; + use alloc::vec; let submission = pgx::datum::sql_entity_graph::PostgresOrdEntity { name: stringify!(#name), file: file!(), diff --git a/pgx-utils/src/sql_entity_graph/postgres_type.rs b/pgx-utils/src/sql_entity_graph/postgres_type.rs index bea3d9904..805f0bdb5 100644 --- a/pgx-utils/src/sql_entity_graph/postgres_type.rs +++ b/pgx-utils/src/sql_entity_graph/postgres_type.rs @@ -130,6 +130,11 @@ impl ToTokens for PostgresType { let inv = quote! { #[no_mangle] pub extern "C" fn #sql_graph_entity_fn_name() -> pgx::datum::sql_entity_graph::SqlGraphEntity { + extern crate alloc; + use alloc::vec::Vec; + use alloc::vec; + use alloc::string::{String, ToString}; + let mut mappings = Default::default(); <#name #ty_generics as pgx::datum::WithTypeIds>::register_with_refs( &mut mappings, diff --git a/pgx/Cargo.toml b/pgx/Cargo.toml index 846737742..38a06d6c1 100644 --- a/pgx/Cargo.toml +++ b/pgx/Cargo.toml @@ -30,6 +30,7 @@ no-default-features = true rustc-args = ["--cfg", "docsrs"] [dependencies] +cstr_core = "0.2.5" enum-primitive-derive = "0.2.2" num-traits = "0.2.14" seahash = "4.1.0" diff --git a/pgx/src/datum/from.rs b/pgx/src/datum/from.rs index 33277a62a..678fefa88 100644 --- a/pgx/src/datum/from.rs +++ b/pgx/src/datum/from.rs @@ -270,6 +270,26 @@ impl<'a> FromDatum for &'a std::ffi::CStr { } } +impl<'a> FromDatum for &'a crate::cstr_core::CStr { + const NEEDS_TYPID: bool = false; + #[inline] + unsafe fn from_datum( + datum: pg_sys::Datum, + is_null: bool, + _: pg_sys::Oid, + ) -> Option<&'a crate::cstr_core::CStr> { + if is_null { + None + } else if datum == 0 { + panic!("a cstring Datum was flagged as non-null but the datum is zero"); + } else { + Some(crate::cstr_core::CStr::from_ptr( + datum as *const std::os::raw::c_char, + )) + } + } +} + /// for bytea impl<'a> FromDatum for &'a [u8] { const NEEDS_TYPID: bool = false; diff --git a/pgx/src/datum/into.rs b/pgx/src/datum/into.rs index 5171f7bf4..5de216861 100644 --- a/pgx/src/datum/into.rs +++ b/pgx/src/datum/into.rs @@ -220,6 +220,17 @@ impl<'a> IntoDatum for &'a std::ffi::CStr { } } +impl<'a> IntoDatum for &'a crate::cstr_core::CStr { + #[inline] + fn into_datum(self) -> Option { + Some(self.as_ptr() as pg_sys::Datum) + } + + fn type_oid() -> u32 { + pg_sys::CSTRINGOID + } +} + /// for bytea impl<'a> IntoDatum for &'a [u8] { #[inline] diff --git a/pgx/src/datum/varlena.rs b/pgx/src/datum/varlena.rs index 70ffc45ef..1ffba265c 100644 --- a/pgx/src/datum/varlena.rs +++ b/pgx/src/datum/varlena.rs @@ -43,7 +43,6 @@ impl Clone for PallocdVarlena { /// ## Example /// /// ```rust -/// use std::ffi::CStr; /// use std::str::FromStr; /// /// use crate::pgx::*; @@ -57,7 +56,7 @@ impl Clone for PallocdVarlena { /// } /// /// impl PgVarlenaInOutFuncs for MyType { -/// fn input(input: &std::ffi::CStr) -> PgVarlena { +/// fn input(input: &pgx::cstr_core::CStr) -> PgVarlena { /// let mut iter = input.to_str().unwrap().split(','); /// let (a, b, c) = (iter.next(), iter.next(), iter.next()); /// diff --git a/pgx/src/inoutfuncs.rs b/pgx/src/inoutfuncs.rs index dd6aa9974..ea050c63d 100644 --- a/pgx/src/inoutfuncs.rs +++ b/pgx/src/inoutfuncs.rs @@ -15,7 +15,7 @@ pub trait PgVarlenaInOutFuncs { /// Given a string representation of `Self`, parse it into a `PgVarlena`. /// /// It is expected that malformed input will raise an `error!()` or `panic!()` - fn input(input: &std::ffi::CStr) -> PgVarlena + fn input(input: &crate::cstr_core::CStr) -> PgVarlena where Self: Copy + Sized; @@ -29,7 +29,7 @@ pub trait InOutFuncs { /// Given a string representation of `Self`, parse it into `Self`. /// /// It is expected that malformed input will raise an `error!()` or `panic!()` - fn input(input: &std::ffi::CStr) -> Self + fn input(input: &crate::cstr_core::CStr) -> Self where Self: Sized; @@ -41,7 +41,7 @@ pub trait InOutFuncs { /// **not** also have the `#[inoutfuncs]` attribute macro pub trait JsonInOutFuncs<'de>: serde::de::Deserialize<'de> + serde::ser::Serialize { /// Uses `serde_json` to deserialize the input, which is assumed to be JSON - fn input(input: &'de std::ffi::CStr) -> Self { + fn input(input: &'de crate::cstr_core::CStr) -> Self { serde_json::from_str(input.to_str().expect("text input is not valid UTF8")) .expect("failed to deserialize json") } diff --git a/pgx/src/lib.rs b/pgx/src/lib.rs index a0ec5801a..d6090ddf8 100644 --- a/pgx/src/lib.rs +++ b/pgx/src/lib.rs @@ -94,6 +94,8 @@ pub use pgx_pg_sys as pg_sys; // the module only, not its contents pub use pgx_pg_sys::submodules::*; pub use pgx_pg_sys::PgBuiltInOids; // reexport this so it looks like it comes from here +pub use cstr_core; + use core::any::TypeId; use once_cell::sync::Lazy; use std::collections::HashSet; @@ -194,6 +196,7 @@ pub static DEFAULT_TYPEID_SQL_MAPPING: Lazy> = Lazy::new map_type!(m, String, "text"); map_type!(m, &std::ffi::CStr, "cstring"); + map_type!(m, &crate::cstr_core::CStr, "cstring"); map_type!(m, (), "void"); map_type!(m, i8, "\"char\""); map_type!(m, i16, "smallint"); @@ -284,29 +287,28 @@ macro_rules! pg_magic_func { #[allow(unused)] #[link_name = "Pg_magic_func"] pub extern "C" fn Pg_magic_func() -> &'static pgx::pg_sys::Pg_magic_struct { + use core::mem::size_of; use pgx; - use std::mem::size_of; - use std::os::raw::c_int; #[cfg(any(feature = "pg10", feature = "pg11", feature = "pg12"))] const MY_MAGIC: pgx::pg_sys::Pg_magic_struct = pgx::pg_sys::Pg_magic_struct { - len: size_of::() as c_int, - version: pgx::pg_sys::PG_VERSION_NUM as c_int / 100, - funcmaxargs: pgx::pg_sys::FUNC_MAX_ARGS as c_int, - indexmaxkeys: pgx::pg_sys::INDEX_MAX_KEYS as c_int, - namedatalen: pgx::pg_sys::NAMEDATALEN as c_int, - float4byval: pgx::pg_sys::USE_FLOAT4_BYVAL as c_int, - float8byval: pgx::pg_sys::USE_FLOAT8_BYVAL as c_int, + len: size_of::() as i32, + version: pgx::pg_sys::PG_VERSION_NUM as i32 / 100, + funcmaxargs: pgx::pg_sys::FUNC_MAX_ARGS as i32, + indexmaxkeys: pgx::pg_sys::INDEX_MAX_KEYS as i32, + namedatalen: pgx::pg_sys::NAMEDATALEN as i32, + float4byval: pgx::pg_sys::USE_FLOAT4_BYVAL as i32, + float8byval: pgx::pg_sys::USE_FLOAT8_BYVAL as i32, }; #[cfg(any(feature = "pg13", feature = "pg14"))] const MY_MAGIC: pgx::pg_sys::Pg_magic_struct = pgx::pg_sys::Pg_magic_struct { - len: size_of::() as c_int, - version: pgx::pg_sys::PG_VERSION_NUM as c_int / 100, - funcmaxargs: pgx::pg_sys::FUNC_MAX_ARGS as c_int, - indexmaxkeys: pgx::pg_sys::INDEX_MAX_KEYS as c_int, - namedatalen: pgx::pg_sys::NAMEDATALEN as c_int, - float8byval: pgx::pg_sys::USE_FLOAT8_BYVAL as c_int, + len: size_of::() as i32, + version: pgx::pg_sys::PG_VERSION_NUM as i32 / 100, + funcmaxargs: pgx::pg_sys::FUNC_MAX_ARGS as i32, + indexmaxkeys: pgx::pg_sys::INDEX_MAX_KEYS as i32, + namedatalen: pgx::pg_sys::NAMEDATALEN as i32, + float8byval: pgx::pg_sys::USE_FLOAT8_BYVAL as i32, }; // go ahead and register our panic handler since Postgres @@ -336,8 +338,8 @@ macro_rules! pg_sql_graph_magic { pub extern "C" fn __pgx_marker() -> pgx::datum::sql_entity_graph::reexports::eyre::Result< pgx::datum::sql_entity_graph::ControlFile, > { + use core::convert::TryFrom; use pgx::datum::sql_entity_graph::reexports::eyre::WrapErr; - use std::convert::TryFrom; let package_version = env!("CARGO_PKG_VERSION"); let context = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), diff --git a/pgx/src/stringinfo.rs b/pgx/src/stringinfo.rs index 995227b18..96b2d72bf 100644 --- a/pgx/src/stringinfo.rs +++ b/pgx/src/stringinfo.rs @@ -34,6 +34,20 @@ impl Into<&'static std::ffi::CStr> for StringInfo { } } +impl Into<&'static crate::cstr_core::CStr> for StringInfo { + fn into(self) -> &'static crate::cstr_core::CStr { + let len = self.len(); + let ptr = self.into_char_ptr(); + + unsafe { + crate::cstr_core::CStr::from_bytes_with_nul_unchecked(std::slice::from_raw_parts( + ptr as *const u8, + (len + 1) as usize, // +1 to get the trailing null byte + )) + } + } +} + impl std::io::Write for StringInfo { fn write(&mut self, buf: &[u8]) -> Result { self.push_bytes(buf);