pgrx_macros/
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.
10extern crate proc_macro;
11
12use proc_macro::TokenStream;
13use std::collections::HashSet;
14use std::ffi::CString;
15
16use proc_macro2::Ident;
17use quote::{format_ident, quote, ToTokens};
18use syn::spanned::Spanned;
19use syn::{parse_macro_input, Attribute, Data, DeriveInput, Item, ItemImpl};
20
21use operators::{deriving_postgres_eq, deriving_postgres_hash, deriving_postgres_ord};
22use pgrx_sql_entity_graph as sql_gen;
23use sql_gen::{
24    parse_extern_attributes, CodeEnrichment, ExtensionSql, ExtensionSqlFile, ExternArgs,
25    PgAggregate, PgCast, PgExtern, PostgresEnum, Schema,
26};
27
28mod operators;
29mod rewriter;
30
31/// Declare a function as `#[pg_guard]` to indicate that it is called from a Postgres `extern "C-unwind"`
32/// function so that Rust `panic!()`s (and Postgres `elog(ERROR)`s) will be properly handled by `pgrx`
33#[proc_macro_attribute]
34pub fn pg_guard(_attr: TokenStream, item: TokenStream) -> TokenStream {
35    // get a usable token stream
36    let ast = parse_macro_input!(item as syn::Item);
37
38    let res = match ast {
39        // this is for processing the members of extern "C-unwind" { } blocks
40        // functions inside the block get wrapped as public, top-level unsafe functions that are not "extern"
41        Item::ForeignMod(block) => Ok(rewriter::extern_block(block)),
42
43        // process top-level functions
44        Item::Fn(func) => rewriter::item_fn_without_rewrite(func),
45        unknown => Err(syn::Error::new(
46            unknown.span(),
47            "#[pg_guard] can only be applied to extern \"C-unwind\" blocks and top-level functions",
48        )),
49    };
50    res.unwrap_or_else(|e| e.into_compile_error()).into()
51}
52
53/// `#[pg_test]` functions are test functions (akin to `#[test]`), but they run in-process inside
54/// Postgres during `cargo pgrx test`.
55///
56/// This can be combined with test attributes like [`#[should_panic(expected = "..")]`][expected].
57///
58/// [expected]: https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute
59#[proc_macro_attribute]
60pub fn pg_test(attr: TokenStream, item: TokenStream) -> TokenStream {
61    let mut stream = proc_macro2::TokenStream::new();
62    let args = parse_extern_attributes(proc_macro2::TokenStream::from(attr.clone()));
63
64    let mut expected_error = None;
65    args.into_iter().for_each(|v| {
66        if let ExternArgs::ShouldPanic(message) = v {
67            expected_error = Some(message)
68        }
69    });
70
71    let ast = parse_macro_input!(item as syn::Item);
72
73    match ast {
74        Item::Fn(mut func) => {
75            // Here we need to break out attributes into test and non-test attributes,
76            // so the generated #[test] attributes are in the appropriate place.
77            let mut test_attributes = Vec::new();
78            let mut non_test_attributes = Vec::new();
79
80            for attribute in func.attrs.iter() {
81                if let Some(ident) = attribute.path().get_ident() {
82                    let ident_str = ident.to_string();
83
84                    if ident_str == "ignore" || ident_str == "should_panic" {
85                        test_attributes.push(attribute.clone());
86                    } else {
87                        non_test_attributes.push(attribute.clone());
88                    }
89                } else {
90                    non_test_attributes.push(attribute.clone());
91                }
92            }
93
94            func.attrs = non_test_attributes;
95
96            stream.extend(proc_macro2::TokenStream::from(pg_extern(
97                attr,
98                Item::Fn(func.clone()).to_token_stream().into(),
99            )));
100
101            let expected_error = match expected_error {
102                Some(msg) => quote! {Some(#msg)},
103                None => quote! {None},
104            };
105
106            let sql_funcname = func.sig.ident.to_string();
107            let test_func_name = format_ident!("pg_{}", func.sig.ident);
108
109            let attributes = func.attrs;
110            let mut att_stream = proc_macro2::TokenStream::new();
111
112            for a in attributes.iter() {
113                let as_str = a.to_token_stream().to_string();
114                att_stream.extend(quote! {
115                    options.push(#as_str);
116                });
117            }
118
119            stream.extend(quote! {
120                #[test]
121                #(#test_attributes)*
122                fn #test_func_name() {
123                    let mut options = Vec::new();
124                    #att_stream
125
126                    crate::pg_test::setup(options);
127                    let res = pgrx_tests::run_test(#sql_funcname, #expected_error, crate::pg_test::postgresql_conf_options());
128                    match res {
129                        Ok(()) => (),
130                        Err(e) => panic!("{e:?}")
131                    }
132                }
133            });
134        }
135
136        thing => {
137            return syn::Error::new(
138                thing.span(),
139                "#[pg_test] can only be applied to top-level functions",
140            )
141            .into_compile_error()
142            .into()
143        }
144    }
145
146    stream.into()
147}
148
149/// Associated macro for `#[pg_test]` to provide context back to your test framework to indicate
150/// that the test system is being initialized
151#[proc_macro_attribute]
152pub fn initialize(_attr: TokenStream, item: TokenStream) -> TokenStream {
153    item
154}
155
156/**
157Declare a function as `#[pg_cast]` to indicate that it represents a Postgres [cast](https://www.postgresql.org/docs/current/sql-createcast.html).
158
159* `assignment`: Corresponds to [`AS ASSIGNMENT`](https://www.postgresql.org/docs/current/sql-createcast.html).
160* `implicit`: Corresponds to [`AS IMPLICIT`](https://www.postgresql.org/docs/current/sql-createcast.html).
161
162By default if no attribute is specified, the cast function can only be used in an explicit cast.
163
164Functions MUST accept and return exactly one value whose type MUST be a `pgrx` supported type. `pgrx` supports many PostgreSQL types by default.
165New types can be defined via [`macro@PostgresType`] or [`macro@PostgresEnum`].
166
167`#[pg_cast]` also supports all the attributes supported by the [`macro@pg_extern]` macro, which are
168passed down to the underlying function.
169
170Example usage:
171```rust,ignore
172use pgrx::*;
173#[pg_cast(implicit)]
174fn cast_json_to_int(input: Json) -> i32 { todo!() }
175*/
176#[proc_macro_attribute]
177pub fn pg_cast(attr: TokenStream, item: TokenStream) -> TokenStream {
178    fn wrapped(attr: TokenStream, item: TokenStream) -> Result<TokenStream, syn::Error> {
179        use syn::parse::Parser;
180        use syn::punctuated::Punctuated;
181
182        let mut cast = None;
183        let mut pg_extern_attrs = proc_macro2::TokenStream::new();
184
185        // look for the attributes `#[pg_cast]` directly understands
186        match Punctuated::<syn::Path, syn::Token![,]>::parse_terminated.parse(attr) {
187            Ok(paths) => {
188                let mut new_paths = Punctuated::<syn::Path, syn::Token![,]>::new();
189                for path in paths {
190                    match (PgCast::try_from(path), &cast) {
191                        (Ok(style), None) => cast = Some(style),
192                        (Ok(_), Some(cast)) => {
193                            panic!("The cast type has already been set to `{cast:?}`")
194                        }
195
196                        // ... and anything it doesn't understand is blindly passed through to the
197                        // underlying `#[pg_extern]` function that gets created, which will ultimately
198                        // decide what's naughty and what's nice
199                        (Err(unknown), _) => {
200                            new_paths.push(unknown);
201                        }
202                    }
203                }
204
205                pg_extern_attrs.extend(new_paths.into_token_stream());
206            }
207            Err(err) => {
208                panic!("Failed to parse attribute to pg_cast: {err}")
209            }
210        }
211
212        let pg_extern = PgExtern::new(pg_extern_attrs, item.clone().into())?.0;
213        Ok(CodeEnrichment(pg_extern.as_cast(cast.unwrap_or_default())).to_token_stream().into())
214    }
215
216    wrapped(attr, item).unwrap_or_else(|e: syn::Error| e.into_compile_error().into())
217}
218
219/// Declare a function as `#[pg_operator]` to indicate that it represents a Postgres operator
220/// `cargo pgrx schema` will automatically generate the underlying SQL
221#[proc_macro_attribute]
222pub fn pg_operator(attr: TokenStream, item: TokenStream) -> TokenStream {
223    pg_extern(attr, item)
224}
225
226/// Used with `#[pg_operator]`.  1 value which is the operator name itself
227#[proc_macro_attribute]
228pub fn opname(_attr: TokenStream, item: TokenStream) -> TokenStream {
229    item
230}
231
232/// Used with `#[pg_operator]`.  1 value which is the function name
233#[proc_macro_attribute]
234pub fn commutator(_attr: TokenStream, item: TokenStream) -> TokenStream {
235    item
236}
237
238/// Used with `#[pg_operator]`.  1 value which is the function name
239#[proc_macro_attribute]
240pub fn negator(_attr: TokenStream, item: TokenStream) -> TokenStream {
241    item
242}
243
244/// Used with `#[pg_operator]`.  1 value which is the function name
245#[proc_macro_attribute]
246pub fn restrict(_attr: TokenStream, item: TokenStream) -> TokenStream {
247    item
248}
249
250/// Used with `#[pg_operator]`.  1 value which is the function name
251#[proc_macro_attribute]
252pub fn join(_attr: TokenStream, item: TokenStream) -> TokenStream {
253    item
254}
255
256/// Used with `#[pg_operator]`.  no values
257#[proc_macro_attribute]
258pub fn hashes(_attr: TokenStream, item: TokenStream) -> TokenStream {
259    item
260}
261
262/// Used with `#[pg_operator]`.  no values
263#[proc_macro_attribute]
264pub fn merges(_attr: TokenStream, item: TokenStream) -> TokenStream {
265    item
266}
267
268/**
269Declare a Rust module and its contents to be in a schema.
270
271The schema name will always be the `mod`'s identifier. So `mod flop` will create a `flop` schema.
272
273If there is a schema inside a schema, the most specific schema is chosen.
274
275In this example, the created `example` function is in the `dsl_filters` schema.
276
277```rust,ignore
278use pgrx::*;
279
280#[pg_schema]
281mod dsl {
282    use pgrx::*;
283    #[pg_schema]
284    mod dsl_filters {
285        use pgrx::*;
286        #[pg_extern]
287        fn example() { todo!() }
288    }
289}
290```
291
292File modules (like `mod name;`) aren't able to be supported due to [`rust/#54725`](https://github.com/rust-lang/rust/issues/54725).
293
294*/
295#[proc_macro_attribute]
296pub fn pg_schema(_attr: TokenStream, input: TokenStream) -> TokenStream {
297    fn wrapped(input: TokenStream) -> Result<TokenStream, syn::Error> {
298        let pgrx_schema: Schema = syn::parse(input)?;
299        Ok(pgrx_schema.to_token_stream().into())
300    }
301
302    wrapped(input).unwrap_or_else(|e: syn::Error| e.into_compile_error().into())
303}
304
305/**
306Declare SQL to be included in generated extension script.
307
308Accepts a String literal, a `name` attribute, and optionally others:
309
310* `name = "item"`: Set the unique identifier to `"item"` for use in `requires` declarations.
311* `requires = [item, item_two]`: References to other `name`s or Rust items which this SQL should be present after.
312* `creates = [ Type(submod::Cust), Enum(Pre), Function(defined)]`: Communicates that this SQL block creates certain entities.
313  Please note it **does not** create matching Rust types.
314* `bootstrap` (**Unique**): Communicates that this is SQL intended to go before all other generated SQL.
315* `finalize` (**Unique**): Communicates that this is SQL intended to go after all other generated SQL.
316
317You can declare some SQL without any positioning information, meaning it can end up anywhere in the generated SQL:
318
319```rust,ignore
320use pgrx_macros::extension_sql;
321
322extension_sql!(
323    r#"
324    -- SQL statements
325    "#,
326    name = "demo",
327);
328```
329
330To cause the SQL to be output at the start of the generated SQL:
331
332```rust,ignore
333use pgrx_macros::extension_sql;
334
335extension_sql!(
336    r#"
337    -- SQL statements
338    "#,
339    name = "demo",
340    bootstrap,
341);
342```
343
344To cause the SQL to be output at the end of the generated SQL:
345
346```rust,ignore
347use pgrx_macros::extension_sql;
348
349extension_sql!(
350    r#"
351    -- SQL statements
352    "#,
353    name = "demo",
354    finalize,
355);
356```
357
358To declare the SQL dependent, or a dependency of, other items:
359
360```rust,ignore
361use pgrx_macros::extension_sql;
362
363struct Treat;
364
365mod dog_characteristics {
366    enum DogAlignment {
367        Good
368    }
369}
370
371extension_sql!(r#"
372    -- SQL statements
373    "#,
374    name = "named_one",
375);
376
377extension_sql!(r#"
378    -- SQL statements
379    "#,
380    name = "demo",
381    requires = [ "named_one", dog_characteristics::DogAlignment ],
382);
383```
384
385To declare the SQL defines some entity (**Caution:** This is not recommended usage):
386
387```rust,ignore
388use pgrx::stringinfo::StringInfo;
389use pgrx::*;
390use pgrx_utils::get_named_capture;
391
392#[derive(Debug)]
393#[repr(C)]
394struct Complex {
395    x: f64,
396    y: f64,
397}
398
399extension_sql!(r#"\
400        CREATE TYPE complex;\
401    "#,
402    name = "create_complex_type",
403    creates = [Type(Complex)],
404);
405
406#[pg_extern(immutable)]
407fn complex_in(input: &core::ffi::CStr) -> PgBox<Complex> {
408    todo!()
409}
410
411#[pg_extern(immutable)]
412fn complex_out(complex: PgBox<Complex>) -> &'static ::core::ffi::CStr {
413    todo!()
414}
415
416extension_sql!(r#"\
417        CREATE TYPE complex (
418            internallength = 16,
419            input = complex_in,
420            output = complex_out,
421            alignment = double
422        );\
423    "#,
424    name = "demo",
425    requires = ["create_complex_type", complex_in, complex_out],
426);
427
428```
429*/
430#[proc_macro]
431pub fn extension_sql(input: TokenStream) -> TokenStream {
432    fn wrapped(input: TokenStream) -> Result<TokenStream, syn::Error> {
433        let ext_sql: CodeEnrichment<ExtensionSql> = syn::parse(input)?;
434        Ok(ext_sql.to_token_stream().into())
435    }
436
437    wrapped(input).unwrap_or_else(|e: syn::Error| e.into_compile_error().into())
438}
439
440/**
441Declare SQL (from a file) to be included in generated extension script.
442
443Accepts the same options as [`macro@extension_sql`]. `name` is automatically set to the file name (not the full path).
444
445You can declare some SQL without any positioning information, meaning it can end up anywhere in the generated SQL:
446
447```rust,ignore
448use pgrx_macros::extension_sql_file;
449extension_sql_file!(
450    "../static/demo.sql",
451);
452```
453
454To override the default name:
455
456```rust,ignore
457use pgrx_macros::extension_sql_file;
458
459extension_sql_file!(
460    "../static/demo.sql",
461    name = "singular",
462);
463```
464
465For all other options, and examples of them, see [`macro@extension_sql`].
466*/
467#[proc_macro]
468pub fn extension_sql_file(input: TokenStream) -> TokenStream {
469    fn wrapped(input: TokenStream) -> Result<TokenStream, syn::Error> {
470        let ext_sql: CodeEnrichment<ExtensionSqlFile> = syn::parse(input)?;
471        Ok(ext_sql.to_token_stream().into())
472    }
473
474    wrapped(input).unwrap_or_else(|e: syn::Error| e.into_compile_error().into())
475}
476
477/// Associated macro for `#[pg_extern]` or `#[macro@pg_operator]`.  Used to set the `SEARCH_PATH` option
478/// on the `CREATE FUNCTION` statement.
479#[proc_macro_attribute]
480pub fn search_path(_attr: TokenStream, item: TokenStream) -> TokenStream {
481    item
482}
483
484/**
485Declare a function as `#[pg_extern]` to indicate that it can be used by Postgres as a UDF.
486
487Optionally accepts the following attributes:
488
489* `immutable`: Corresponds to [`IMMUTABLE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
490* `strict`: Corresponds to [`STRICT`](https://www.postgresql.org/docs/current/sql-createfunction.html).
491  + In most cases, `#[pg_extern]` can detect when no `Option<T>`s are used, and automatically set this.
492* `stable`: Corresponds to [`STABLE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
493* `volatile`: Corresponds to [`VOLATILE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
494* `raw`: Corresponds to [`RAW`](https://www.postgresql.org/docs/current/sql-createfunction.html).
495* `support`: Corresponds to [`SUPPORT`](https://www.postgresql.org/docs/current/sql-createfunction.html) and is the Rust path to a function to act as the SUPPORT function
496* `security_definer`: Corresponds to [`SECURITY DEFINER`](https://www.postgresql.org/docs/current/sql-createfunction.html)
497* `security_invoker`: Corresponds to [`SECURITY INVOKER`](https://www.postgresql.org/docs/current/sql-createfunction.html)
498* `parallel_safe`: Corresponds to [`PARALLEL SAFE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
499* `parallel_unsafe`: Corresponds to [`PARALLEL UNSAFE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
500* `parallel_restricted`: Corresponds to [`PARALLEL RESTRICTED`](https://www.postgresql.org/docs/current/sql-createfunction.html).
501* `no_guard`: Do not use `#[pg_guard]` with the function.
502* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
503* `name`: Specifies target function name. Defaults to Rust function name.
504
505Functions can accept and return any type which `pgrx` supports. `pgrx` supports many PostgreSQL types by default.
506New types can be defined via [`macro@PostgresType`] or [`macro@PostgresEnum`].
507
508
509Without any arguments or returns:
510```rust,ignore
511use pgrx::*;
512#[pg_extern]
513fn foo() { todo!() }
514```
515
516# Arguments
517It's possible to pass even complex arguments:
518
519```rust,ignore
520use pgrx::*;
521#[pg_extern]
522fn boop(
523    a: i32,
524    b: Option<i32>,
525    c: Vec<i32>,
526    d: Option<Vec<Option<i32>>>
527) { todo!() }
528```
529
530It's possible to set argument defaults, set by PostgreSQL when the function is invoked:
531
532```rust,ignore
533use pgrx::*;
534#[pg_extern]
535fn boop(a: default!(i32, 11111)) { todo!() }
536#[pg_extern]
537fn doop(
538    a: default!(Vec<Option<&str>>, "ARRAY[]::text[]"),
539    b: default!(String, "'note the inner quotes!'")
540) { todo!() }
541```
542
543The `default!()` macro may only be used in argument position.
544
545It accepts 2 arguments:
546
547* A type
548* A `bool`, numeric, or SQL string to represent the default. `"NULL"` is a possible value, as is `"'string'"`
549
550**If the default SQL entity created by the extension:** ensure it is added to `requires` as a dependency:
551
552```rust,ignore
553use pgrx::*;
554#[pg_extern]
555fn default_value() -> i32 { todo!() }
556
557#[pg_extern(
558    requires = [ default_value, ],
559)]
560fn do_it(
561    a: default!(i32, "default_value()"),
562) { todo!() }
563```
564
565# Returns
566
567It's possible to return even complex values, as well:
568
569```rust,ignore
570use pgrx::*;
571#[pg_extern]
572fn boop() -> i32 { todo!() }
573#[pg_extern]
574fn doop() -> Option<i32> { todo!() }
575#[pg_extern]
576fn swoop() -> Option<Vec<Option<i32>>> { todo!() }
577#[pg_extern]
578fn floop() -> (i32, i32) { todo!() }
579```
580
581Like in PostgreSQL, it's possible to return tables using iterators and the `name!()` macro:
582
583```rust,ignore
584use pgrx::*;
585#[pg_extern]
586fn floop<'a>() -> TableIterator<'a, (name!(a, i32), name!(b, i32))> {
587    TableIterator::new(None.into_iter())
588}
589
590#[pg_extern]
591fn singular_floop() -> (name!(a, i32), name!(b, i32)) {
592    todo!()
593}
594```
595
596The `name!()` macro may only be used in return position inside the `T` of a `TableIterator<'a, T>`.
597
598It accepts 2 arguments:
599
600* A name, such as `example`
601* A type
602
603# Special Cases
604
605`pg_sys::Oid` is a special cased type alias, in order to use it as an argument or return it must be
606passed with it's full module path (`pg_sys::Oid`) in order to be resolved.
607
608```rust,ignore
609use pgrx::*;
610
611#[pg_extern]
612fn example_arg(animals: pg_sys::Oid) {
613    todo!()
614}
615
616#[pg_extern]
617fn example_return() -> pg_sys::Oid {
618    todo!()
619}
620```
621
622*/
623#[proc_macro_attribute]
624#[track_caller]
625pub fn pg_extern(attr: TokenStream, item: TokenStream) -> TokenStream {
626    fn wrapped(attr: TokenStream, item: TokenStream) -> Result<TokenStream, syn::Error> {
627        let pg_extern_item = PgExtern::new(attr.into(), item.into())?;
628        Ok(pg_extern_item.to_token_stream().into())
629    }
630
631    wrapped(attr, item).unwrap_or_else(|e: syn::Error| e.into_compile_error().into())
632}
633
634/**
635Generate necessary bindings for using the enum with PostgreSQL.
636
637```rust,ignore
638# use pgrx_pg_sys as pg_sys;
639use pgrx::*;
640use serde::{Deserialize, Serialize};
641#[derive(Debug, Serialize, Deserialize, PostgresEnum)]
642enum DogNames {
643    Nami,
644    Brandy,
645}
646```
647
648*/
649#[proc_macro_derive(PostgresEnum, attributes(requires, pgrx))]
650pub fn postgres_enum(input: TokenStream) -> TokenStream {
651    let ast = parse_macro_input!(input as syn::DeriveInput);
652
653    impl_postgres_enum(ast).unwrap_or_else(|e| e.into_compile_error()).into()
654}
655
656fn impl_postgres_enum(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
657    let mut stream = proc_macro2::TokenStream::new();
658    let sql_graph_entity_ast = ast.clone();
659    let generics = &ast.generics.clone();
660    let enum_ident = &ast.ident;
661    let enum_name = enum_ident.to_string();
662
663    // validate that we're only operating on an enum
664    let Data::Enum(enum_data) = ast.data else {
665        return Err(syn::Error::new(
666            ast.span(),
667            "#[derive(PostgresEnum)] can only be applied to enums",
668        ));
669    };
670
671    let mut from_datum = proc_macro2::TokenStream::new();
672    let mut into_datum = proc_macro2::TokenStream::new();
673
674    for d in enum_data.variants.clone() {
675        let label_ident = &d.ident;
676        let label_string = label_ident.to_string();
677
678        from_datum.extend(quote! { #label_string => Some(#enum_ident::#label_ident), });
679        into_datum.extend(quote! { #enum_ident::#label_ident => Some(::pgrx::enum_helper::lookup_enum_by_label(#enum_name, #label_string)), });
680    }
681
682    // We need another variant of the params for the ArgAbi impl
683    let fcx_lt = syn::Lifetime::new("'fcx", proc_macro2::Span::mixed_site());
684    let mut generics_with_fcx = generics.clone();
685    // so that we can bound on Self: 'fcx
686    generics_with_fcx.make_where_clause().predicates.push(syn::WherePredicate::Type(
687        syn::PredicateType {
688            lifetimes: None,
689            bounded_ty: syn::parse_quote! { Self },
690            colon_token: syn::Token![:](proc_macro2::Span::mixed_site()),
691            bounds: syn::parse_quote! { #fcx_lt },
692        },
693    ));
694    let (impl_gens, ty_gens, where_clause) = generics_with_fcx.split_for_impl();
695    let mut impl_gens: syn::Generics = syn::parse_quote! { #impl_gens };
696    impl_gens
697        .params
698        .insert(0, syn::GenericParam::Lifetime(syn::LifetimeParam::new(fcx_lt.clone())));
699
700    stream.extend(quote! {
701        impl ::pgrx::datum::FromDatum for #enum_ident {
702            #[inline]
703            unsafe fn from_polymorphic_datum(datum: ::pgrx::pg_sys::Datum, is_null: bool, _typeoid: ::pgrx::pg_sys::Oid) -> Option<#enum_ident> {
704                if is_null {
705                    None
706                } else {
707                    // GREPME: non-primitive cast u64 as Oid
708                    let (name, _, _) = ::pgrx::enum_helper::lookup_enum_by_oid(unsafe { ::pgrx::pg_sys::Oid::from_datum(datum, is_null)? } );
709                    match name.as_str() {
710                        #from_datum
711                        _ => panic!("invalid enum value: {name}")
712                    }
713                }
714            }
715        }
716
717        unsafe impl #impl_gens ::pgrx::callconv::ArgAbi<#fcx_lt> for #enum_ident #ty_gens #where_clause {
718            unsafe fn unbox_arg_unchecked(arg: ::pgrx::callconv::Arg<'_, #fcx_lt>) -> Self {
719                let index = arg.index();
720                unsafe { arg.unbox_arg_using_from_datum().unwrap_or_else(|| panic!("argument {index} must not be null")) }
721            }
722
723        }
724
725        unsafe impl #generics ::pgrx::datum::UnboxDatum for #enum_ident #generics {
726            type As<'dat> = #enum_ident #generics where Self: 'dat;
727            #[inline]
728            unsafe fn unbox<'dat>(d: ::pgrx::datum::Datum<'dat>) -> Self::As<'dat> where Self: 'dat {
729                <Self as ::pgrx::datum::FromDatum>::from_datum(::core::mem::transmute(d), false).unwrap()
730            }
731        }
732
733        impl ::pgrx::datum::IntoDatum for #enum_ident {
734            #[inline]
735            fn into_datum(self) -> Option<::pgrx::pg_sys::Datum> {
736                match self {
737                    #into_datum
738                }
739            }
740
741            fn type_oid() -> ::pgrx::pg_sys::Oid {
742                ::pgrx::wrappers::regtypein(#enum_name)
743            }
744
745        }
746
747        unsafe impl ::pgrx::callconv::BoxRet for #enum_ident {
748            unsafe fn box_into<'fcx>(self, fcinfo: &mut ::pgrx::callconv::FcInfo<'fcx>) -> ::pgrx::datum::Datum<'fcx> {
749                match ::pgrx::datum::IntoDatum::into_datum(self) {
750                    None => fcinfo.return_null(),
751                    Some(datum) => unsafe { fcinfo.return_raw_datum(datum) },
752                }
753            }
754        }
755    });
756
757    let sql_graph_entity_item = PostgresEnum::from_derive_input(sql_graph_entity_ast)?;
758    sql_graph_entity_item.to_tokens(&mut stream);
759
760    Ok(stream)
761}
762
763/**
764Generate necessary bindings for using the type with PostgreSQL.
765
766```rust,ignore
767# use pgrx_pg_sys as pg_sys;
768use pgrx::*;
769use serde::{Deserialize, Serialize};
770#[derive(Debug, Serialize, Deserialize, PostgresType)]
771struct Dog {
772    treats_received: i64,
773    pets_gotten: i64,
774}
775
776#[derive(Debug, Serialize, Deserialize, PostgresType)]
777enum Animal {
778    Dog(Dog),
779}
780```
781
782Optionally accepts the following attributes:
783
784* `inoutfuncs(some_in_fn, some_out_fn)`: Define custom in/out functions for the type.
785* `pgvarlena_inoutfuncs(some_in_fn, some_out_fn)`: Define custom in/out functions for the `PgVarlena` of this type.
786* `pg_binary_protocol`: Use the binary protocol for this type.
787* `pgrx(alignment = "<align>")`: Derive Postgres alignment from Rust type. One of `"on"`, or `"off"`.
788* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
789*/
790#[proc_macro_derive(
791    PostgresType,
792    attributes(
793        inoutfuncs,
794        pgvarlena_inoutfuncs,
795        pg_binary_protocol,
796        bikeshed_postgres_type_manually_impl_from_into_datum,
797        requires,
798        pgrx
799    )
800)]
801pub fn postgres_type(input: TokenStream) -> TokenStream {
802    let ast = parse_macro_input!(input as syn::DeriveInput);
803
804    impl_postgres_type(ast).unwrap_or_else(|e| e.into_compile_error()).into()
805}
806
807fn impl_postgres_type(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
808    let name = &ast.ident;
809    let generics = &ast.generics.clone();
810    let has_lifetimes = generics.lifetimes().next();
811    let funcname_in = Ident::new(&format!("{name}_in").to_lowercase(), name.span());
812    let funcname_out = Ident::new(&format!("{name}_out").to_lowercase(), name.span());
813    let funcname_recv = Ident::new(&format!("{name}_recv").to_lowercase(), name.span());
814    let funcname_send = Ident::new(&format!("{name}_send").to_lowercase(), name.span());
815
816    let mut args = parse_postgres_type_args(&ast.attrs);
817    let mut stream = proc_macro2::TokenStream::new();
818
819    // validate that we're only operating on a struct
820    match ast.data {
821        Data::Struct(_) => { /* this is okay */ }
822        Data::Enum(_) => {
823            // this is okay and if there's an attempt to implement PostgresEnum,
824            // it will result in compile-time error of conflicting implementation
825            // of traits (IntoDatum, inout, etc.)
826        }
827        _ => {
828            return Err(syn::Error::new(
829                ast.span(),
830                "#[derive(PostgresType)] can only be applied to structs or enums",
831            ))
832        }
833    }
834
835    if !args.contains(&PostgresTypeAttribute::InOutFuncs)
836        && !args.contains(&PostgresTypeAttribute::PgVarlenaInOutFuncs)
837    {
838        // assume the user wants us to implement the InOutFuncs
839        args.insert(PostgresTypeAttribute::Default);
840    }
841
842    let lifetime = match has_lifetimes {
843        Some(lifetime) => quote! {#lifetime},
844        None => quote! {'_},
845    };
846
847    // We need another variant of the params for the ArgAbi impl
848    let fcx_lt = syn::Lifetime::new("'fcx", proc_macro2::Span::mixed_site());
849    let mut generics_with_fcx = generics.clone();
850    // so that we can bound on Self: 'fcx
851    generics_with_fcx.make_where_clause().predicates.push(syn::WherePredicate::Type(
852        syn::PredicateType {
853            lifetimes: None,
854            bounded_ty: syn::parse_quote! { Self },
855            colon_token: syn::Token![:](proc_macro2::Span::mixed_site()),
856            bounds: syn::parse_quote! { #fcx_lt },
857        },
858    ));
859    let (impl_gens, ty_gens, where_clause) = generics_with_fcx.split_for_impl();
860    let mut impl_gens: syn::Generics = syn::parse_quote! { #impl_gens };
861    impl_gens
862        .params
863        .insert(0, syn::GenericParam::Lifetime(syn::LifetimeParam::new(fcx_lt.clone())));
864
865    // all #[derive(PostgresType)] need to implement that trait
866    // and also the FromDatum and IntoDatum
867    stream.extend(quote! {
868        impl #generics ::pgrx::datum::PostgresType for #name #generics { }
869    });
870
871    if !args.contains(&PostgresTypeAttribute::ManualFromIntoDatum) {
872        stream.extend(
873            quote! {
874                impl #generics ::pgrx::datum::IntoDatum for #name #generics {
875                    fn into_datum(self) -> Option<::pgrx::pg_sys::Datum> {
876                        #[allow(deprecated)]
877                        Some(unsafe { ::pgrx::datum::cbor_encode(&self) }.into())
878                    }
879
880                    fn type_oid() -> ::pgrx::pg_sys::Oid {
881                        ::pgrx::wrappers::rust_regtypein::<Self>()
882                    }
883                }
884
885                unsafe impl #generics ::pgrx::callconv::BoxRet for #name #generics {
886                    unsafe fn box_into<'fcx>(self, fcinfo: &mut ::pgrx::callconv::FcInfo<'fcx>) -> ::pgrx::datum::Datum<'fcx> {
887                        match ::pgrx::datum::IntoDatum::into_datum(self) {
888                            None => fcinfo.return_null(),
889                            Some(datum) => unsafe { fcinfo.return_raw_datum(datum) },
890                        }
891                    }
892                }
893
894                impl #generics ::pgrx::datum::FromDatum for #name #generics {
895                    unsafe fn from_polymorphic_datum(
896                        datum: ::pgrx::pg_sys::Datum,
897                        is_null: bool,
898                        _typoid: ::pgrx::pg_sys::Oid,
899                    ) -> Option<Self> {
900                        if is_null {
901                            None
902                        } else {
903                            #[allow(deprecated)]
904                            ::pgrx::datum::cbor_decode(datum.cast_mut_ptr())
905                        }
906                    }
907
908                    unsafe fn from_datum_in_memory_context(
909                        mut memory_context: ::pgrx::memcxt::PgMemoryContexts,
910                        datum: ::pgrx::pg_sys::Datum,
911                        is_null: bool,
912                        _typoid: ::pgrx::pg_sys::Oid,
913                    ) -> Option<Self> {
914                        if is_null {
915                            None
916                        } else {
917                            memory_context.switch_to(|_| {
918                                // this gets the varlena Datum copied into this memory context
919                                let varlena = ::pgrx::pg_sys::pg_detoast_datum_copy(datum.cast_mut_ptr());
920                                <Self as ::pgrx::datum::FromDatum>::from_datum(varlena.into(), is_null)
921                            })
922                        }
923                    }
924                }
925
926                unsafe impl #generics ::pgrx::datum::UnboxDatum for #name #generics {
927                    type As<'dat> = Self where Self: 'dat;
928                    unsafe fn unbox<'dat>(datum: ::pgrx::datum::Datum<'dat>) -> Self::As<'dat> where Self: 'dat {
929                        <Self as ::pgrx::datum::FromDatum>::from_datum(::core::mem::transmute(datum), false).unwrap()
930                    }
931                }
932
933                unsafe impl #impl_gens ::pgrx::callconv::ArgAbi<#fcx_lt> for #name #ty_gens #where_clause
934                {
935                        unsafe fn unbox_arg_unchecked(arg: ::pgrx::callconv::Arg<'_, #fcx_lt>) -> Self {
936                        let index = arg.index();
937                        unsafe { arg.unbox_arg_using_from_datum().unwrap_or_else(|| panic!("argument {index} must not be null")) }
938                    }
939                }
940            }
941        )
942    }
943
944    // and if we don't have custom inout/funcs, we use the JsonInOutFuncs trait
945    // which implements _in and _out #[pg_extern] functions that just return the type itself
946    if args.contains(&PostgresTypeAttribute::Default) {
947        stream.extend(quote! {
948            #[doc(hidden)]
949            #[::pgrx::pgrx_macros::pg_extern(immutable, parallel_safe)]
950            pub fn #funcname_in #generics(input: Option<&#lifetime ::core::ffi::CStr>) -> Option<#name #generics> {
951                use ::pgrx::inoutfuncs::json_from_slice;
952                input.map(|cstr| json_from_slice(cstr.to_bytes()).ok()).flatten()
953            }
954
955            #[doc(hidden)]
956            #[::pgrx::pgrx_macros::pg_extern (immutable, parallel_safe)]
957            pub fn #funcname_out #generics(input: #name #generics) -> ::pgrx::ffi::CString {
958                use ::pgrx::inoutfuncs::json_to_vec;
959                let mut bytes = json_to_vec(&input).unwrap();
960                bytes.push(0); // terminate
961                ::pgrx::ffi::CString::from_vec_with_nul(bytes).unwrap()
962            }
963        });
964    } else if args.contains(&PostgresTypeAttribute::InOutFuncs) {
965        // otherwise if it's InOutFuncs our _in/_out functions use an owned type instance
966        stream.extend(quote! {
967            #[doc(hidden)]
968            #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)]
969            pub fn #funcname_in #generics(input: Option<&::core::ffi::CStr>) -> Option<#name #generics> {
970                input.map_or_else(|| {
971                    if let Some(m) = <#name as ::pgrx::inoutfuncs::InOutFuncs>::NULL_ERROR_MESSAGE {
972                        ::pgrx::pg_sys::error!("{m}");
973                    }
974                    None
975                }, |i| Some(<#name as ::pgrx::inoutfuncs::InOutFuncs>::input(i)))
976            }
977
978            #[doc(hidden)]
979            #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)]
980            pub fn #funcname_out #generics(input: #name #generics) -> ::pgrx::ffi::CString {
981                let mut buffer = ::pgrx::stringinfo::StringInfo::new();
982                ::pgrx::inoutfuncs::InOutFuncs::output(&input, &mut buffer);
983                // SAFETY: We just constructed this StringInfo ourselves
984                unsafe { buffer.leak_cstr().to_owned() }
985            }
986        });
987    } else if args.contains(&PostgresTypeAttribute::PgVarlenaInOutFuncs) {
988        // otherwise if it's PgVarlenaInOutFuncs our _in/_out functions use a PgVarlena
989        stream.extend(quote! {
990            #[doc(hidden)]
991            #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)]
992            pub fn #funcname_in #generics(input: Option<&::core::ffi::CStr>) -> Option<::pgrx::datum::PgVarlena<#name #generics>> {
993                input.map_or_else(|| {
994                    if let Some(m) = <#name as ::pgrx::inoutfuncs::PgVarlenaInOutFuncs>::NULL_ERROR_MESSAGE {
995                        ::pgrx::pg_sys::error!("{m}");
996                    }
997                    None
998                }, |i| Some(<#name as ::pgrx::inoutfuncs::PgVarlenaInOutFuncs>::input(i)))
999            }
1000
1001            #[doc(hidden)]
1002            #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)]
1003            pub fn #funcname_out #generics(input: ::pgrx::datum::PgVarlena<#name #generics>) -> ::pgrx::ffi::CString {
1004                let mut buffer = ::pgrx::stringinfo::StringInfo::new();
1005                ::pgrx::inoutfuncs::PgVarlenaInOutFuncs::output(&*input, &mut buffer);
1006                // SAFETY: We just constructed this StringInfo ourselves
1007                unsafe { buffer.leak_cstr().to_owned() }
1008            }
1009        });
1010    }
1011
1012    if args.contains(&PostgresTypeAttribute::PgBinaryProtocol) {
1013        // At this time, the `PostgresTypeAttribute` does not impact the way we generate
1014        // the `recv` and `send` functions.
1015        stream.extend(quote! {
1016            #[doc(hidden)]
1017            #[::pgrx::pgrx_macros::pg_extern(immutable, strict, parallel_safe)]
1018            pub fn #funcname_recv #generics(
1019                mut internal: ::pgrx::datum::Internal,
1020            ) -> #name #generics {
1021                let buf = unsafe { internal.get_mut::<::pgrx::pg_sys::StringInfoData>().unwrap() };
1022
1023                let mut serialized = ::pgrx::StringInfo::new();
1024
1025                serialized.push_bytes(&[0u8; ::pgrx::pg_sys::VARHDRSZ]); // reserve space for the header
1026                serialized.push_bytes(unsafe {
1027                    core::slice::from_raw_parts(
1028                        buf.data as *const u8,
1029                        buf.len as usize
1030                    )
1031                });
1032
1033                let size = serialized.len();
1034                let varlena = serialized.into_char_ptr();
1035
1036                unsafe{
1037                    ::pgrx::set_varsize_4b(varlena as *mut ::pgrx::pg_sys::varlena, size as i32);
1038                    buf.cursor = buf.len;
1039                    ::pgrx::datum::cbor_decode(varlena as *mut ::pgrx::pg_sys::varlena)
1040                }
1041            }
1042            #[doc(hidden)]
1043            #[::pgrx::pgrx_macros::pg_extern(immutable, strict, parallel_safe)]
1044            pub fn #funcname_send #generics(input: #name #generics) -> Vec<u8> {
1045                use ::pgrx::datum::{FromDatum, IntoDatum};
1046                let Some(datum): Option<::pgrx::pg_sys::Datum> = input.into_datum() else {
1047                    ::pgrx::error!("Datum of type `{}` is unexpectedly NULL.", stringify!(#name));
1048                };
1049                unsafe {
1050                    let Some(serialized): Option<Vec<u8>> = FromDatum::from_datum(datum, false) else {
1051                        ::pgrx::error!("Failed to CBOR-serialize Datum to type `{}`.", stringify!(#name));
1052                    };
1053                    serialized
1054                }
1055            }
1056        });
1057    }
1058
1059    let sql_graph_entity_item = sql_gen::PostgresTypeDerive::from_derive_input(
1060        ast,
1061        args.contains(&PostgresTypeAttribute::PgBinaryProtocol),
1062    )?;
1063    sql_graph_entity_item.to_tokens(&mut stream);
1064
1065    Ok(stream)
1066}
1067
1068/// Derives the `GucEnum` trait, so that normal Rust enums can be used as a GUC.
1069#[proc_macro_derive(PostgresGucEnum, attributes(name, hidden))]
1070pub fn postgres_guc_enum(input: TokenStream) -> TokenStream {
1071    let ast = parse_macro_input!(input as syn::DeriveInput);
1072
1073    impl_guc_enum(ast).unwrap_or_else(|e| e.into_compile_error()).into()
1074}
1075
1076fn impl_guc_enum(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
1077    use std::str::FromStr;
1078    use syn::parse::Parse;
1079
1080    enum GucEnumAttribute {
1081        Name(CString),
1082        Hidden(bool),
1083    }
1084
1085    impl GucEnumAttribute {
1086        fn is_guc_enum_attribute(attribute: &str) -> bool {
1087            matches!(attribute, "name" | "hidden")
1088        }
1089    }
1090
1091    impl Parse for GucEnumAttribute {
1092        fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
1093            let ident: Ident = input.parse()?;
1094            let _: syn::token::Eq = input.parse()?;
1095            match ident.to_string().as_str() {
1096                "name" => input.parse::<syn::LitCStr>().map(|val| Self::Name(val.value())),
1097                "hidden" => input.parse::<syn::LitBool>().map(|val| Self::Hidden(val.value())),
1098                x => Err(syn::Error::new(input.span(), format!("unknown attribute {x}"))),
1099            }
1100        }
1101    }
1102
1103    // validate that we're only operating on an enum
1104    let Data::Enum(data) = ast.data.clone() else {
1105        return Err(syn::Error::new(
1106            ast.span(),
1107            "#[derive(PostgresGucEnum)] can only be applied to enums",
1108        ));
1109    };
1110    let ident = ast.ident.clone();
1111    let mut config = Vec::new();
1112    for (index, variant) in data.variants.iter().enumerate() {
1113        let default_name = CString::from_str(&variant.ident.to_string())
1114            .expect("the identifier contains a null character.");
1115        let default_val = index as i32;
1116        let default_hidden = false;
1117        let mut name = None;
1118        let mut hidden = None;
1119        for attr in variant.attrs.iter() {
1120            if let Some(ident) = attr.path().get_ident() {
1121                if GucEnumAttribute::is_guc_enum_attribute(&ident.to_string()) {
1122                    let pair: GucEnumAttribute = syn::parse2(attr.meta.to_token_stream())?;
1123                    match pair {
1124                        GucEnumAttribute::Name(value) => {
1125                            if name.replace(value).is_some() {
1126                                return Err(syn::Error::new(
1127                                    ast.span(),
1128                                    "too many #[name] attributes",
1129                                ));
1130                            }
1131                        }
1132                        GucEnumAttribute::Hidden(value) => {
1133                            if hidden.replace(value).is_some() {
1134                                return Err(syn::Error::new(
1135                                    ast.span(),
1136                                    "too many #[hidden] attributes",
1137                                ));
1138                            }
1139                        }
1140                    }
1141                }
1142            }
1143        }
1144        let ident = variant.ident.clone();
1145        let name = name.unwrap_or(default_name);
1146        let val = default_val;
1147        let hidden = hidden.unwrap_or(default_hidden);
1148        config.push((ident, name, val, hidden));
1149    }
1150    let config_idents = config.iter().map(|x| &x.0).collect::<Vec<_>>();
1151    let config_names = config.iter().map(|x| &x.1).collect::<Vec<_>>();
1152    let config_vals = config.iter().map(|x| &x.2).collect::<Vec<_>>();
1153    let config_hiddens = config.iter().map(|x| &x.3).collect::<Vec<_>>();
1154
1155    Ok(quote! {
1156        unsafe impl ::pgrx::guc::GucEnum for #ident {
1157            fn from_ordinal(ordinal: i32) -> Self {
1158                match ordinal {
1159                    #(#config_vals => Self::#config_idents,)*
1160                    _ => panic!("Unrecognized ordinal"),
1161                }
1162            }
1163
1164            fn to_ordinal(&self) -> i32 {
1165                match self {
1166                    #(Self::#config_idents => #config_vals,)*
1167                }
1168            }
1169
1170            const CONFIG_ENUM_ENTRY: *const ::pgrx::pg_sys::config_enum_entry = [
1171                #(
1172                    ::pgrx::pg_sys::config_enum_entry {
1173                        name: #config_names.as_ptr(),
1174                        val: #config_vals,
1175                        hidden: #config_hiddens,
1176                    },
1177                )*
1178                ::pgrx::pg_sys::config_enum_entry {
1179                    name: core::ptr::null(),
1180                    val: 0,
1181                    hidden: false,
1182                },
1183            ].as_ptr();
1184        }
1185    })
1186}
1187
1188#[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
1189enum PostgresTypeAttribute {
1190    InOutFuncs,
1191    PgBinaryProtocol,
1192    PgVarlenaInOutFuncs,
1193    Default,
1194    ManualFromIntoDatum,
1195}
1196
1197fn parse_postgres_type_args(attributes: &[Attribute]) -> HashSet<PostgresTypeAttribute> {
1198    let mut categorized_attributes = HashSet::new();
1199
1200    for a in attributes {
1201        let path = &a.path();
1202        let path = quote! {#path}.to_string();
1203        match path.as_str() {
1204            "inoutfuncs" => {
1205                categorized_attributes.insert(PostgresTypeAttribute::InOutFuncs);
1206            }
1207            "pg_binary_protocol" => {
1208                categorized_attributes.insert(PostgresTypeAttribute::PgBinaryProtocol);
1209            }
1210            "pgvarlena_inoutfuncs" => {
1211                categorized_attributes.insert(PostgresTypeAttribute::PgVarlenaInOutFuncs);
1212            }
1213            "bikeshed_postgres_type_manually_impl_from_into_datum" => {
1214                categorized_attributes.insert(PostgresTypeAttribute::ManualFromIntoDatum);
1215            }
1216            _ => {
1217                // we can just ignore attributes we don't understand
1218            }
1219        };
1220    }
1221
1222    categorized_attributes
1223}
1224
1225/**
1226Generate necessary code using the type in operators like `==` and `!=`.
1227
1228```rust,ignore
1229# use pgrx_pg_sys as pg_sys;
1230use pgrx::*;
1231use serde::{Deserialize, Serialize};
1232#[derive(Debug, Serialize, Deserialize, PostgresEnum, PartialEq, Eq, PostgresEq)]
1233enum DogNames {
1234    Nami,
1235    Brandy,
1236}
1237```
1238Optionally accepts the following attributes:
1239
1240* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
1241
1242# No bounds?
1243Unlike some derives, this does not implement a "real" Rust trait, thus
1244PostgresEq cannot be used in trait bounds, nor can it be manually implemented.
1245*/
1246#[proc_macro_derive(PostgresEq, attributes(pgrx))]
1247pub fn derive_postgres_eq(input: TokenStream) -> TokenStream {
1248    let ast = parse_macro_input!(input as syn::DeriveInput);
1249    deriving_postgres_eq(ast).unwrap_or_else(syn::Error::into_compile_error).into()
1250}
1251
1252/**
1253Generate necessary code using the type in operators like `>`, `<`, `<=`, and `>=`.
1254
1255```rust,ignore
1256# use pgrx_pg_sys as pg_sys;
1257use pgrx::*;
1258use serde::{Deserialize, Serialize};
1259#[derive(
1260    Debug, Serialize, Deserialize, PartialEq, Eq,
1261     PartialOrd, Ord, PostgresEnum, PostgresOrd
1262)]
1263enum DogNames {
1264    Nami,
1265    Brandy,
1266}
1267```
1268Optionally accepts the following attributes:
1269
1270* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
1271
1272# No bounds?
1273Unlike some derives, this does not implement a "real" Rust trait, thus
1274PostgresOrd cannot be used in trait bounds, nor can it be manually implemented.
1275*/
1276#[proc_macro_derive(PostgresOrd, attributes(pgrx))]
1277pub fn derive_postgres_ord(input: TokenStream) -> TokenStream {
1278    let ast = parse_macro_input!(input as syn::DeriveInput);
1279    deriving_postgres_ord(ast).unwrap_or_else(syn::Error::into_compile_error).into()
1280}
1281
1282/**
1283Generate necessary code for stable hashing the type so it can be used with `USING hash` indexes.
1284
1285```rust,ignore
1286# use pgrx_pg_sys as pg_sys;
1287use pgrx::*;
1288use serde::{Deserialize, Serialize};
1289#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, PostgresEnum, PostgresHash)]
1290enum DogNames {
1291    Nami,
1292    Brandy,
1293}
1294```
1295Optionally accepts the following attributes:
1296
1297* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
1298
1299# No bounds?
1300Unlike some derives, this does not implement a "real" Rust trait, thus
1301PostgresHash cannot be used in trait bounds, nor can it be manually implemented.
1302*/
1303#[proc_macro_derive(PostgresHash, attributes(pgrx))]
1304pub fn derive_postgres_hash(input: TokenStream) -> TokenStream {
1305    let ast = parse_macro_input!(input as syn::DeriveInput);
1306    deriving_postgres_hash(ast).unwrap_or_else(syn::Error::into_compile_error).into()
1307}
1308
1309/// Derives the `ToAggregateName` trait.
1310#[proc_macro_derive(AggregateName, attributes(aggregate_name))]
1311pub fn derive_aggregate_name(input: TokenStream) -> TokenStream {
1312    let ast = parse_macro_input!(input as syn::DeriveInput);
1313
1314    impl_aggregate_name(ast).unwrap_or_else(|e| e.into_compile_error()).into()
1315}
1316
1317fn impl_aggregate_name(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
1318    let name = &ast.ident;
1319
1320    let mut custom_name_value: Option<String> = None;
1321
1322    for attr in &ast.attrs {
1323        if attr.path().is_ident("aggregate_name") {
1324            let meta = &attr.meta;
1325            match meta {
1326                syn::Meta::NameValue(syn::MetaNameValue {
1327                    value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), .. }),
1328                    ..
1329                }) => {
1330                    custom_name_value = Some(s.value());
1331                    break;
1332                }
1333                _ => {
1334                    return Err(syn::Error::new_spanned(
1335                            attr,
1336                            "#[aggregate_name] must be in the form `#[aggregate_name = \"string_literal\"]`",
1337                        ));
1338                }
1339            }
1340        }
1341    }
1342
1343    let name_str = custom_name_value.unwrap_or(name.to_string());
1344
1345    let expanded = quote! {
1346        impl ::pgrx::aggregate::ToAggregateName for #name {
1347            const NAME: &'static str = #name_str;
1348        }
1349    };
1350
1351    Ok(expanded)
1352}
1353
1354/**
1355Declare a `pgrx::Aggregate` implementation on a type as able to used by Postgres as an aggregate.
1356
1357Functions inside the `impl` may use the [`#[pgrx]`](macro@pgrx) attribute.
1358*/
1359#[proc_macro_attribute]
1360pub fn pg_aggregate(_attr: TokenStream, item: TokenStream) -> TokenStream {
1361    // We don't care about `_attr` as we can find it in the `ItemMod`.
1362    fn wrapped(item_impl: ItemImpl) -> Result<TokenStream, syn::Error> {
1363        let sql_graph_entity_item = PgAggregate::new(item_impl)?;
1364
1365        Ok(sql_graph_entity_item.to_token_stream().into())
1366    }
1367
1368    let parsed_base = parse_macro_input!(item as syn::ItemImpl);
1369    wrapped(parsed_base).unwrap_or_else(|e| e.into_compile_error().into())
1370}
1371
1372/**
1373A helper attribute for various contexts.
1374
1375## Usage with [`#[pg_aggregate]`](macro@pg_aggregate).
1376
1377It can be decorated on functions inside a [`#[pg_aggregate]`](macro@pg_aggregate) implementation.
1378In this position, it takes the same args as [`#[pg_extern]`](macro@pg_extern), and those args have the same effect.
1379
1380## Usage for configuring SQL generation
1381
1382This attribute can be used to control the behavior of the SQL generator on a decorated item,
1383e.g. `#[pgrx(sql = false)]`
1384
1385Currently `sql` can be provided one of the following:
1386
1387* Disable SQL generation with `#[pgrx(sql = false)]`
1388* Call custom SQL generator function with `#[pgrx(sql = path::to_function)]`
1389* Render a specific fragment of SQL with a string `#[pgrx(sql = "CREATE FUNCTION ...")]`
1390
1391*/
1392#[proc_macro_attribute]
1393pub fn pgrx(_attr: TokenStream, item: TokenStream) -> TokenStream {
1394    item
1395}
1396
1397/**
1398Create a [PostgreSQL trigger function](https://www.postgresql.org/docs/current/plpgsql-trigger.html)
1399
1400Review the `pgrx::trigger_support::PgTrigger` documentation for use.
1401
1402 */
1403#[proc_macro_attribute]
1404pub fn pg_trigger(attrs: TokenStream, input: TokenStream) -> TokenStream {
1405    fn wrapped(attrs: TokenStream, input: TokenStream) -> Result<TokenStream, syn::Error> {
1406        use pgrx_sql_entity_graph::{PgTrigger, PgTriggerAttribute};
1407        use syn::parse::Parser;
1408        use syn::punctuated::Punctuated;
1409        use syn::Token;
1410
1411        let attributes =
1412            Punctuated::<PgTriggerAttribute, Token![,]>::parse_terminated.parse(attrs)?;
1413        let item_fn: syn::ItemFn = syn::parse(input)?;
1414        let trigger_item = PgTrigger::new(item_fn, attributes)?;
1415        let trigger_tokens = trigger_item.to_token_stream();
1416
1417        Ok(trigger_tokens.into())
1418    }
1419
1420    wrapped(attrs, input).unwrap_or_else(|e| e.into_compile_error().into())
1421}