pgrx/
lib.rs

1//LICENSE Portions Copyright 2019-2021 ZomboDB, LLC.
2//LICENSE
3//LICENSE Portions Copyright 2021-2023 Technology Concepts & Design, Inc.
4//LICENSE
5//LICENSE Portions Copyright 2023-2023 PgCentral Foundation, Inc. <contact@pgcentral.org>
6//LICENSE
7//LICENSE All rights reserved.
8//LICENSE
9//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
10//! `pgrx` is a framework for creating Postgres extensions in 100% Rust
11//!
12//! ## Example
13//!
14//! ```rust
15//! use pgrx::prelude::*;
16//!
17//! // Convert the input string to lowercase and return
18//! #[pg_extern]
19//! fn my_to_lowercase(input: &str) -> String {
20//!     input.to_lowercase()
21//! }
22//!
23//! ```
24#![cfg_attr(feature = "nightly", feature(allocator_api))]
25
26#[macro_use]
27extern crate bitflags;
28extern crate alloc;
29
30use once_cell::sync::Lazy;
31// expose our various derive macros
32pub use pgrx_macros;
33pub use pgrx_macros::*;
34
35/// The PGRX prelude includes necessary imports to make extensions work.
36pub mod prelude;
37
38pub mod aggregate;
39pub mod array;
40pub mod atomics;
41pub mod bgworkers;
42pub mod callbacks;
43pub mod callconv;
44pub mod datum;
45pub mod enum_helper;
46pub mod fcinfo;
47pub mod ffi;
48pub mod fn_call;
49pub mod guc;
50pub mod heap_tuple;
51#[cfg(feature = "cshim")]
52#[allow(deprecated)]
53pub mod hooks;
54pub mod htup;
55pub mod inoutfuncs;
56pub mod itemptr;
57pub mod iter;
58pub mod layout;
59pub mod list;
60pub mod lwlock;
61pub mod memcx;
62pub mod memcxt;
63pub mod misc;
64#[cfg(feature = "cshim")]
65pub mod namespace;
66pub mod nodes;
67pub mod nullable;
68pub mod pg_catalog;
69pub mod pgbox;
70pub mod rel;
71pub mod shmem;
72pub mod spi;
73#[cfg(feature = "cshim")]
74pub mod spinlock;
75pub mod stringinfo;
76pub mod trigger_support;
77pub mod tupdesc;
78pub mod varlena;
79pub mod wrappers;
80pub mod xid;
81
82/// Not ready for public exposure.
83mod ptr;
84mod slice;
85mod toast;
86
87pub use aggregate::*;
88pub use atomics::*;
89pub use callbacks::*;
90pub use datum::{
91    numeric, AnyArray, AnyElement, AnyNumeric, Array, FromDatum, Inet, Internal, IntoDatum, Json,
92    JsonB, Numeric, Range, Uuid, VariadicArray,
93};
94pub use enum_helper::*;
95pub use fcinfo::*;
96pub use guc::*;
97#[cfg(feature = "cshim")]
98#[allow(deprecated)]
99pub use hooks::*;
100pub use htup::*;
101pub use inoutfuncs::*;
102#[cfg(feature = "cshim")]
103pub use list::old_list::*;
104pub use lwlock::*;
105pub use memcxt::*;
106#[cfg(feature = "cshim")]
107pub use namespace::*;
108pub use nodes::*;
109pub use pgbox::*;
110pub use rel::*;
111pub use shmem::*;
112pub use spi::Spi; // only Spi.  We don't want the top-level namespace polluted with spi::Result and spi::Error
113pub use stringinfo::*;
114pub use trigger_support::*;
115pub use tupdesc::*;
116pub use varlena::*;
117pub use wrappers::*;
118pub use xid::*;
119
120pub mod pg_sys;
121
122// and re-export these
123pub use pg_sys::elog::PgLogLevel;
124pub use pg_sys::errcodes::PgSqlErrorCode;
125pub use pg_sys::oids::PgOid;
126pub use pg_sys::panic::pgrx_extern_c_guard;
127pub use pg_sys::pg_try::PgTryBuilder;
128pub use pg_sys::utils::name_data_to_str;
129pub use pg_sys::PgBuiltInOids;
130pub use pg_sys::{
131    check_for_interrupts, debug1, debug2, debug3, debug4, debug5, ereport, error, function_name,
132    info, log, notice, warning, FATAL, PANIC,
133};
134#[doc(hidden)]
135pub use pgrx_sql_entity_graph;
136
137mod seal {
138    /// A trait you can reference but can't impl externally
139    pub trait Sealed {}
140}
141
142// Postgres v15+ has the concept of an ABI "name".  The default is `c"PostgreSQL"` and this is the
143// ABI that pgrx extensions expect to be running under.  We will refuse to compile if it is detected
144// that we're trying to be built against some other kind of "postgres" that has its own ABI name.
145//
146// Unless the compiling user explicitly told us that they're aware of this via `--features unsafe-postgres`.
147#[cfg(all(
148    any(feature = "pg15", feature = "pg16", feature = "pg17"),
149    not(feature = "unsafe-postgres")
150))]
151const _: () = {
152    use core::ffi::CStr;
153    // to appease `const`
154    const fn same_cstr(a: &CStr, b: &CStr) -> bool {
155        if a.to_bytes().len() != b.to_bytes().len() {
156            return false;
157        }
158        let mut i = 0;
159        while i < a.to_bytes().len() {
160            if a.to_bytes()[i] != b.to_bytes()[i] {
161                return false;
162            }
163            i += 1;
164        }
165        true
166    }
167    assert!(
168        same_cstr(pg_sys::FMGR_ABI_EXTRA, c"PostgreSQL"),
169        "Unsupported Postgres ABI. Perhaps you need `--features unsafe-postgres`?",
170    );
171};
172
173/// A macro for marking a library compatible with [`pgrx`][crate].
174///
175/// <div class="example-wrap" style="display:inline-block">
176/// <pre class="ignore" style="white-space:normal;font:inherit;">
177///
178/// **Note**: Every [`pgrx`][crate] extension **must** have this macro called at top level (usually `src/lib.rs`) to be valid.
179///
180/// </pre></div>
181///
182/// This calls [`pg_magic_func!()`](pg_magic_func).
183#[macro_export]
184macro_rules! pg_module_magic {
185    () => {
186        $crate::pg_magic_func!();
187
188        // A marker function which must exist in the root of the extension for proper linking by the
189        // "pgrx_embed" binary during `cargo-pgrx schema` generation.
190        #[inline(never)] /* we don't want DCE to remove this as it *could* cause the compiler to decide to not link to us */
191        #[doc(hidden)]
192        pub fn __pgrx_marker() {
193            // noop
194        }
195    };
196}
197
198/// Create the `Pg_magic_func` required by PGRX in extensions.
199///
200/// <div class="example-wrap" style="display:inline-block">
201/// <pre class="ignore" style="white-space:normal;font:inherit;">
202///
203/// **Note**: Generally [`pg_module_magic`] is preferred, and results in this macro being called.
204/// This macro should only be directly called in advanced use cases.
205///
206/// </pre></div>
207///
208/// Creates a “magic block” that describes the capabilities of the extension to
209/// Postgres at runtime. From the [Dynamic Loading] section of the upstream documentation:
210///
211/// > To ensure that a dynamically loaded object file is not loaded into an incompatible
212/// > server, PostgreSQL checks that the file contains a “magic block” with the appropriate
213/// > contents. This allows the server to detect obvious incompatibilities, such as code
214/// > compiled for a different major version of PostgreSQL. To include a magic block,
215/// > write this in one (and only one) of the module source files, after having included
216/// > the header `fmgr.h`:
217/// >
218/// > ```c
219/// > PG_MODULE_MAGIC;
220/// > ```
221///
222/// ## Acknowledgements
223///
224/// This macro was initially inspired from the `pg_module` macro by [Daniel Fagnan]
225/// and expanded by [Benjamin Fry].
226///
227/// [Benjamin Fry]: https://github.com/bluejekyll/pg-extend-rs
228/// [Daniel Fagnan]: https://github.com/thehydroimpulse/postgres-extension.rs
229/// [Dynamic Loading]: https://www.postgresql.org/docs/current/xfunc-c.html#XFUNC-C-DYNLOAD
230#[macro_export]
231macro_rules! pg_magic_func {
232    () => {
233        #[no_mangle]
234        #[allow(non_snake_case, unexpected_cfgs)]
235        #[doc(hidden)]
236        pub extern "C-unwind" fn Pg_magic_func() -> &'static ::pgrx::pg_sys::Pg_magic_struct {
237            static MY_MAGIC: ::pgrx::pg_sys::Pg_magic_struct = ::pgrx::pg_sys::Pg_magic_struct {
238                len: ::core::mem::size_of::<::pgrx::pg_sys::Pg_magic_struct>() as i32,
239                version: ::pgrx::pg_sys::PG_VERSION_NUM as i32 / 100,
240                funcmaxargs: ::pgrx::pg_sys::FUNC_MAX_ARGS as i32,
241                indexmaxkeys: ::pgrx::pg_sys::INDEX_MAX_KEYS as i32,
242                namedatalen: ::pgrx::pg_sys::NAMEDATALEN as i32,
243                float8byval: cfg!(target_pointer_width = "64") as i32,
244                #[cfg(any(feature = "pg15", feature = "pg16", feature = "pg17"))]
245                abi_extra: {
246                    // we'll use what the bindings tell us, but if it ain't "PostgreSQL" then we'll
247                    // raise a compilation error unless the `unsafe-postgres` feature is set
248                    let magic = ::pgrx::pg_sys::FMGR_ABI_EXTRA.to_bytes_with_nul();
249                    let mut abi = [0 as ::pgrx::ffi::c_char; 32];
250                    let mut i = 0;
251                    while i < magic.len() {
252                        abi[i] = magic[i] as ::pgrx::ffi::c_char;
253                        i += 1;
254                    }
255                    abi
256                },
257            };
258
259            // since Postgres calls this first, register our panic handler now
260            // so we don't unwind into C / Postgres
261            ::pgrx::pg_sys::panic::register_pg_guard_panic_hook();
262
263            // return the magic
264            &MY_MAGIC
265        }
266    };
267}
268
269pub(crate) static UTF8DATABASE: Lazy<Utf8Compat> = Lazy::new(|| {
270    use pg_sys::pg_enc::*;
271    let encoding_int = unsafe { pg_sys::GetDatabaseEncoding() };
272    match encoding_int as _ {
273        PG_UTF8 => Utf8Compat::Yes,
274        // The 0 encoding. It... may be UTF-8
275        PG_SQL_ASCII => Utf8Compat::Maybe,
276        // Modifies ASCII, and should never be seen as PG doesn't support it as server encoding
277        PG_SJIS | PG_SHIFT_JIS_2004
278        // Not specified as an ASCII extension, also not a server encoding
279        | PG_BIG5
280        // Wild vendor differences including non-ASCII are possible, also not a server encoding
281        | PG_JOHAB => unreachable!("impossible? unsupported non-ASCII-compatible database encoding is not a server encoding"),
282        // Other Postgres encodings either extend US-ASCII or CP437 (which includes US-ASCII)
283        // There may be a subtlety that requires us to revisit this later
284        1..=41=> Utf8Compat::Ascii,
285        // Unfamiliar encoding? Run UTF-8 validation like normal and hope for the best
286        _ => Utf8Compat::Maybe,
287    }
288});
289
290#[derive(Debug, Clone, Copy)]
291pub(crate) enum Utf8Compat {
292    /// It's UTF-8, so... obviously
293    Yes,
294    /// This is what is assumed about "SQL_ASCII"
295    Maybe,
296    /// An "extended ASCII" encoding, so we're fine if we only touch ASCII
297    Ascii,
298}
299
300/// Entry point for cargo-pgrx's schema generation so that PGRX's framework can
301/// generate SQL for its types and functions and topographically sort them into
302/// an order Postgres will accept. Typically written by the `cargo pgrx new`
303/// template, so you probably don't need to worry about this.
304#[macro_export]
305macro_rules! pgrx_embed {
306    () => {
307        mod pgrx_embed {
308            #![allow(unexpected_cfgs)]
309
310            #[cfg(not(pgrx_embed))]
311            pub fn main() {
312                panic!("PGRX_EMBED was not set.");
313            }
314            #[cfg(pgrx_embed)]
315            include!(env!("PGRX_EMBED"));
316        }
317        pub use pgrx_embed::main;
318    };
319}