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}