pgx_macros/
lib.rs

1/*
2Portions Copyright 2019-2021 ZomboDB, LLC.
3Portions Copyright 2021-2022 Technology Concepts & Design, Inc. <support@tcdi.com>
4
5All rights reserved.
6
7Use of this source code is governed by the MIT license that can be found in the LICENSE file.
8*/
9
10extern crate proc_macro;
11
12use proc_macro::TokenStream;
13use std::collections::HashSet;
14
15use proc_macro2::Ident;
16use quote::{quote, ToTokens};
17use syn::spanned::Spanned;
18use syn::{parse_macro_input, Attribute, Data, DeriveInput, Item, ItemImpl};
19
20use operators::{impl_postgres_eq, impl_postgres_hash, impl_postgres_ord};
21use pgx_sql_entity_graph::{
22    parse_extern_attributes, CodeEnrichment, ExtensionSql, ExtensionSqlFile, ExternArgs,
23    PgAggregate, PgExtern, PostgresEnum, PostgresType, Schema,
24};
25
26use crate::rewriter::PgGuardRewriter;
27
28mod operators;
29mod rewriter;
30
31/// Declare a function as `#[pg_guard]` to indicate that it is called from a Postgres `extern "C"`
32/// function so that Rust `panic!()`s (and Postgres `elog(ERROR)`s) will be properly handled by `pgx`
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 rewriter = PgGuardRewriter::new();
39
40    let res = match ast {
41        // this is for processing the members of extern "C" { } blocks
42        // functions inside the block get wrapped as public, top-level unsafe functions that are not "extern"
43        Item::ForeignMod(block) => Ok(rewriter.extern_block(block)),
44
45        // process top-level functions
46        Item::Fn(func) => rewriter.item_fn_without_rewrite(func),
47        unknown => Err(syn::Error::new(
48            unknown.span(),
49            "#[pg_guard] can only be applied to extern \"C\" blocks and top-level functions",
50        )),
51    };
52    res.unwrap_or_else(|e| e.into_compile_error()).into()
53}
54
55/// `#[pg_test]` functions are test functions (akin to `#[test]`), but they run in-process inside
56/// Postgres during `cargo pgx test`.
57#[proc_macro_attribute]
58pub fn pg_test(attr: TokenStream, item: TokenStream) -> TokenStream {
59    let mut stream = proc_macro2::TokenStream::new();
60    let args = parse_extern_attributes(proc_macro2::TokenStream::from(attr.clone()));
61
62    let mut expected_error = None;
63    args.into_iter().for_each(|v| {
64        if let ExternArgs::Error(message) = v {
65            expected_error = Some(message)
66        }
67    });
68
69    let ast = parse_macro_input!(item as syn::Item);
70
71    match ast {
72        Item::Fn(mut func) => {
73            // Here we need to break out attributes into test and non-test attributes,
74            // so the generated #[test] attributes are in the appropriate place.
75            let mut test_attributes = Vec::new();
76            let mut non_test_attributes = Vec::new();
77
78            for attribute in func.attrs.iter() {
79                if let Some(ident) = attribute.path.get_ident() {
80                    let ident_str = ident.to_string();
81
82                    if ident_str == "ignore" || ident_str == "should_panic" {
83                        test_attributes.push(attribute.clone());
84                    } else {
85                        non_test_attributes.push(attribute.clone());
86                    }
87                } else {
88                    non_test_attributes.push(attribute.clone());
89                }
90            }
91
92            func.attrs = non_test_attributes;
93
94            stream.extend(proc_macro2::TokenStream::from(pg_extern(
95                attr,
96                Item::Fn(func.clone()).to_token_stream().into(),
97            )));
98
99            let expected_error = match expected_error {
100                Some(msg) => quote! {Some(#msg)},
101                None => quote! {None},
102            };
103
104            let sql_funcname = func.sig.ident.to_string();
105            let test_func_name =
106                Ident::new(&format!("pg_{}", func.sig.ident.to_string()), func.span());
107
108            let attributes = func.attrs;
109            let mut att_stream = proc_macro2::TokenStream::new();
110
111            for a in attributes.iter() {
112                let as_str = a.tokens.to_string();
113                att_stream.extend(quote! {
114                    options.push(#as_str);
115                });
116            }
117
118            stream.extend(quote! {
119                #[test]
120                #(#test_attributes)*
121                fn #test_func_name() {
122                    let mut options = Vec::new();
123                    #att_stream
124
125                    crate::pg_test::setup(options);
126                    let res = pgx_tests::run_test(#sql_funcname, #expected_error, crate::pg_test::postgresql_conf_options());
127                    match res {
128                        Ok(()) => (),
129                        Err(e) => panic!("{:?}", e)
130                    }
131                }
132            });
133        }
134
135        thing => {
136            return syn::Error::new(
137                thing.span(),
138                "#[pg_test] can only be applied to top-level functions",
139            )
140            .to_compile_error()
141            .into()
142        }
143    }
144
145    stream.into()
146}
147
148/// Associated macro for `#[pg_test]` to provide context back to your test framework to indicate
149/// that the test system is being initialized
150#[proc_macro_attribute]
151pub fn initialize(_attr: TokenStream, item: TokenStream) -> TokenStream {
152    item
153}
154
155/// Declare a function as `#[pg_operator]` to indicate that it represents a Postgres operator
156/// `cargo pgx schema` will automatically generate the underlying SQL
157#[proc_macro_attribute]
158pub fn pg_operator(attr: TokenStream, item: TokenStream) -> TokenStream {
159    pg_extern(attr, item)
160}
161
162/// Used with `#[pg_operator]`.  1 value which is the operator name itself
163#[proc_macro_attribute]
164pub fn opname(_attr: TokenStream, item: TokenStream) -> TokenStream {
165    item
166}
167
168/// Used with `#[pg_operator]`.  1 value which is the function name
169#[proc_macro_attribute]
170pub fn commutator(_attr: TokenStream, item: TokenStream) -> TokenStream {
171    item
172}
173
174/// Used with `#[pg_operator]`.  1 value which is the function name
175#[proc_macro_attribute]
176pub fn negator(_attr: TokenStream, item: TokenStream) -> TokenStream {
177    item
178}
179
180/// Used with `#[pg_operator]`.  1 value which is the function name
181#[proc_macro_attribute]
182pub fn restrict(_attr: TokenStream, item: TokenStream) -> TokenStream {
183    item
184}
185
186/// Used with `#[pg_operator]`.  1 value which is the function name
187#[proc_macro_attribute]
188pub fn join(_attr: TokenStream, item: TokenStream) -> TokenStream {
189    item
190}
191
192/// Used with `#[pg_operator]`.  no values
193#[proc_macro_attribute]
194pub fn hashes(_attr: TokenStream, item: TokenStream) -> TokenStream {
195    item
196}
197
198/// Used with `#[pg_operator]`.  no values
199#[proc_macro_attribute]
200pub fn merges(_attr: TokenStream, item: TokenStream) -> TokenStream {
201    item
202}
203
204/**
205Declare a Rust module and its contents to be in a schema.
206
207The schema name will always be the `mod`'s identifier. So `mod flop` will create a `flop` schema.
208
209If there is a schema inside a schema, the most specific schema is chosen.
210
211In this example, the created `example` function is in the `dsl_filters` schema.
212
213```rust,ignore
214use pgx::*;
215
216#[pg_schema]
217mod dsl {
218    use pgx::*;
219    #[pg_schema]
220    mod dsl_filters {
221        use pgx::*;
222        #[pg_extern]
223        fn example() { todo!() }
224    }
225}
226```
227
228File modules (like `mod name;`) aren't able to be supported due to [`rust/#54725`](https://github.com/rust-lang/rust/issues/54725).
229
230*/
231#[proc_macro_attribute]
232pub fn pg_schema(_attr: TokenStream, input: TokenStream) -> TokenStream {
233    fn wrapped(input: TokenStream) -> Result<TokenStream, syn::Error> {
234        let pgx_schema: Schema = syn::parse(input)?;
235        Ok(pgx_schema.to_token_stream().into())
236    }
237
238    match wrapped(input) {
239        Ok(tokens) => tokens,
240        Err(e) => {
241            let msg = e.to_string();
242            TokenStream::from(quote! {
243              compile_error!(#msg);
244            })
245        }
246    }
247}
248
249/**
250Declare SQL to be included in generated extension script.
251
252Accepts a String literal, a `name` attribute, and optionally others:
253
254* `name = "item"`: Set the unique identifier to `"item"` for use in `requires` declarations.
255* `requires = [item, item_two]`: References to other `name`s or Rust items which this SQL should be present after.
256* `creates = [ Type(submod::Cust), Enum(Pre), Function(defined)]`: Communicates that this SQL block creates certain entities.
257  Please note it **does not** create matching Rust types.
258* `bootstrap` (**Unique**): Communicates that this is SQL intended to go before all other generated SQL.
259* `finalize` (**Unique**): Communicates that this is SQL intended to go after all other generated SQL.
260
261You can declare some SQL without any positioning information, meaning it can end up anywhere in the generated SQL:
262
263```rust,ignore
264use pgx_macros::extension_sql;
265
266extension_sql!(
267    r#"
268    -- SQL statements
269    "#,
270    name = "demo",
271);
272```
273
274To cause the SQL to be output at the start of the generated SQL:
275
276```rust,ignore
277use pgx_macros::extension_sql;
278
279extension_sql!(
280    r#"
281    -- SQL statements
282    "#,
283    name = "demo",
284    bootstrap,
285);
286```
287
288To cause the SQL to be output at the end of the generated SQL:
289
290```rust,ignore
291use pgx_macros::extension_sql;
292
293extension_sql!(
294    r#"
295    -- SQL statements
296    "#,
297    name = "demo",
298    finalize,
299);
300```
301
302To declare the SQL dependent, or a dependency of, other items:
303
304```rust,ignore
305use pgx_macros::extension_sql;
306
307struct Treat;
308
309mod dog_characteristics {
310    enum DogAlignment {
311        Good
312    }
313}
314
315extension_sql!(r#"
316    -- SQL statements
317    "#,
318    name = "named_one",
319);
320
321extension_sql!(r#"
322    -- SQL statements
323    "#,
324    name = "demo",
325    requires = [ "named_one", dog_characteristics::DogAlignment ],
326);
327```
328
329To declare the SQL defines some entity (**Caution:** This is not recommended usage):
330
331```rust,ignore
332use pgx::stringinfo::StringInfo;
333use pgx::*;
334use pgx_utils::get_named_capture;
335
336#[derive(Debug)]
337#[repr(C)]
338struct Complex {
339    x: f64,
340    y: f64,
341}
342
343extension_sql!(r#"\
344        CREATE TYPE complex;\
345    "#,
346    name = "create_complex_type",
347    creates = [Type(Complex)],
348);
349
350#[pg_extern(immutable)]
351fn complex_in(input: &core::ffi::CStr) -> PgBox<Complex> {
352    todo!()
353}
354
355#[pg_extern(immutable)]
356fn complex_out(complex: PgBox<Complex>) -> &'static core::ffi::CStr {
357    todo!()
358}
359
360extension_sql!(r#"\
361        CREATE TYPE complex (
362            internallength = 16,
363            input = complex_in,
364            output = complex_out,
365            alignment = double
366        );\
367    "#,
368    name = "demo",
369    requires = ["create_complex_type", complex_in, complex_out],
370);
371
372```
373*/
374#[proc_macro]
375pub fn extension_sql(input: TokenStream) -> TokenStream {
376    fn wrapped(input: TokenStream) -> Result<TokenStream, syn::Error> {
377        let ext_sql: CodeEnrichment<ExtensionSql> = syn::parse(input)?;
378        Ok(ext_sql.to_token_stream().into())
379    }
380
381    match wrapped(input) {
382        Ok(tokens) => tokens,
383        Err(e) => {
384            let msg = e.to_string();
385            TokenStream::from(quote! {
386              compile_error!(#msg);
387            })
388        }
389    }
390}
391
392/**
393Declare SQL (from a file) to be included in generated extension script.
394
395Accepts the same options as [`macro@extension_sql`]. `name` is automatically set to the file name (not the full path).
396
397You can declare some SQL without any positioning information, meaning it can end up anywhere in the generated SQL:
398
399```rust,ignore
400use pgx_macros::extension_sql_file;
401extension_sql_file!(
402    "../static/demo.sql",
403);
404```
405
406To override the default name:
407
408```rust,ignore
409use pgx_macros::extension_sql_file;
410
411extension_sql_file!(
412    "../static/demo.sql",
413    name = "singular",
414);
415```
416
417For all other options, and examples of them, see [`macro@extension_sql`].
418*/
419#[proc_macro]
420pub fn extension_sql_file(input: TokenStream) -> TokenStream {
421    fn wrapped(input: TokenStream) -> Result<TokenStream, syn::Error> {
422        let ext_sql: CodeEnrichment<ExtensionSqlFile> = syn::parse(input)?;
423        Ok(ext_sql.to_token_stream().into())
424    }
425
426    match wrapped(input) {
427        Ok(tokens) => tokens,
428        Err(e) => {
429            let msg = e.to_string();
430            TokenStream::from(quote! {
431              compile_error!(#msg);
432            })
433        }
434    }
435}
436
437/// Associated macro for `#[pg_extern]` or `#[macro@pg_operator]`.  Used to set the `SEARCH_PATH` option
438/// on the `CREATE FUNCTION` statement.
439#[proc_macro_attribute]
440pub fn search_path(_attr: TokenStream, item: TokenStream) -> TokenStream {
441    item
442}
443
444/**
445Declare a function as `#[pg_extern]` to indicate that it can be used by Postgres as a UDF.
446
447Optionally accepts the following attributes:
448
449* `immutable`: Corresponds to [`IMMUTABLE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
450* `strict`: Corresponds to [`STRICT`](https://www.postgresql.org/docs/current/sql-createfunction.html).
451  + In most cases, `#[pg_extern]` can detect when no `Option<T>`s are used, and automatically set this.
452* `stable`: Corresponds to [`STABLE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
453* `volatile`: Corresponds to [`VOLATILE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
454* `raw`: Corresponds to [`RAW`](https://www.postgresql.org/docs/current/sql-createfunction.html).
455* `parallel_safe`: Corresponds to [`PARALLEL SAFE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
456* `parallel_unsafe`: Corresponds to [`PARALLEL UNSAFE`](https://www.postgresql.org/docs/current/sql-createfunction.html).
457* `parallel_restricted`: Corresponds to [`PARALLEL RESTRICTED`](https://www.postgresql.org/docs/current/sql-createfunction.html).
458* `no_guard`: Do not use `#[pg_guard]` with the function.
459* `sql`: Same arguments as [`#[pgx(sql = ..)]`](macro@pgx).
460* `name`: Specifies target function name. Defaults to Rust function name.
461
462Functions can accept and return any type which `pgx` supports. `pgx` supports many PostgreSQL types by default.
463New types can be defined via [`macro@PostgresType`] or [`macro@PostgresEnum`].
464
465
466Without any arguments or returns:
467```rust,ignore
468use pgx::*;
469#[pg_extern]
470fn foo() { todo!() }
471```
472
473# Arguments
474It's possible to pass even complex arguments:
475
476```rust,ignore
477use pgx::*;
478#[pg_extern]
479fn boop(
480    a: i32,
481    b: Option<i32>,
482    c: Vec<i32>,
483    d: Option<Vec<Option<i32>>>
484) { todo!() }
485```
486
487It's possible to set argument defaults, set by PostgreSQL when the function is invoked:
488
489```rust,ignore
490use pgx::*;
491#[pg_extern]
492fn boop(a: default!(i32, 11111)) { todo!() }
493#[pg_extern]
494fn doop(
495    a: default!(Vec<Option<&str>>, "ARRAY[]::text[]"),
496    b: default!(String, "'note the inner quotes!'")
497) { todo!() }
498```
499
500The `default!()` macro may only be used in argument position.
501
502It accepts 2 arguments:
503
504* A type
505* A `bool`, numeric, or SQL string to represent the default. `"NULL"` is a possible value, as is `"'string'"`
506
507**If the default SQL entity created by the extension:** ensure it is added to `requires` as a dependency:
508
509```rust,ignore
510use pgx::*;
511#[pg_extern]
512fn default_value() -> i32 { todo!() }
513
514#[pg_extern(
515    requires = [ default_value, ],
516)]
517fn do_it(
518    a: default!(i32, "default_value()"),
519) { todo!() }
520```
521
522# Returns
523
524It's possible to return even complex values, as well:
525
526```rust,ignore
527use pgx::*;
528#[pg_extern]
529fn boop() -> i32 { todo!() }
530#[pg_extern]
531fn doop() -> Option<i32> { todo!() }
532#[pg_extern]
533fn swoop() -> Option<Vec<Option<i32>>> { todo!() }
534#[pg_extern]
535fn floop() -> (i32, i32) { todo!() }
536```
537
538Like in PostgreSQL, it's possible to return tables using iterators and the `name!()` macro:
539
540```rust,ignore
541use pgx::*;
542#[pg_extern]
543fn floop<'a>() -> TableIterator<'a, (name!(a, i32), name!(b, i32))> {
544    TableIterator::new(None.into_iter())
545}
546
547#[pg_extern]
548fn singular_floop() -> (name!(a, i32), name!(b, i32)) {
549    todo!()
550}
551```
552
553The `name!()` macro may only be used in return position inside the `T` of a `TableIterator<'a, T>`.
554
555It accepts 2 arguments:
556
557* A name, such as `example`
558* A type
559
560# Special Cases
561
562`pg_sys::Oid` is a special cased type alias, in order to use it as an argument or return it must be
563passed with it's full module path (`pg_sys::Oid`) in order to be resolved.
564
565```rust,ignore
566use pgx::*;
567
568#[pg_extern]
569fn example_arg(animals: pg_sys::Oid) {
570    todo!()
571}
572
573#[pg_extern]
574fn example_return() -> pg_sys::Oid {
575    todo!()
576}
577```
578
579*/
580#[proc_macro_attribute]
581pub fn pg_extern(attr: TokenStream, item: TokenStream) -> TokenStream {
582    fn wrapped(attr: TokenStream, item: TokenStream) -> Result<TokenStream, syn::Error> {
583        let pg_extern_item = PgExtern::new(attr.clone().into(), item.clone().into())?;
584        Ok(pg_extern_item.to_token_stream().into())
585    }
586
587    match wrapped(attr, item) {
588        Ok(tokens) => tokens,
589        Err(e) => {
590            let msg = e.to_string();
591            TokenStream::from(quote! {
592              compile_error!(#msg);
593            })
594        }
595    }
596}
597
598/**
599Generate necessary bindings for using the enum with PostgreSQL.
600
601```rust,ignore
602# use pgx_pg_sys as pg_sys;
603use pgx::*;
604use serde::{Deserialize, Serialize};
605#[derive(Debug, Serialize, Deserialize, PostgresEnum)]
606enum DogNames {
607    Nami,
608    Brandy,
609}
610```
611
612*/
613#[proc_macro_derive(PostgresEnum, attributes(requires, pgx))]
614pub fn postgres_enum(input: TokenStream) -> TokenStream {
615    let ast = parse_macro_input!(input as syn::DeriveInput);
616
617    impl_postgres_enum(ast).unwrap_or_else(|e| e.to_compile_error()).into()
618}
619
620fn impl_postgres_enum(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
621    let mut stream = proc_macro2::TokenStream::new();
622    let sql_graph_entity_ast = ast.clone();
623    let enum_ident = &ast.ident;
624    let enum_name = enum_ident.to_string();
625
626    // validate that we're only operating on an enum
627    let enum_data = match ast.data {
628        Data::Enum(e) => e,
629        _ => {
630            return Err(syn::Error::new(
631                ast.span(),
632                "#[derive(PostgresEnum)] can only be applied to enums",
633            ))
634        }
635    };
636
637    let mut from_datum = proc_macro2::TokenStream::new();
638    let mut into_datum = proc_macro2::TokenStream::new();
639
640    for d in enum_data.variants.clone() {
641        let label_ident = &d.ident;
642        let label_string = label_ident.to_string();
643
644        from_datum.extend(quote! { #label_string => Some(#enum_ident::#label_ident), });
645        into_datum.extend(quote! { #enum_ident::#label_ident => Some(::pgx::enum_helper::lookup_enum_by_label(#enum_name, #label_string)), });
646    }
647
648    stream.extend(quote! {
649        impl ::pgx::datum::FromDatum for #enum_ident {
650            #[inline]
651            unsafe fn from_polymorphic_datum(datum: ::pgx::pg_sys::Datum, is_null: bool, typeoid: ::pgx::pg_sys::Oid) -> Option<#enum_ident> {
652                if is_null {
653                    None
654                } else {
655                    // GREPME: non-primitive cast u64 as Oid
656                    let (name, _, _) = ::pgx::enum_helper::lookup_enum_by_oid(unsafe { ::pgx::pg_sys::Oid::from_datum(datum, is_null)? } );
657                    match name.as_str() {
658                        #from_datum
659                        _ => panic!("invalid enum value: {}", name)
660                    }
661                }
662            }
663        }
664
665        impl ::pgx::datum::IntoDatum for #enum_ident {
666            #[inline]
667            fn into_datum(self) -> Option<::pgx::pg_sys::Datum> {
668                match self {
669                    #into_datum
670                }
671            }
672
673            fn type_oid() -> ::pgx::pg_sys::Oid {
674                ::pgx::wrappers::regtypein(#enum_name)
675            }
676
677        }
678    });
679
680    let sql_graph_entity_item = PostgresEnum::from_derive_input(sql_graph_entity_ast)?;
681    sql_graph_entity_item.to_tokens(&mut stream);
682
683    Ok(stream)
684}
685
686/**
687Generate necessary bindings for using the type with PostgreSQL.
688
689```rust,ignore
690# use pgx_pg_sys as pg_sys;
691use pgx::*;
692use serde::{Deserialize, Serialize};
693#[derive(Debug, Serialize, Deserialize, PostgresType)]
694struct Dog {
695    treats_received: i64,
696    pets_gotten: i64,
697}
698
699#[derive(Debug, Serialize, Deserialize, PostgresType)]
700enum Animal {
701    Dog(Dog),
702}
703```
704
705Optionally accepts the following attributes:
706
707* `inoutfuncs(some_in_fn, some_out_fn)`: Define custom in/out functions for the type.
708* `pgvarlena_inoutfuncs(some_in_fn, some_out_fn)`: Define custom in/out functions for the `PgVarlena` of this type.
709* `sql`: Same arguments as [`#[pgx(sql = ..)]`](macro@pgx).
710*/
711#[proc_macro_derive(PostgresType, attributes(inoutfuncs, pgvarlena_inoutfuncs, requires, pgx))]
712pub fn postgres_type(input: TokenStream) -> TokenStream {
713    let ast = parse_macro_input!(input as syn::DeriveInput);
714
715    impl_postgres_type(ast).unwrap_or_else(|e| e.to_compile_error()).into()
716}
717
718fn impl_postgres_type(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
719    let name = &ast.ident;
720    let generics = &ast.generics;
721    let has_lifetimes = generics.lifetimes().next();
722    let funcname_in = Ident::new(&format!("{}_in", name).to_lowercase(), name.span());
723    let funcname_out = Ident::new(&format!("{}_out", name).to_lowercase(), name.span());
724    let mut args = parse_postgres_type_args(&ast.attrs);
725    let mut stream = proc_macro2::TokenStream::new();
726
727    // validate that we're only operating on a struct
728    match ast.data {
729        Data::Struct(_) => { /* this is okay */ }
730        Data::Enum(_) => {
731            // this is okay and if there's an attempt to implement PostgresEnum,
732            // it will result in compile-time error of conflicting implementation
733            // of traits (IntoDatum, inout, etc.)
734        }
735        _ => {
736            return Err(syn::Error::new(
737                ast.span(),
738                "#[derive(PostgresType)] can only be applied to structs or enums",
739            ))
740        }
741    }
742
743    if args.is_empty() {
744        // assume the user wants us to implement the InOutFuncs
745        args.insert(PostgresTypeAttribute::Default);
746    }
747
748    let lifetime = match has_lifetimes {
749        Some(lifetime) => quote! {#lifetime},
750        None => quote! {'static},
751    };
752
753    // all #[derive(PostgresType)] need to implement that trait
754    stream.extend(quote! {
755        impl #generics ::pgx::PostgresType for #name #generics { }
756    });
757
758    // and if we don't have custom inout/funcs, we use the JsonInOutFuncs trait
759    // which implements _in and _out #[pg_extern] functions that just return the type itself
760    if args.contains(&PostgresTypeAttribute::Default) {
761        let inout_generics = if has_lifetimes.is_some() {
762            quote! {#generics}
763        } else {
764            quote! {<'_>}
765        };
766
767        stream.extend(quote! {
768            impl #generics ::pgx::inoutfuncs::JsonInOutFuncs #inout_generics for #name #generics {}
769
770            #[doc(hidden)]
771            #[::pgx::pgx_macros::pg_extern(immutable,parallel_safe)]
772            pub fn #funcname_in #generics(input: Option<&#lifetime ::core::ffi::CStr>) -> Option<#name #generics> {
773                input.map_or_else(|| {
774                    for m in <#name as ::pgx::inoutfuncs::JsonInOutFuncs>::NULL_ERROR_MESSAGE {
775                        ::pgx::pg_sys::error!("{}", m);
776                    }
777                    None
778                }, |i| Some(<#name as ::pgx::inoutfuncs::JsonInOutFuncs>::input(i)))
779            }
780
781            #[doc(hidden)]
782            #[::pgx::pgx_macros::pg_extern(immutable,parallel_safe)]
783            pub fn #funcname_out #generics(input: #name #generics) -> &#lifetime ::core::ffi::CStr {
784                let mut buffer = ::pgx::stringinfo::StringInfo::new();
785                ::pgx::inoutfuncs::JsonInOutFuncs::output(&input, &mut buffer);
786                buffer.into()
787            }
788
789        });
790    } else if args.contains(&PostgresTypeAttribute::InOutFuncs) {
791        // otherwise if it's InOutFuncs our _in/_out functions use an owned type instance
792        stream.extend(quote! {
793            #[doc(hidden)]
794            #[::pgx::pgx_macros::pg_extern(immutable,parallel_safe)]
795            pub fn #funcname_in #generics(input: Option<&#lifetime ::core::ffi::CStr>) -> Option<#name #generics> {
796                input.map_or_else(|| {
797                    for m in <#name as ::pgx::inoutfuncs::InOutFuncs>::NULL_ERROR_MESSAGE {
798                        ::pgx::pg_sys::error!("{}", m);
799                    }
800                    None
801                }, |i| Some(<#name as ::pgx::inoutfuncs::InOutFuncs>::input(i)))
802            }
803
804            #[doc(hidden)]
805            #[::pgx::pgx_macros::pg_extern(immutable,parallel_safe)]
806            pub fn #funcname_out #generics(input: #name #generics) -> &#lifetime ::core::ffi::CStr {
807                let mut buffer = ::pgx::stringinfo::StringInfo::new();
808                ::pgx::inoutfuncs::InOutFuncs::output(&input, &mut buffer);
809                buffer.into()
810            }
811        });
812    } else if args.contains(&PostgresTypeAttribute::PgVarlenaInOutFuncs) {
813        // otherwise if it's PgVarlenaInOutFuncs our _in/_out functions use a PgVarlena
814        stream.extend(quote! {
815            #[doc(hidden)]
816            #[::pgx::pgx_macros::pg_extern(immutable,parallel_safe)]
817            pub fn #funcname_in #generics(input: Option<&#lifetime ::core::ffi::CStr>) -> Option<::pgx::datum::PgVarlena<#name #generics>> {
818                input.map_or_else(|| {
819                    for m in <#name as ::pgx::inoutfuncs::PgVarlenaInOutFuncs>::NULL_ERROR_MESSAGE {
820                        ::pgx::pg_sys::error!("{}", m);
821                    }
822                    None
823                }, |i| Some(<#name as ::pgx::inoutfuncs::PgVarlenaInOutFuncs>::input(i)))
824            }
825
826            #[doc(hidden)]
827            #[::pgx::pgx_macros::pg_extern(immutable,parallel_safe)]
828            pub fn #funcname_out #generics(input: ::pgx::datum::PgVarlena<#name #generics>) -> &#lifetime ::core::ffi::CStr {
829                let mut buffer = ::pgx::stringinfo::StringInfo::new();
830                ::pgx::inoutfuncs::PgVarlenaInOutFuncs::output(&*input, &mut buffer);
831                buffer.into()
832            }
833        });
834    }
835
836    let sql_graph_entity_item = PostgresType::from_derive_input(ast)?;
837    sql_graph_entity_item.to_tokens(&mut stream);
838
839    Ok(stream)
840}
841
842/// Derives the `GucEnum` trait, so that normal Rust enums can be used as a GUC.
843#[proc_macro_derive(PostgresGucEnum, attributes(hidden))]
844pub fn postgres_guc_enum(input: TokenStream) -> TokenStream {
845    let ast = parse_macro_input!(input as syn::DeriveInput);
846
847    impl_guc_enum(ast).unwrap_or_else(|e| e.to_compile_error()).into()
848}
849
850fn impl_guc_enum(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
851    let mut stream = proc_macro2::TokenStream::new();
852
853    // validate that we're only operating on an enum
854    let enum_data = match ast.data {
855        Data::Enum(e) => e,
856        _ => {
857            return Err(syn::Error::new(
858                ast.span(),
859                "#[derive(PostgresGucEnum)] can only be applied to enums",
860            ))
861        }
862    };
863    let enum_name = ast.ident;
864    let enum_len = enum_data.variants.len();
865
866    let mut from_match_arms = proc_macro2::TokenStream::new();
867    for (idx, e) in enum_data.variants.iter().enumerate() {
868        let label = &e.ident;
869        let idx = idx as i32;
870        from_match_arms.extend(quote! { #idx => #enum_name::#label, })
871    }
872    from_match_arms.extend(quote! { _ => panic!("Unrecognized ordinal ")});
873
874    let mut ordinal_match_arms = proc_macro2::TokenStream::new();
875    for (idx, e) in enum_data.variants.iter().enumerate() {
876        let label = &e.ident;
877        let idx = idx as i32;
878        ordinal_match_arms.extend(quote! { #enum_name::#label => #idx, });
879    }
880
881    let mut build_array_body = proc_macro2::TokenStream::new();
882    for (idx, e) in enum_data.variants.iter().enumerate() {
883        let label = e.ident.to_string();
884        let mut hidden = false;
885
886        for att in e.attrs.iter() {
887            let att = quote! {#att}.to_string();
888            if att == "# [hidden]" {
889                hidden = true;
890                break;
891            }
892        }
893
894        build_array_body.extend(quote! {
895            ::pgx::pgbox::PgBox::<_, ::pgx::pgbox::AllocatedByPostgres>::with(&mut slice[#idx], |v| {
896                v.name = ::pgx::memcxt::PgMemoryContexts::TopMemoryContext.pstrdup(#label);
897                v.val = #idx as i32;
898                v.hidden = #hidden;
899            });
900        });
901    }
902
903    stream.extend(quote! {
904        impl ::pgx::guc::GucEnum<#enum_name> for #enum_name {
905            fn from_ordinal(ordinal: i32) -> #enum_name {
906                match ordinal {
907                    #from_match_arms
908                }
909            }
910
911            fn to_ordinal(&self) -> i32 {
912                match *self {
913                    #ordinal_match_arms
914                }
915            }
916
917            unsafe fn config_matrix(&self) -> *const ::pgx::pg_sys::config_enum_entry {
918                let slice = ::pgx::memcxt::PgMemoryContexts::TopMemoryContext.palloc0_slice::<::pgx::pg_sys::config_enum_entry>(#enum_len + 1usize);
919
920                #build_array_body
921
922                slice.as_ptr()
923            }
924        }
925    });
926
927    Ok(stream)
928}
929
930#[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
931enum PostgresTypeAttribute {
932    InOutFuncs,
933    PgVarlenaInOutFuncs,
934    Default,
935}
936
937fn parse_postgres_type_args(attributes: &[Attribute]) -> HashSet<PostgresTypeAttribute> {
938    let mut categorized_attributes = HashSet::new();
939
940    for a in attributes {
941        let path = &a.path;
942        let path = quote! {#path}.to_string();
943        match path.as_str() {
944            "inoutfuncs" => {
945                categorized_attributes.insert(PostgresTypeAttribute::InOutFuncs);
946            }
947
948            "pgvarlena_inoutfuncs" => {
949                categorized_attributes.insert(PostgresTypeAttribute::PgVarlenaInOutFuncs);
950            }
951
952            _ => {
953                // we can just ignore attributes we don't understand
954            }
955        };
956    }
957
958    categorized_attributes
959}
960
961/**
962Generate necessary code using the type in operators like `==` and `!=`.
963
964```rust,ignore
965# use pgx_pg_sys as pg_sys;
966use pgx::*;
967use serde::{Deserialize, Serialize};
968#[derive(Debug, Serialize, Deserialize, PostgresEnum, PartialEq, Eq, PostgresEq)]
969enum DogNames {
970    Nami,
971    Brandy,
972}
973```
974Optionally accepts the following attributes:
975
976* `sql`: Same arguments as [`#[pgx(sql = ..)]`](macro@pgx).
977*/
978#[proc_macro_derive(PostgresEq, attributes(pgx))]
979pub fn postgres_eq(input: TokenStream) -> TokenStream {
980    let ast = parse_macro_input!(input as syn::DeriveInput);
981    impl_postgres_eq(ast).unwrap_or_else(syn::Error::into_compile_error).into()
982}
983
984/**
985Generate necessary code using the type in operators like `>`, `<`, `<=`, and `>=`.
986
987```rust,ignore
988# use pgx_pg_sys as pg_sys;
989use pgx::*;
990use serde::{Deserialize, Serialize};
991#[derive(
992    Debug, Serialize, Deserialize, PartialEq, Eq,
993     PartialOrd, Ord, PostgresEnum, PostgresOrd
994)]
995enum DogNames {
996    Nami,
997    Brandy,
998}
999```
1000Optionally accepts the following attributes:
1001
1002* `sql`: Same arguments as [`#[pgx(sql = ..)]`](macro@pgx).
1003*/
1004#[proc_macro_derive(PostgresOrd, attributes(pgx))]
1005pub fn postgres_ord(input: TokenStream) -> TokenStream {
1006    let ast = parse_macro_input!(input as syn::DeriveInput);
1007    impl_postgres_ord(ast).unwrap_or_else(syn::Error::into_compile_error).into()
1008}
1009
1010/**
1011Generate necessary code for stable hashing the type so it can be used with `USING hash` indexes.
1012
1013```rust,ignore
1014# use pgx_pg_sys as pg_sys;
1015use pgx::*;
1016use serde::{Deserialize, Serialize};
1017#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, PostgresEnum, PostgresHash)]
1018enum DogNames {
1019    Nami,
1020    Brandy,
1021}
1022```
1023Optionally accepts the following attributes:
1024
1025* `sql`: Same arguments as [`#[pgx(sql = ..)]`](macro@pgx).
1026*/
1027#[proc_macro_derive(PostgresHash, attributes(pgx))]
1028pub fn postgres_hash(input: TokenStream) -> TokenStream {
1029    let ast = parse_macro_input!(input as syn::DeriveInput);
1030    impl_postgres_hash(ast).unwrap_or_else(syn::Error::into_compile_error).into()
1031}
1032
1033/**
1034Declare a `pgx::Aggregate` implementation on a type as able to used by Postgres as an aggregate.
1035
1036Functions inside the `impl` may use the [`#[pgx]`](macro@pgx) attribute.
1037*/
1038#[proc_macro_attribute]
1039pub fn pg_aggregate(_attr: TokenStream, item: TokenStream) -> TokenStream {
1040    // We don't care about `_attr` as we can find it in the `ItemMod`.
1041    fn wrapped(item_impl: ItemImpl) -> Result<TokenStream, syn::Error> {
1042        let sql_graph_entity_item = PgAggregate::new(item_impl.into())?;
1043
1044        Ok(sql_graph_entity_item.to_token_stream().into())
1045    }
1046
1047    let parsed_base = parse_macro_input!(item as syn::ItemImpl);
1048    match wrapped(parsed_base) {
1049        Ok(tokens) => tokens,
1050        Err(e) => {
1051            let msg = e.to_string();
1052            TokenStream::from(quote! {
1053              compile_error!(#msg);
1054            })
1055        }
1056    }
1057}
1058
1059/**
1060A helper attribute for various contexts.
1061
1062## Usage with [`#[pg_aggregate]`](macro@pg_aggregate).
1063
1064It can be decorated on functions inside a [`#[pg_aggregate]`](macro@pg_aggregate) implementation.
1065In this position, it takes the same args as [`#[pg_extern]`](macro@pg_extern), and those args have the same effect.
1066
1067## Usage for configuring SQL generation
1068
1069This attribute can be used to control the behavior of the SQL generator on a decorated item,
1070e.g. `#[pgx(sql = false)]`
1071
1072Currently `sql` can be provided one of the following:
1073
1074* Disable SQL generation with `#[pgx(sql = false)]`
1075* Call custom SQL generator function with `#[pgx(sql = path::to_function)]`
1076* Render a specific fragment of SQL with a string `#[pgx(sql = "CREATE FUNCTION ...")]`
1077
1078*/
1079#[proc_macro_attribute]
1080pub fn pgx(_attr: TokenStream, item: TokenStream) -> TokenStream {
1081    item
1082}
1083
1084/**
1085Create a [PostgreSQL trigger function](https://www.postgresql.org/docs/current/plpgsql-trigger.html)
1086
1087Review the `pgx::trigger_support::PgTrigger` documentation for use.
1088
1089 */
1090#[proc_macro_attribute]
1091pub fn pg_trigger(attrs: TokenStream, input: TokenStream) -> TokenStream {
1092    fn wrapped(attrs: TokenStream, input: TokenStream) -> Result<TokenStream, syn::Error> {
1093        use pgx_sql_entity_graph::{PgTrigger, PgTriggerAttribute};
1094        use syn::parse::Parser;
1095        use syn::punctuated::Punctuated;
1096        use syn::Token;
1097
1098        let attributes =
1099            Punctuated::<PgTriggerAttribute, Token![,]>::parse_terminated.parse(attrs)?;
1100        let item_fn: syn::ItemFn = syn::parse(input)?;
1101        let trigger_item = PgTrigger::new(item_fn, attributes)?;
1102        let trigger_tokens = trigger_item.to_token_stream();
1103
1104        Ok(trigger_tokens.into())
1105    }
1106
1107    match wrapped(attrs, input) {
1108        Ok(tokens) => tokens,
1109        Err(e) => {
1110            let msg = e.to_string();
1111            TokenStream::from(quote! {
1112              compile_error!(#msg);
1113            })
1114        }
1115    }
1116}