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* `security_definer`: Corresponds to [`SECURITY DEFINER`](https://www.postgresql.org/docs/current/sql-createfunction.html)
496* `security_invoker`: Corresponds to [`SECURITY INVOKER`](https://www.postgresql.org/docs/current/sql-createfunction.html)
497* `parallel_safe`: Corresponds to [`PARALLEL SAFE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
498* `parallel_unsafe`: Corresponds to [`PARALLEL UNSAFE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
499* `parallel_restricted`: Corresponds to [`PARALLEL RESTRICTED`](https://www.postgresql.org/docs/current/sql-createfunction.html).
500* `no_guard`: Do not use `#[pg_guard]` with the function.
501* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
502* `name`: Specifies target function name. Defaults to Rust function name.
503
504Functions can accept and return any type which `pgrx` supports. `pgrx` supports many PostgreSQL types by default.
505New types can be defined via [`macro@PostgresType`] or [`macro@PostgresEnum`].
506
507
508Without any arguments or returns:
509```rust,ignore
510use pgrx::*;
511#[pg_extern]
512fn foo() { todo!() }
513```
514
515# Arguments
516It's possible to pass even complex arguments:
517
518```rust,ignore
519use pgrx::*;
520#[pg_extern]
521fn boop(
522    a: i32,
523    b: Option<i32>,
524    c: Vec<i32>,
525    d: Option<Vec<Option<i32>>>
526) { todo!() }
527```
528
529It's possible to set argument defaults, set by PostgreSQL when the function is invoked:
530
531```rust,ignore
532use pgrx::*;
533#[pg_extern]
534fn boop(a: default!(i32, 11111)) { todo!() }
535#[pg_extern]
536fn doop(
537    a: default!(Vec<Option<&str>>, "ARRAY[]::text[]"),
538    b: default!(String, "'note the inner quotes!'")
539) { todo!() }
540```
541
542The `default!()` macro may only be used in argument position.
543
544It accepts 2 arguments:
545
546* A type
547* A `bool`, numeric, or SQL string to represent the default. `"NULL"` is a possible value, as is `"'string'"`
548
549**If the default SQL entity created by the extension:** ensure it is added to `requires` as a dependency:
550
551```rust,ignore
552use pgrx::*;
553#[pg_extern]
554fn default_value() -> i32 { todo!() }
555
556#[pg_extern(
557    requires = [ default_value, ],
558)]
559fn do_it(
560    a: default!(i32, "default_value()"),
561) { todo!() }
562```
563
564# Returns
565
566It's possible to return even complex values, as well:
567
568```rust,ignore
569use pgrx::*;
570#[pg_extern]
571fn boop() -> i32 { todo!() }
572#[pg_extern]
573fn doop() -> Option<i32> { todo!() }
574#[pg_extern]
575fn swoop() -> Option<Vec<Option<i32>>> { todo!() }
576#[pg_extern]
577fn floop() -> (i32, i32) { todo!() }
578```
579
580Like in PostgreSQL, it's possible to return tables using iterators and the `name!()` macro:
581
582```rust,ignore
583use pgrx::*;
584#[pg_extern]
585fn floop<'a>() -> TableIterator<'a, (name!(a, i32), name!(b, i32))> {
586    TableIterator::new(None.into_iter())
587}
588
589#[pg_extern]
590fn singular_floop() -> (name!(a, i32), name!(b, i32)) {
591    todo!()
592}
593```
594
595The `name!()` macro may only be used in return position inside the `T` of a `TableIterator<'a, T>`.
596
597It accepts 2 arguments:
598
599* A name, such as `example`
600* A type
601
602# Special Cases
603
604`pg_sys::Oid` is a special cased type alias, in order to use it as an argument or return it must be
605passed with it's full module path (`pg_sys::Oid`) in order to be resolved.
606
607```rust,ignore
608use pgrx::*;
609
610#[pg_extern]
611fn example_arg(animals: pg_sys::Oid) {
612    todo!()
613}
614
615#[pg_extern]
616fn example_return() -> pg_sys::Oid {
617    todo!()
618}
619```
620
621*/
622#[proc_macro_attribute]
623#[track_caller]
624pub fn pg_extern(attr: TokenStream, item: TokenStream) -> TokenStream {
625    fn wrapped(attr: TokenStream, item: TokenStream) -> Result<TokenStream, syn::Error> {
626        let pg_extern_item = PgExtern::new(attr.into(), item.into())?;
627        Ok(pg_extern_item.to_token_stream().into())
628    }
629
630    wrapped(attr, item).unwrap_or_else(|e: syn::Error| e.into_compile_error().into())
631}
632
633/**
634Generate necessary bindings for using the enum with PostgreSQL.
635
636```rust,ignore
637# use pgrx_pg_sys as pg_sys;
638use pgrx::*;
639use serde::{Deserialize, Serialize};
640#[derive(Debug, Serialize, Deserialize, PostgresEnum)]
641enum DogNames {
642    Nami,
643    Brandy,
644}
645```
646
647*/
648#[proc_macro_derive(PostgresEnum, attributes(requires, pgrx))]
649pub fn postgres_enum(input: TokenStream) -> TokenStream {
650    let ast = parse_macro_input!(input as syn::DeriveInput);
651
652    impl_postgres_enum(ast).unwrap_or_else(|e| e.into_compile_error()).into()
653}
654
655fn impl_postgres_enum(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
656    let mut stream = proc_macro2::TokenStream::new();
657    let sql_graph_entity_ast = ast.clone();
658    let generics = &ast.generics.clone();
659    let enum_ident = &ast.ident;
660    let enum_name = enum_ident.to_string();
661
662    // validate that we're only operating on an enum
663    let Data::Enum(enum_data) = ast.data else {
664        return Err(syn::Error::new(
665            ast.span(),
666            "#[derive(PostgresEnum)] can only be applied to enums",
667        ));
668    };
669
670    let mut from_datum = proc_macro2::TokenStream::new();
671    let mut into_datum = proc_macro2::TokenStream::new();
672
673    for d in enum_data.variants.clone() {
674        let label_ident = &d.ident;
675        let label_string = label_ident.to_string();
676
677        from_datum.extend(quote! { #label_string => Some(#enum_ident::#label_ident), });
678        into_datum.extend(quote! { #enum_ident::#label_ident => Some(::pgrx::enum_helper::lookup_enum_by_label(#enum_name, #label_string)), });
679    }
680
681    // We need another variant of the params for the ArgAbi impl
682    let fcx_lt = syn::Lifetime::new("'fcx", proc_macro2::Span::mixed_site());
683    let mut generics_with_fcx = generics.clone();
684    // so that we can bound on Self: 'fcx
685    generics_with_fcx.make_where_clause().predicates.push(syn::WherePredicate::Type(
686        syn::PredicateType {
687            lifetimes: None,
688            bounded_ty: syn::parse_quote! { Self },
689            colon_token: syn::Token![:](proc_macro2::Span::mixed_site()),
690            bounds: syn::parse_quote! { #fcx_lt },
691        },
692    ));
693    let (impl_gens, ty_gens, where_clause) = generics_with_fcx.split_for_impl();
694    let mut impl_gens: syn::Generics = syn::parse_quote! { #impl_gens };
695    impl_gens
696        .params
697        .insert(0, syn::GenericParam::Lifetime(syn::LifetimeParam::new(fcx_lt.clone())));
698
699    stream.extend(quote! {
700        impl ::pgrx::datum::FromDatum for #enum_ident {
701            #[inline]
702            unsafe fn from_polymorphic_datum(datum: ::pgrx::pg_sys::Datum, is_null: bool, _typeoid: ::pgrx::pg_sys::Oid) -> Option<#enum_ident> {
703                if is_null {
704                    None
705                } else {
706                    // GREPME: non-primitive cast u64 as Oid
707                    let (name, _, _) = ::pgrx::enum_helper::lookup_enum_by_oid(unsafe { ::pgrx::pg_sys::Oid::from_datum(datum, is_null)? } );
708                    match name.as_str() {
709                        #from_datum
710                        _ => panic!("invalid enum value: {name}")
711                    }
712                }
713            }
714        }
715
716        unsafe impl #impl_gens ::pgrx::callconv::ArgAbi<#fcx_lt> for #enum_ident #ty_gens #where_clause {
717            unsafe fn unbox_arg_unchecked(arg: ::pgrx::callconv::Arg<'_, #fcx_lt>) -> Self {
718                let index = arg.index();
719                unsafe { arg.unbox_arg_using_from_datum().unwrap_or_else(|| panic!("argument {index} must not be null")) }
720            }
721
722        }
723
724        unsafe impl #generics ::pgrx::datum::UnboxDatum for #enum_ident #generics {
725            type As<'dat> = #enum_ident #generics where Self: 'dat;
726            #[inline]
727            unsafe fn unbox<'dat>(d: ::pgrx::datum::Datum<'dat>) -> Self::As<'dat> where Self: 'dat {
728                <Self as ::pgrx::datum::FromDatum>::from_datum(::core::mem::transmute(d), false).unwrap()
729            }
730        }
731
732        impl ::pgrx::datum::IntoDatum for #enum_ident {
733            #[inline]
734            fn into_datum(self) -> Option<::pgrx::pg_sys::Datum> {
735                match self {
736                    #into_datum
737                }
738            }
739
740            fn type_oid() -> ::pgrx::pg_sys::Oid {
741                ::pgrx::wrappers::regtypein(#enum_name)
742            }
743
744        }
745
746        unsafe impl ::pgrx::callconv::BoxRet for #enum_ident {
747            unsafe fn box_into<'fcx>(self, fcinfo: &mut ::pgrx::callconv::FcInfo<'fcx>) -> ::pgrx::datum::Datum<'fcx> {
748                match ::pgrx::datum::IntoDatum::into_datum(self) {
749                    None => fcinfo.return_null(),
750                    Some(datum) => unsafe { fcinfo.return_raw_datum(datum) },
751                }
752            }
753        }
754    });
755
756    let sql_graph_entity_item = PostgresEnum::from_derive_input(sql_graph_entity_ast)?;
757    sql_graph_entity_item.to_tokens(&mut stream);
758
759    Ok(stream)
760}
761
762/**
763Generate necessary bindings for using the type with PostgreSQL.
764
765```rust,ignore
766# use pgrx_pg_sys as pg_sys;
767use pgrx::*;
768use serde::{Deserialize, Serialize};
769#[derive(Debug, Serialize, Deserialize, PostgresType)]
770struct Dog {
771    treats_received: i64,
772    pets_gotten: i64,
773}
774
775#[derive(Debug, Serialize, Deserialize, PostgresType)]
776enum Animal {
777    Dog(Dog),
778}
779```
780
781Optionally accepts the following attributes:
782
783* `inoutfuncs(some_in_fn, some_out_fn)`: Define custom in/out functions for the type.
784* `pgvarlena_inoutfuncs(some_in_fn, some_out_fn)`: Define custom in/out functions for the `PgVarlena` of this type.
785* `pg_binary_protocol`: Use the binary protocol for this type.
786* `pgrx(alignment = "<align>")`: Derive Postgres alignment from Rust type. One of `"on"`, or `"off"`.
787* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
788*/
789#[proc_macro_derive(
790    PostgresType,
791    attributes(
792        inoutfuncs,
793        pgvarlena_inoutfuncs,
794        pg_binary_protocol,
795        bikeshed_postgres_type_manually_impl_from_into_datum,
796        requires,
797        pgrx
798    )
799)]
800pub fn postgres_type(input: TokenStream) -> TokenStream {
801    let ast = parse_macro_input!(input as syn::DeriveInput);
802
803    impl_postgres_type(ast).unwrap_or_else(|e| e.into_compile_error()).into()
804}
805
806fn impl_postgres_type(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
807    let name = &ast.ident;
808    let generics = &ast.generics.clone();
809    let has_lifetimes = generics.lifetimes().next();
810    let funcname_in = Ident::new(&format!("{name}_in").to_lowercase(), name.span());
811    let funcname_out = Ident::new(&format!("{name}_out").to_lowercase(), name.span());
812    let funcname_recv = Ident::new(&format!("{name}_recv").to_lowercase(), name.span());
813    let funcname_send = Ident::new(&format!("{name}_send").to_lowercase(), name.span());
814
815    let mut args = parse_postgres_type_args(&ast.attrs);
816    let mut stream = proc_macro2::TokenStream::new();
817
818    // validate that we're only operating on a struct
819    match ast.data {
820        Data::Struct(_) => { /* this is okay */ }
821        Data::Enum(_) => {
822            // this is okay and if there's an attempt to implement PostgresEnum,
823            // it will result in compile-time error of conflicting implementation
824            // of traits (IntoDatum, inout, etc.)
825        }
826        _ => {
827            return Err(syn::Error::new(
828                ast.span(),
829                "#[derive(PostgresType)] can only be applied to structs or enums",
830            ))
831        }
832    }
833
834    if !args.contains(&PostgresTypeAttribute::InOutFuncs)
835        && !args.contains(&PostgresTypeAttribute::PgVarlenaInOutFuncs)
836    {
837        // assume the user wants us to implement the InOutFuncs
838        args.insert(PostgresTypeAttribute::Default);
839    }
840
841    let lifetime = match has_lifetimes {
842        Some(lifetime) => quote! {#lifetime},
843        None => quote! {'_},
844    };
845
846    // We need another variant of the params for the ArgAbi impl
847    let fcx_lt = syn::Lifetime::new("'fcx", proc_macro2::Span::mixed_site());
848    let mut generics_with_fcx = generics.clone();
849    // so that we can bound on Self: 'fcx
850    generics_with_fcx.make_where_clause().predicates.push(syn::WherePredicate::Type(
851        syn::PredicateType {
852            lifetimes: None,
853            bounded_ty: syn::parse_quote! { Self },
854            colon_token: syn::Token![:](proc_macro2::Span::mixed_site()),
855            bounds: syn::parse_quote! { #fcx_lt },
856        },
857    ));
858    let (impl_gens, ty_gens, where_clause) = generics_with_fcx.split_for_impl();
859    let mut impl_gens: syn::Generics = syn::parse_quote! { #impl_gens };
860    impl_gens
861        .params
862        .insert(0, syn::GenericParam::Lifetime(syn::LifetimeParam::new(fcx_lt.clone())));
863
864    // all #[derive(PostgresType)] need to implement that trait
865    // and also the FromDatum and IntoDatum
866    stream.extend(quote! {
867        impl #generics ::pgrx::datum::PostgresType for #name #generics { }
868    });
869
870    if !args.contains(&PostgresTypeAttribute::ManualFromIntoDatum) {
871        stream.extend(
872            quote! {
873                impl #generics ::pgrx::datum::IntoDatum for #name #generics {
874                    fn into_datum(self) -> Option<::pgrx::pg_sys::Datum> {
875                        #[allow(deprecated)]
876                        Some(unsafe { ::pgrx::datum::cbor_encode(&self) }.into())
877                    }
878
879                    fn type_oid() -> ::pgrx::pg_sys::Oid {
880                        ::pgrx::wrappers::rust_regtypein::<Self>()
881                    }
882                }
883
884                unsafe impl #generics ::pgrx::callconv::BoxRet for #name #generics {
885                    unsafe fn box_into<'fcx>(self, fcinfo: &mut ::pgrx::callconv::FcInfo<'fcx>) -> ::pgrx::datum::Datum<'fcx> {
886                        match ::pgrx::datum::IntoDatum::into_datum(self) {
887                            None => fcinfo.return_null(),
888                            Some(datum) => unsafe { fcinfo.return_raw_datum(datum) },
889                        }
890                    }
891                }
892
893                impl #generics ::pgrx::datum::FromDatum for #name #generics {
894                    unsafe fn from_polymorphic_datum(
895                        datum: ::pgrx::pg_sys::Datum,
896                        is_null: bool,
897                        _typoid: ::pgrx::pg_sys::Oid,
898                    ) -> Option<Self> {
899                        if is_null {
900                            None
901                        } else {
902                            #[allow(deprecated)]
903                            ::pgrx::datum::cbor_decode(datum.cast_mut_ptr())
904                        }
905                    }
906
907                    unsafe fn from_datum_in_memory_context(
908                        mut memory_context: ::pgrx::memcxt::PgMemoryContexts,
909                        datum: ::pgrx::pg_sys::Datum,
910                        is_null: bool,
911                        _typoid: ::pgrx::pg_sys::Oid,
912                    ) -> Option<Self> {
913                        if is_null {
914                            None
915                        } else {
916                            memory_context.switch_to(|_| {
917                                // this gets the varlena Datum copied into this memory context
918                                let varlena = ::pgrx::pg_sys::pg_detoast_datum_copy(datum.cast_mut_ptr());
919                                <Self as ::pgrx::datum::FromDatum>::from_datum(varlena.into(), is_null)
920                            })
921                        }
922                    }
923                }
924
925                unsafe impl #generics ::pgrx::datum::UnboxDatum for #name #generics {
926                    type As<'dat> = Self where Self: 'dat;
927                    unsafe fn unbox<'dat>(datum: ::pgrx::datum::Datum<'dat>) -> Self::As<'dat> where Self: 'dat {
928                        <Self as ::pgrx::datum::FromDatum>::from_datum(::core::mem::transmute(datum), false).unwrap()
929                    }
930                }
931
932                unsafe impl #impl_gens ::pgrx::callconv::ArgAbi<#fcx_lt> for #name #ty_gens #where_clause
933                {
934                        unsafe fn unbox_arg_unchecked(arg: ::pgrx::callconv::Arg<'_, #fcx_lt>) -> Self {
935                        let index = arg.index();
936                        unsafe { arg.unbox_arg_using_from_datum().unwrap_or_else(|| panic!("argument {index} must not be null")) }
937                    }
938                }
939            }
940        )
941    }
942
943    // and if we don't have custom inout/funcs, we use the JsonInOutFuncs trait
944    // which implements _in and _out #[pg_extern] functions that just return the type itself
945    if args.contains(&PostgresTypeAttribute::Default) {
946        stream.extend(quote! {
947            #[doc(hidden)]
948            #[::pgrx::pgrx_macros::pg_extern(immutable, parallel_safe)]
949            pub fn #funcname_in #generics(input: Option<&#lifetime ::core::ffi::CStr>) -> Option<#name #generics> {
950                use ::pgrx::inoutfuncs::json_from_slice;
951                input.map(|cstr| json_from_slice(cstr.to_bytes()).ok()).flatten()
952            }
953
954            #[doc(hidden)]
955            #[::pgrx::pgrx_macros::pg_extern (immutable, parallel_safe)]
956            pub fn #funcname_out #generics(input: #name #generics) -> ::pgrx::ffi::CString {
957                use ::pgrx::inoutfuncs::json_to_vec;
958                let mut bytes = json_to_vec(&input).unwrap();
959                bytes.push(0); // terminate
960                ::pgrx::ffi::CString::from_vec_with_nul(bytes).unwrap()
961            }
962        });
963    } else if args.contains(&PostgresTypeAttribute::InOutFuncs) {
964        // otherwise if it's InOutFuncs our _in/_out functions use an owned type instance
965        stream.extend(quote! {
966            #[doc(hidden)]
967            #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)]
968            pub fn #funcname_in #generics(input: Option<&::core::ffi::CStr>) -> Option<#name #generics> {
969                input.map_or_else(|| {
970                    if let Some(m) = <#name as ::pgrx::inoutfuncs::InOutFuncs>::NULL_ERROR_MESSAGE {
971                        ::pgrx::pg_sys::error!("{m}");
972                    }
973                    None
974                }, |i| Some(<#name as ::pgrx::inoutfuncs::InOutFuncs>::input(i)))
975            }
976
977            #[doc(hidden)]
978            #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)]
979            pub fn #funcname_out #generics(input: #name #generics) -> ::pgrx::ffi::CString {
980                let mut buffer = ::pgrx::stringinfo::StringInfo::new();
981                ::pgrx::inoutfuncs::InOutFuncs::output(&input, &mut buffer);
982                // SAFETY: We just constructed this StringInfo ourselves
983                unsafe { buffer.leak_cstr().to_owned() }
984            }
985        });
986    } else if args.contains(&PostgresTypeAttribute::PgVarlenaInOutFuncs) {
987        // otherwise if it's PgVarlenaInOutFuncs our _in/_out functions use a PgVarlena
988        stream.extend(quote! {
989            #[doc(hidden)]
990            #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)]
991            pub fn #funcname_in #generics(input: Option<&::core::ffi::CStr>) -> Option<::pgrx::datum::PgVarlena<#name #generics>> {
992                input.map_or_else(|| {
993                    if let Some(m) = <#name as ::pgrx::inoutfuncs::PgVarlenaInOutFuncs>::NULL_ERROR_MESSAGE {
994                        ::pgrx::pg_sys::error!("{m}");
995                    }
996                    None
997                }, |i| Some(<#name as ::pgrx::inoutfuncs::PgVarlenaInOutFuncs>::input(i)))
998            }
999
1000            #[doc(hidden)]
1001            #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)]
1002            pub fn #funcname_out #generics(input: ::pgrx::datum::PgVarlena<#name #generics>) -> ::pgrx::ffi::CString {
1003                let mut buffer = ::pgrx::stringinfo::StringInfo::new();
1004                ::pgrx::inoutfuncs::PgVarlenaInOutFuncs::output(&*input, &mut buffer);
1005                // SAFETY: We just constructed this StringInfo ourselves
1006                unsafe { buffer.leak_cstr().to_owned() }
1007            }
1008        });
1009    }
1010
1011    if args.contains(&PostgresTypeAttribute::PgBinaryProtocol) {
1012        // At this time, the `PostgresTypeAttribute` does not impact the way we generate
1013        // the `recv` and `send` functions.
1014        stream.extend(quote! {
1015            #[doc(hidden)]
1016            #[::pgrx::pgrx_macros::pg_extern(immutable, strict, parallel_safe)]
1017            pub fn #funcname_recv #generics(
1018                mut internal: ::pgrx::datum::Internal,
1019            ) -> #name #generics {
1020                let buf = unsafe { internal.get_mut::<::pgrx::pg_sys::StringInfoData>().unwrap() };
1021
1022                let mut serialized = ::pgrx::StringInfo::new();
1023
1024                serialized.push_bytes(&[0u8; ::pgrx::pg_sys::VARHDRSZ]); // reserve space for the header
1025                serialized.push_bytes(unsafe {
1026                    core::slice::from_raw_parts(
1027                        buf.data as *const u8,
1028                        buf.len as usize
1029                    )
1030                });
1031
1032                let size = serialized.len();
1033                let varlena = serialized.into_char_ptr();
1034
1035                unsafe{
1036                    ::pgrx::set_varsize_4b(varlena as *mut ::pgrx::pg_sys::varlena, size as i32);
1037                    buf.cursor = buf.len;
1038                    ::pgrx::datum::cbor_decode(varlena as *mut ::pgrx::pg_sys::varlena)
1039                }
1040            }
1041            #[doc(hidden)]
1042            #[::pgrx::pgrx_macros::pg_extern(immutable, strict, parallel_safe)]
1043            pub fn #funcname_send #generics(input: #name #generics) -> Vec<u8> {
1044                use ::pgrx::datum::{FromDatum, IntoDatum};
1045                let Some(datum): Option<::pgrx::pg_sys::Datum> = input.into_datum() else {
1046                    ::pgrx::error!("Datum of type `{}` is unexpectedly NULL.", stringify!(#name));
1047                };
1048                unsafe {
1049                    let Some(serialized): Option<Vec<u8>> = FromDatum::from_datum(datum, false) else {
1050                        ::pgrx::error!("Failed to CBOR-serialize Datum to type `{}`.", stringify!(#name));
1051                    };
1052                    serialized
1053                }
1054            }
1055        });
1056    }
1057
1058    let sql_graph_entity_item = sql_gen::PostgresTypeDerive::from_derive_input(
1059        ast,
1060        args.contains(&PostgresTypeAttribute::PgBinaryProtocol),
1061    )?;
1062    sql_graph_entity_item.to_tokens(&mut stream);
1063
1064    Ok(stream)
1065}
1066
1067/// Derives the `GucEnum` trait, so that normal Rust enums can be used as a GUC.
1068#[proc_macro_derive(PostgresGucEnum, attributes(name, hidden))]
1069pub fn postgres_guc_enum(input: TokenStream) -> TokenStream {
1070    let ast = parse_macro_input!(input as syn::DeriveInput);
1071
1072    impl_guc_enum(ast).unwrap_or_else(|e| e.into_compile_error()).into()
1073}
1074
1075fn impl_guc_enum(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
1076    use std::str::FromStr;
1077    use syn::parse::Parse;
1078
1079    enum GucEnumAttribute {
1080        Name(CString),
1081        Hidden(bool),
1082    }
1083
1084    impl Parse for GucEnumAttribute {
1085        fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
1086            let ident: Ident = input.parse()?;
1087            let _: syn::token::Eq = input.parse()?;
1088            match ident.to_string().as_str() {
1089                "name" => input.parse::<syn::LitCStr>().map(|val| Self::Name(val.value())),
1090                "hidden" => input.parse::<syn::LitBool>().map(|val| Self::Hidden(val.value())),
1091                x => Err(syn::Error::new(input.span(), format!("unknown attribute {x}"))),
1092            }
1093        }
1094    }
1095
1096    // validate that we're only operating on an enum
1097    let Data::Enum(data) = ast.data.clone() else {
1098        return Err(syn::Error::new(
1099            ast.span(),
1100            "#[derive(PostgresGucEnum)] can only be applied to enums",
1101        ));
1102    };
1103    let ident = ast.ident.clone();
1104    let mut config = Vec::new();
1105    for (index, variant) in data.variants.iter().enumerate() {
1106        let default_name = CString::from_str(&variant.ident.to_string())
1107            .expect("the identifier contains a null character.");
1108        let default_val = index as i32;
1109        let default_hidden = false;
1110        let mut name = None;
1111        let mut hidden = None;
1112        for attr in variant.attrs.iter() {
1113            let tokens = attr.meta.require_name_value()?.to_token_stream();
1114            let pair: GucEnumAttribute = syn::parse2(tokens)?;
1115            match pair {
1116                GucEnumAttribute::Name(value) => {
1117                    if name.replace(value).is_some() {
1118                        return Err(syn::Error::new(ast.span(), "too many #[name] attributes"));
1119                    }
1120                }
1121                GucEnumAttribute::Hidden(value) => {
1122                    if hidden.replace(value).is_some() {
1123                        return Err(syn::Error::new(ast.span(), "too many #[hidden] attributes"));
1124                    }
1125                }
1126            }
1127        }
1128        let ident = variant.ident.clone();
1129        let name = name.unwrap_or(default_name);
1130        let val = default_val;
1131        let hidden = hidden.unwrap_or(default_hidden);
1132        config.push((ident, name, val, hidden));
1133    }
1134    let config_idents = config.iter().map(|x| &x.0).collect::<Vec<_>>();
1135    let config_names = config.iter().map(|x| &x.1).collect::<Vec<_>>();
1136    let config_vals = config.iter().map(|x| &x.2).collect::<Vec<_>>();
1137    let config_hiddens = config.iter().map(|x| &x.3).collect::<Vec<_>>();
1138
1139    Ok(quote! {
1140        unsafe impl ::pgrx::guc::GucEnum for #ident {
1141            fn from_ordinal(ordinal: i32) -> Self {
1142                match ordinal {
1143                    #(#config_vals => Self::#config_idents,)*
1144                    _ => panic!("Unrecognized ordinal"),
1145                }
1146            }
1147
1148            fn to_ordinal(&self) -> i32 {
1149                match self {
1150                    #(Self::#config_idents => #config_vals,)*
1151                }
1152            }
1153
1154            const CONFIG_ENUM_ENTRY: *const ::pgrx::pg_sys::config_enum_entry = [
1155                #(
1156                    ::pgrx::pg_sys::config_enum_entry {
1157                        name: #config_names.as_ptr(),
1158                        val: #config_vals,
1159                        hidden: #config_hiddens,
1160                    },
1161                )*
1162                ::pgrx::pg_sys::config_enum_entry {
1163                    name: core::ptr::null(),
1164                    val: 0,
1165                    hidden: false,
1166                },
1167            ].as_ptr();
1168        }
1169    })
1170}
1171
1172#[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
1173enum PostgresTypeAttribute {
1174    InOutFuncs,
1175    PgBinaryProtocol,
1176    PgVarlenaInOutFuncs,
1177    Default,
1178    ManualFromIntoDatum,
1179}
1180
1181fn parse_postgres_type_args(attributes: &[Attribute]) -> HashSet<PostgresTypeAttribute> {
1182    let mut categorized_attributes = HashSet::new();
1183
1184    for a in attributes {
1185        let path = &a.path();
1186        let path = quote! {#path}.to_string();
1187        match path.as_str() {
1188            "inoutfuncs" => {
1189                categorized_attributes.insert(PostgresTypeAttribute::InOutFuncs);
1190            }
1191            "pg_binary_protocol" => {
1192                categorized_attributes.insert(PostgresTypeAttribute::PgBinaryProtocol);
1193            }
1194            "pgvarlena_inoutfuncs" => {
1195                categorized_attributes.insert(PostgresTypeAttribute::PgVarlenaInOutFuncs);
1196            }
1197            "bikeshed_postgres_type_manually_impl_from_into_datum" => {
1198                categorized_attributes.insert(PostgresTypeAttribute::ManualFromIntoDatum);
1199            }
1200            _ => {
1201                // we can just ignore attributes we don't understand
1202            }
1203        };
1204    }
1205
1206    categorized_attributes
1207}
1208
1209/**
1210Generate necessary code using the type in operators like `==` and `!=`.
1211
1212```rust,ignore
1213# use pgrx_pg_sys as pg_sys;
1214use pgrx::*;
1215use serde::{Deserialize, Serialize};
1216#[derive(Debug, Serialize, Deserialize, PostgresEnum, PartialEq, Eq, PostgresEq)]
1217enum DogNames {
1218    Nami,
1219    Brandy,
1220}
1221```
1222Optionally accepts the following attributes:
1223
1224* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
1225
1226# No bounds?
1227Unlike some derives, this does not implement a "real" Rust trait, thus
1228PostgresEq cannot be used in trait bounds, nor can it be manually implemented.
1229*/
1230#[proc_macro_derive(PostgresEq, attributes(pgrx))]
1231pub fn derive_postgres_eq(input: TokenStream) -> TokenStream {
1232    let ast = parse_macro_input!(input as syn::DeriveInput);
1233    deriving_postgres_eq(ast).unwrap_or_else(syn::Error::into_compile_error).into()
1234}
1235
1236/**
1237Generate necessary code using the type in operators like `>`, `<`, `<=`, and `>=`.
1238
1239```rust,ignore
1240# use pgrx_pg_sys as pg_sys;
1241use pgrx::*;
1242use serde::{Deserialize, Serialize};
1243#[derive(
1244    Debug, Serialize, Deserialize, PartialEq, Eq,
1245     PartialOrd, Ord, PostgresEnum, PostgresOrd
1246)]
1247enum DogNames {
1248    Nami,
1249    Brandy,
1250}
1251```
1252Optionally accepts the following attributes:
1253
1254* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
1255
1256# No bounds?
1257Unlike some derives, this does not implement a "real" Rust trait, thus
1258PostgresOrd cannot be used in trait bounds, nor can it be manually implemented.
1259*/
1260#[proc_macro_derive(PostgresOrd, attributes(pgrx))]
1261pub fn derive_postgres_ord(input: TokenStream) -> TokenStream {
1262    let ast = parse_macro_input!(input as syn::DeriveInput);
1263    deriving_postgres_ord(ast).unwrap_or_else(syn::Error::into_compile_error).into()
1264}
1265
1266/**
1267Generate necessary code for stable hashing the type so it can be used with `USING hash` indexes.
1268
1269```rust,ignore
1270# use pgrx_pg_sys as pg_sys;
1271use pgrx::*;
1272use serde::{Deserialize, Serialize};
1273#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, PostgresEnum, PostgresHash)]
1274enum DogNames {
1275    Nami,
1276    Brandy,
1277}
1278```
1279Optionally accepts the following attributes:
1280
1281* `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx).
1282
1283# No bounds?
1284Unlike some derives, this does not implement a "real" Rust trait, thus
1285PostgresHash cannot be used in trait bounds, nor can it be manually implemented.
1286*/
1287#[proc_macro_derive(PostgresHash, attributes(pgrx))]
1288pub fn derive_postgres_hash(input: TokenStream) -> TokenStream {
1289    let ast = parse_macro_input!(input as syn::DeriveInput);
1290    deriving_postgres_hash(ast).unwrap_or_else(syn::Error::into_compile_error).into()
1291}
1292
1293/// Derives the `ToAggregateName` trait.
1294#[proc_macro_derive(AggregateName, attributes(aggregate_name))]
1295pub fn derive_aggregate_name(input: TokenStream) -> TokenStream {
1296    let ast = parse_macro_input!(input as syn::DeriveInput);
1297
1298    impl_aggregate_name(ast).unwrap_or_else(|e| e.into_compile_error()).into()
1299}
1300
1301fn impl_aggregate_name(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
1302    let name = &ast.ident;
1303
1304    let mut custom_name_value: Option<String> = None;
1305
1306    for attr in &ast.attrs {
1307        if attr.path().is_ident("aggregate_name") {
1308            let meta = &attr.meta;
1309            match meta {
1310                syn::Meta::NameValue(syn::MetaNameValue {
1311                    value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), .. }),
1312                    ..
1313                }) => {
1314                    custom_name_value = Some(s.value());
1315                    break;
1316                }
1317                _ => {
1318                    return Err(syn::Error::new_spanned(
1319                            attr,
1320                            "#[aggregate_name] must be in the form `#[aggregate_name = \"string_literal\"]`",
1321                        ));
1322                }
1323            }
1324        }
1325    }
1326
1327    let name_str = custom_name_value.unwrap_or(name.to_string());
1328
1329    let expanded = quote! {
1330        impl ::pgrx::aggregate::ToAggregateName for #name {
1331            const NAME: &'static str = #name_str;
1332        }
1333    };
1334
1335    Ok(expanded)
1336}
1337
1338/**
1339Declare a `pgrx::Aggregate` implementation on a type as able to used by Postgres as an aggregate.
1340
1341Functions inside the `impl` may use the [`#[pgrx]`](macro@pgrx) attribute.
1342*/
1343#[proc_macro_attribute]
1344pub fn pg_aggregate(_attr: TokenStream, item: TokenStream) -> TokenStream {
1345    // We don't care about `_attr` as we can find it in the `ItemMod`.
1346    fn wrapped(item_impl: ItemImpl) -> Result<TokenStream, syn::Error> {
1347        let sql_graph_entity_item = PgAggregate::new(item_impl)?;
1348
1349        Ok(sql_graph_entity_item.to_token_stream().into())
1350    }
1351
1352    let parsed_base = parse_macro_input!(item as syn::ItemImpl);
1353    wrapped(parsed_base).unwrap_or_else(|e| e.into_compile_error().into())
1354}
1355
1356/**
1357A helper attribute for various contexts.
1358
1359## Usage with [`#[pg_aggregate]`](macro@pg_aggregate).
1360
1361It can be decorated on functions inside a [`#[pg_aggregate]`](macro@pg_aggregate) implementation.
1362In this position, it takes the same args as [`#[pg_extern]`](macro@pg_extern), and those args have the same effect.
1363
1364## Usage for configuring SQL generation
1365
1366This attribute can be used to control the behavior of the SQL generator on a decorated item,
1367e.g. `#[pgrx(sql = false)]`
1368
1369Currently `sql` can be provided one of the following:
1370
1371* Disable SQL generation with `#[pgrx(sql = false)]`
1372* Call custom SQL generator function with `#[pgrx(sql = path::to_function)]`
1373* Render a specific fragment of SQL with a string `#[pgrx(sql = "CREATE FUNCTION ...")]`
1374
1375*/
1376#[proc_macro_attribute]
1377pub fn pgrx(_attr: TokenStream, item: TokenStream) -> TokenStream {
1378    item
1379}
1380
1381/**
1382Create a [PostgreSQL trigger function](https://www.postgresql.org/docs/current/plpgsql-trigger.html)
1383
1384Review the `pgrx::trigger_support::PgTrigger` documentation for use.
1385
1386 */
1387#[proc_macro_attribute]
1388pub fn pg_trigger(attrs: TokenStream, input: TokenStream) -> TokenStream {
1389    fn wrapped(attrs: TokenStream, input: TokenStream) -> Result<TokenStream, syn::Error> {
1390        use pgrx_sql_entity_graph::{PgTrigger, PgTriggerAttribute};
1391        use syn::parse::Parser;
1392        use syn::punctuated::Punctuated;
1393        use syn::Token;
1394
1395        let attributes =
1396            Punctuated::<PgTriggerAttribute, Token![,]>::parse_terminated.parse(attrs)?;
1397        let item_fn: syn::ItemFn = syn::parse(input)?;
1398        let trigger_item = PgTrigger::new(item_fn, attributes)?;
1399        let trigger_tokens = trigger_item.to_token_stream();
1400
1401        Ok(trigger_tokens.into())
1402    }
1403
1404    wrapped(attrs, input).unwrap_or_else(|e| e.into_compile_error().into())
1405}