pgx_utils/sql_entity_graph/pg_extern/
mod.rs1mod argument;
19mod attribute;
20pub mod entity;
21mod operator;
22mod returning;
23mod search_path;
24
25pub use argument::PgExternArgument;
26pub use operator::PgOperator;
27pub use returning::NameMacro;
28
29use crate::sql_entity_graph::ToSqlConfig;
30use crate::staticize_lifetimes;
31use attribute::Attribute;
32use operator::{PgxOperatorAttributeWithIdent, PgxOperatorOpName};
33use search_path::SearchPathList;
34
35use eyre::WrapErr;
36use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
37use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
38use syn::parse::{Parse, ParseStream, Parser};
39use syn::punctuated::Punctuated;
40use syn::spanned::Spanned;
41use syn::{Meta, Token};
42
43use self::returning::Returning;
44
45use super::UsedType;
46
47#[derive(Debug, Clone)]
69pub struct PgExtern {
70 attrs: Vec<Attribute>,
71 func: syn::ItemFn,
72 to_sql_config: ToSqlConfig,
73}
74
75impl PgExtern {
76 pub fn new(attr: TokenStream2, item: TokenStream2) -> Result<Self, syn::Error> {
77 let mut attrs = Vec::new();
78 let mut to_sql_config: Option<ToSqlConfig> = None;
79
80 let parser = Punctuated::<Attribute, Token![,]>::parse_terminated;
81 let punctuated_attrs = parser.parse2(attr)?;
82 for pair in punctuated_attrs.into_pairs() {
83 match pair.into_value() {
84 Attribute::Sql(config) => {
85 to_sql_config.get_or_insert(config);
86 }
87 attr => {
88 attrs.push(attr);
89 }
90 }
91 }
92
93 let mut to_sql_config = to_sql_config.unwrap_or_default();
94
95 let func = syn::parse2::<syn::ItemFn>(item)?;
96
97 if let Some(ref mut content) = to_sql_config.content {
98 let value = content.value();
99 let updated_value = value
100 .replace("@FUNCTION_NAME@", &*(func.sig.ident.to_string() + "_wrapper"))
101 + "\n";
102 *content = syn::LitStr::new(&updated_value, Span::call_site());
103 }
104
105 if !to_sql_config.overrides_default() {
106 crate::ident_is_acceptable_to_postgres(&func.sig.ident)?;
107 }
108
109 Ok(Self { attrs, func, to_sql_config })
110 }
111
112 fn name(&self) -> String {
113 self.attrs
114 .iter()
115 .find_map(|a| match a {
116 Attribute::Name(name) => Some(name.value()),
117 _ => None,
118 })
119 .unwrap_or_else(|| self.func.sig.ident.to_string())
120 }
121
122 fn schema(&self) -> Option<String> {
123 self.attrs.iter().find_map(|a| match a {
124 Attribute::Schema(name) => Some(name.value()),
125 _ => None,
126 })
127 }
128
129 pub fn extern_attrs(&self) -> &[Attribute] {
130 self.attrs.as_slice()
131 }
132
133 fn overridden(&self) -> Option<syn::LitStr> {
134 let mut span = None;
135 let mut retval = None;
136 let mut in_commented_sql_block = false;
137 for attr in &self.func.attrs {
138 let meta = attr.parse_meta().ok();
139 if let Some(meta) = meta {
140 if meta.path().is_ident("doc") {
141 let content = match meta {
142 Meta::Path(_) | Meta::List(_) => continue,
143 Meta::NameValue(mnv) => mnv,
144 };
145 if let syn::Lit::Str(ref inner) = content.lit {
146 span.get_or_insert(content.lit.span());
147 if !in_commented_sql_block && inner.value().trim() == "```pgxsql" {
148 in_commented_sql_block = true;
149 } else if in_commented_sql_block && inner.value().trim() == "```" {
150 in_commented_sql_block = false;
151 } else if in_commented_sql_block {
152 let sql = retval.get_or_insert_with(String::default);
153 let line = inner.value().trim_start().replace(
154 "@FUNCTION_NAME@",
155 &*(self.func.sig.ident.to_string() + "_wrapper"),
156 ) + "\n";
157 sql.push_str(&*line);
158 }
159 }
160 }
161 }
162 }
163 retval.map(|s| syn::LitStr::new(s.as_ref(), span.unwrap()))
164 }
165
166 fn operator(&self) -> Option<PgOperator> {
167 let mut skel = Option::<PgOperator>::default();
168 for attr in &self.func.attrs {
169 let last_segment = attr.path.segments.last().unwrap();
170 match last_segment.ident.to_string().as_str() {
171 "opname" => {
172 let attr: PgxOperatorOpName = syn::parse2(attr.tokens.clone())
173 .expect(&format!("Unable to parse {:?}", &attr.tokens));
174 skel.get_or_insert_with(Default::default).opname.get_or_insert(attr);
175 }
176 "commutator" => {
177 let attr: PgxOperatorAttributeWithIdent = syn::parse2(attr.tokens.clone())
178 .expect(&format!("Unable to parse {:?}", &attr.tokens));
179 skel.get_or_insert_with(Default::default).commutator.get_or_insert(attr);
180 }
181 "negator" => {
182 let attr: PgxOperatorAttributeWithIdent = syn::parse2(attr.tokens.clone())
183 .expect(&format!("Unable to parse {:?}", &attr.tokens));
184 skel.get_or_insert_with(Default::default).negator.get_or_insert(attr);
185 }
186 "join" => {
187 let attr: PgxOperatorAttributeWithIdent = syn::parse2(attr.tokens.clone())
188 .expect(&format!("Unable to parse {:?}", &attr.tokens));
189 skel.get_or_insert_with(Default::default).join.get_or_insert(attr);
190 }
191 "restrict" => {
192 let attr: PgxOperatorAttributeWithIdent = syn::parse2(attr.tokens.clone())
193 .expect(&format!("Unable to parse {:?}", &attr.tokens));
194 skel.get_or_insert_with(Default::default).restrict.get_or_insert(attr);
195 }
196 "hashes" => {
197 skel.get_or_insert_with(Default::default).hashes = true;
198 }
199 "merges" => {
200 skel.get_or_insert_with(Default::default).merges = true;
201 }
202 _ => (),
203 }
204 }
205 skel
206 }
207
208 fn search_path(&self) -> Option<SearchPathList> {
209 self.func
210 .attrs
211 .iter()
212 .find(|f| {
213 f.path
214 .segments
215 .first()
216 .map(|f| f.ident == Ident::new("search_path", Span::call_site()))
217 .unwrap_or_default()
218 })
219 .and_then(|attr| Some(attr.parse_args::<SearchPathList>().unwrap()))
220 }
221
222 fn inputs(&self) -> eyre::Result<Vec<PgExternArgument>> {
223 let mut args = Vec::default();
224 for input in &self.func.sig.inputs {
225 let arg = PgExternArgument::build(input.clone())
226 .wrap_err_with(|| format!("Could not map {:?}", input))?;
227 args.push(arg);
228 }
229 Ok(args)
230 }
231
232 fn returns(&self) -> Result<Returning, syn::Error> {
233 Returning::try_from(&self.func.sig.output)
234 }
235
236 fn entity_tokens(&self) -> TokenStream2 {
237 let ident = &self.func.sig.ident;
238 let name = self.name();
239 let unsafety = &self.func.sig.unsafety;
240 let schema = self.schema();
241 let schema_iter = schema.iter();
242 let extern_attrs = self
243 .attrs
244 .iter()
245 .map(|attr| attr.to_sql_entity_graph_tokens())
246 .collect::<Punctuated<_, Token![,]>>();
247 let search_path = self.search_path().into_iter();
248 let inputs = self.inputs().unwrap();
249 let inputs_iter = inputs.iter().map(|v| v.entity_tokens());
250
251 let input_types = self.func.sig.inputs.iter().filter_map(|v| match v {
252 syn::FnArg::Receiver(_) => None,
253 syn::FnArg::Typed(pat_ty) => {
254 let static_ty = pat_ty.ty.clone();
255 let mut static_ty = UsedType::new(*static_ty).unwrap().resolved_ty;
256 staticize_lifetimes(&mut static_ty);
257 Some(static_ty)
258 }
259 });
260
261 let returns = match self.returns() {
262 Ok(returns) => returns,
263 Err(e) => {
264 let msg = e.to_string();
265 return quote! {
266 std::compile_error!(#msg);
267 };
268 }
269 };
270
271 let return_type = match &self.func.sig.output {
272 syn::ReturnType::Default => None,
273 syn::ReturnType::Type(arrow, ty) => {
274 let mut static_ty = ty.clone();
275 staticize_lifetimes(&mut static_ty);
276 Some(syn::ReturnType::Type(*arrow, static_ty))
277 }
278 };
279
280 let operator = self.operator().into_iter();
281 let to_sql_config = match self.overridden() {
282 None => self.to_sql_config.clone(),
283 Some(content) => {
284 let mut config = self.to_sql_config.clone();
285 config.content = Some(content);
286 config
287 }
288 };
289
290 let sql_graph_entity_fn_name =
291 syn::Ident::new(&format!("__pgx_internals_fn_{}", ident), Span::call_site());
292 quote_spanned! { self.func.sig.span() =>
293 #[no_mangle]
294 #[doc(hidden)]
295 pub extern "Rust" fn #sql_graph_entity_fn_name() -> ::pgx::utils::sql_entity_graph::SqlGraphEntity {
296 extern crate alloc;
297 #[allow(unused_imports)]
298 use alloc::{vec, vec::Vec};
299 type FunctionPointer = #unsafety fn(#( #input_types ),*) #return_type;
300 let metadata: FunctionPointer = #ident;
301 let submission = ::pgx::utils::sql_entity_graph::PgExternEntity {
302 name: #name,
303 unaliased_name: stringify!(#ident),
304 module_path: core::module_path!(),
305 full_path: concat!(core::module_path!(), "::", stringify!(#ident)),
306 metadata: pgx::utils::sql_entity_graph::metadata::FunctionMetadata::entity(&metadata),
307 fn_args: vec![#(#inputs_iter),*],
308 fn_return: #returns,
309 #[allow(clippy::or_fun_call)]
310 schema: None #( .unwrap_or_else(|| Some(#schema_iter)) )*,
311 file: file!(),
312 line: line!(),
313 extern_attrs: vec![#extern_attrs],
314 #[allow(clippy::or_fun_call)]
315 search_path: None #( .unwrap_or_else(|| Some(vec![#search_path])) )*,
316 #[allow(clippy::or_fun_call)]
317 operator: None #( .unwrap_or_else(|| Some(#operator)) )*,
318 to_sql_config: #to_sql_config,
319 };
320 ::pgx::utils::sql_entity_graph::SqlGraphEntity::Function(submission)
321 }
322 }
323 }
324
325 fn finfo_tokens(&self) -> TokenStream2 {
326 let finfo_name = syn::Ident::new(
327 &format!("pg_finfo_{}_wrapper", self.func.sig.ident),
328 Span::call_site(),
329 );
330 quote_spanned! { self.func.sig.span() =>
331 #[no_mangle]
332 #[doc(hidden)]
333 pub extern "C" fn #finfo_name() -> &'static pg_sys::Pg_finfo_record {
334 const V1_API: pg_sys::Pg_finfo_record = pg_sys::Pg_finfo_record { api_version: 1 };
335 &V1_API
336 }
337 }
338 }
339
340 pub fn wrapper_func(&self) -> TokenStream2 {
341 let func_name = &self.func.sig.ident;
342 let func_name_wrapper = Ident::new(
343 &format!("{}_wrapper", &self.func.sig.ident.to_string()),
344 self.func.sig.ident.span(),
345 );
346 let func_generics = &self.func.sig.generics;
347 let is_raw = self.extern_attrs().contains(&Attribute::Raw);
348 let fcinfo_ident = syn::Ident::new("_fcinfo", self.func.sig.ident.span());
350
351 let args = self.inputs().unwrap();
352 let arg_pats = args
353 .iter()
354 .map(|v| syn::Ident::new(&format!("{}_", &v.pat), self.func.sig.span()))
355 .collect::<Vec<_>>();
356 let arg_fetches = args.iter().enumerate().map(|(idx, arg)| {
357 let pat = &arg_pats[idx];
358 let resolved_ty = &arg.used_ty.resolved_ty;
359 if arg.used_ty.resolved_ty.to_token_stream().to_string() == quote!(pgx::pg_sys::FunctionCallInfo).to_token_stream().to_string()
360 || arg.used_ty.resolved_ty.to_token_stream().to_string() == quote!(pg_sys::FunctionCallInfo).to_token_stream().to_string() {
361 quote_spanned! {pat.span()=>
362 let #pat = #fcinfo_ident;
363 }
364 } else if arg.used_ty.resolved_ty.to_token_stream().to_string() == quote!(()).to_token_stream().to_string() {
365 quote_spanned! {pat.span()=>
366 debug_assert!(pgx::pg_getarg::<()>(#fcinfo_ident, #idx).is_none(), "A `()` argument should always recieve `NULL`");
367 let #pat = ();
368 }
369 } else {
370 match (is_raw, &arg.used_ty.optional) {
371 (true, None) | (true, Some(_)) => quote_spanned! { pat.span() =>
372 let #pat = pgx::pg_getarg_datum_raw(#fcinfo_ident, #idx) as #resolved_ty;
373 },
374 (false, None) => quote_spanned! { pat.span() =>
375 let #pat = pgx::pg_getarg::<#resolved_ty>(#fcinfo_ident, #idx).unwrap_or_else(|| panic!("{} is null", stringify!{#pat}));
376 },
377 (false, Some(inner)) => quote_spanned! { pat.span() =>
378 let #pat = pgx::pg_getarg::<#inner>(#fcinfo_ident, #idx);
379 },
380 }
381 }
382 });
383
384 match self.returns().unwrap() {
385 Returning::None => quote_spanned! { self.func.sig.span() =>
386 #[no_mangle]
387 #[doc(hidden)]
388 #[pg_guard]
389 pub unsafe extern "C" fn #func_name_wrapper #func_generics(#fcinfo_ident: ::pgx::pg_sys::FunctionCallInfo) {
390 #(
391 #arg_fetches
392 )*
393
394 #[allow(unused_unsafe)] unsafe { #func_name(#(#arg_pats),*) }
396 }
397 },
398 Returning::Type(retval_ty) => {
399 let result_ident = syn::Ident::new("result", self.func.sig.span());
400 let retval_transform = if retval_ty.resolved_ty == syn::parse_quote!(()) {
401 quote_spanned! { self.func.sig.output.span() =>
402 pgx::pg_return_void()
403 }
404 } else if retval_ty.resolved_ty == syn::parse_quote!(pg_sys::Datum)
405 || retval_ty.resolved_ty == syn::parse_quote!(pgx::pg_sys::Datum)
406 {
407 quote_spanned! { self.func.sig.output.span() =>
408 #result_ident
409 }
410 } else if retval_ty.optional.is_some() {
411 quote_spanned! { self.func.sig.output.span() =>
412 match #result_ident {
413 Some(result) => {
414 pgx::datum::IntoDatum::into_datum(result).unwrap_or_else(|| panic!("returned Option<T> was NULL"))
415 },
416 None => pgx::pg_return_null(#fcinfo_ident)
417 }
418 }
419 } else {
420 quote_spanned! { self.func.sig.output.span() =>
421 pgx::datum::IntoDatum::into_datum(#result_ident).unwrap_or_else(|| panic!("returned Datum was NULL"))
422 }
423 };
424
425 quote_spanned! { self.func.sig.span() =>
426 #[no_mangle]
427 #[doc(hidden)]
428 #[pg_guard]
429 pub unsafe extern "C" fn #func_name_wrapper #func_generics(#fcinfo_ident: ::pgx::pg_sys::FunctionCallInfo) -> ::pgx::pg_sys::Datum {
430 #(
431 #arg_fetches
432 )*
433
434 #[allow(unused_unsafe)] let #result_ident = unsafe { #func_name(#(#arg_pats),*) };
436
437 #retval_transform
438 }
439 }
440 }
441 Returning::SetOf {
442 ty: retval_ty,
443 optional,
444 } => {
445 let result_ident = syn::Ident::new("result", self.func.sig.span());
446 let retval_ty_resolved = retval_ty.original_ty;
447 let result_handler = if optional {
448 quote_spanned! { self.func.sig.span() =>
450 #func_name(#(#arg_pats),*)
451 }
452 } else {
453 quote_spanned! { self.func.sig.span() =>
454 Some(#func_name(#(#arg_pats),*))
455 }
456 };
457
458 quote_spanned! { self.func.sig.span() =>
459 #[no_mangle]
460 #[doc(hidden)]
461 #[pg_guard]
462 #[warn(unsafe_op_in_unsafe_fn)]
463 pub unsafe extern "C" fn #func_name_wrapper #func_generics(#fcinfo_ident: ::pgx::pg_sys::FunctionCallInfo) -> ::pgx::pg_sys::Datum {
464 struct IteratorHolder<'__pgx_internal_lifetime, T: std::panic::UnwindSafe + std::panic::RefUnwindSafe> {
465 iter: *mut SetOfIterator<'__pgx_internal_lifetime, T>,
466 }
467
468 let mut funcctx: ::pgx::PgBox<pg_sys::FuncCallContext>;
469 let mut iterator_holder: ::pgx::PgBox<IteratorHolder<#retval_ty_resolved>>;
470
471 unsafe {
472 if ::pgx::srf_is_first_call(#fcinfo_ident) {
473 funcctx = pgx::srf_first_call_init(#fcinfo_ident);
474 funcctx.user_fctx = pgx::PgMemoryContexts::For(funcctx.multi_call_memory_ctx).palloc_struct::<IteratorHolder<#retval_ty_resolved>>() as *mut ::core::ffi::c_void;
475 iterator_holder = pgx::PgBox::from_pg(funcctx.user_fctx as *mut IteratorHolder<#retval_ty_resolved>);
476
477 let #result_ident = match pgx::PgMemoryContexts::For(funcctx.multi_call_memory_ctx).switch_to(|_| {
481 #( #arg_fetches )*
482 #result_handler
483 }) {
484 Some(result) => result,
485 None => {
486 pgx::srf_return_done(#fcinfo_ident, &mut funcctx);
487 return pgx::pg_return_null(#fcinfo_ident)
488 }
489 };
490
491 iterator_holder.iter = pgx::PgMemoryContexts::For(funcctx.multi_call_memory_ctx).leak_trivial_alloc(result);
492 }
493
494 funcctx = pgx::srf_per_call_setup(#fcinfo_ident);
495 iterator_holder = pgx::PgBox::from_pg(funcctx.user_fctx as *mut IteratorHolder<#retval_ty_resolved>);
496 }
497
498 let mut iter = unsafe { Box::from_raw(iterator_holder.iter) };
500 match iter.next() {
501 Some(result) => {
502 Box::leak(iter);
505
506 unsafe { pgx::srf_return_next(#fcinfo_ident, &mut funcctx) };
508 match pgx::datum::IntoDatum::into_datum(result) {
509 Some(datum) => datum,
510 None => pgx::pg_return_null(#fcinfo_ident),
511 }
512 },
513 None => {
514 Box::leak(iter);
517
518 unsafe { pgx::srf_return_done(#fcinfo_ident, &mut funcctx) };
520 pgx::pg_return_null(#fcinfo_ident)
521 },
522 }
523 }
524 }
525 }
526 Returning::Iterated {
527 tys: retval_tys,
528 optional,
529 } => {
530 let result_ident = syn::Ident::new("result", self.func.sig.span());
531 let funcctx_ident = syn::Ident::new("funcctx", self.func.sig.span());
532 let retval_tys_resolved = retval_tys.iter().map(|v| &v.used_ty.resolved_ty);
533 let retval_tys_tuple = quote! { (#(#retval_tys_resolved,)*) };
534
535 let retval_tuple_indexes = (0..retval_tys.len()).map(syn::Index::from);
536 let retval_tuple_len = retval_tuple_indexes.len();
537 let create_heap_tuple = quote! {
538 let mut datums: [::pgx::pg_sys::Datum; #retval_tuple_len] = [::pgx::pg_sys::Datum::from(0); #retval_tuple_len];
539 let mut nulls: [bool; #retval_tuple_len] = [false; #retval_tuple_len];
540
541 #(
542 let datum = pgx::datum::IntoDatum::into_datum(result.#retval_tuple_indexes);
543 match datum {
544 Some(datum) => { datums[#retval_tuple_indexes] = datum.into(); },
545 None => { nulls[#retval_tuple_indexes] = true; }
546 }
547 )*
548
549 let heap_tuple = unsafe { pgx::pg_sys::heap_form_tuple(#funcctx_ident.tuple_desc, datums.as_mut_ptr(), nulls.as_mut_ptr()) };
551 };
552
553 let result_handler = if optional {
554 quote_spanned! { self.func.sig.span() =>
556 #func_name(#(#arg_pats),*)
557 }
558 } else {
559 quote_spanned! { self.func.sig.span() =>
560 Some(#func_name(#(#arg_pats),*))
561 }
562 };
563
564 quote_spanned! { self.func.sig.span() =>
565 #[no_mangle]
566 #[doc(hidden)]
567 #[pg_guard]
568 #[warn(unsafe_op_in_unsafe_fn)]
569 pub unsafe extern "C" fn #func_name_wrapper #func_generics(#fcinfo_ident: ::pgx::pg_sys::FunctionCallInfo) -> ::pgx::pg_sys::Datum {
570 struct IteratorHolder<'__pgx_internal_lifetime, T: std::panic::UnwindSafe + std::panic::RefUnwindSafe> {
571 iter: *mut ::pgx::iter::TableIterator<'__pgx_internal_lifetime, T>,
572 }
573
574 let mut funcctx: pgx::PgBox<pg_sys::FuncCallContext>;
575 let mut iterator_holder: pgx::PgBox<IteratorHolder<#retval_tys_tuple>>;
576
577 unsafe {
578 if ::pgx::srf_is_first_call(#fcinfo_ident) {
579 funcctx = ::pgx::srf_first_call_init(#fcinfo_ident);
580 funcctx.user_fctx = pgx::PgMemoryContexts::For(funcctx.multi_call_memory_ctx).palloc_struct::<IteratorHolder<#retval_tys_tuple>>() as *mut ::core::ffi::c_void;
581 funcctx.tuple_desc = pgx::PgMemoryContexts::For(funcctx.multi_call_memory_ctx).switch_to(|_| {
582 let mut tupdesc: *mut pgx::pg_sys::TupleDescData = std::ptr::null_mut();
583
584 if pgx::pg_sys::get_call_result_type(#fcinfo_ident, std::ptr::null_mut(), &mut tupdesc) != pgx::pg_sys::TypeFuncClass_TYPEFUNC_COMPOSITE {
586 pgx::error!("return type must be a row type");
587 }
588
589 pgx::pg_sys::BlessTupleDesc(tupdesc)
590 });
591 iterator_holder = pgx::PgBox::from_pg(funcctx.user_fctx as *mut IteratorHolder<#retval_tys_tuple>);
592
593 let #result_ident = match pgx::PgMemoryContexts::For(funcctx.multi_call_memory_ctx).switch_to(|_| {
597 #( #arg_fetches )*
598 #result_handler
599 }) {
600 Some(result) => result,
601 None => {
602 pgx::srf_return_done(#fcinfo_ident, &mut funcctx);
603 return pgx::pg_return_null(#fcinfo_ident)
604 }
605 };
606
607 iterator_holder.iter = pgx::PgMemoryContexts::For(funcctx.multi_call_memory_ctx).leak_and_drop_on_delete(result);
608 }
609
610 funcctx = pgx::srf_per_call_setup(#fcinfo_ident);
611 iterator_holder = pgx::PgBox::from_pg(funcctx.user_fctx as *mut IteratorHolder<#retval_tys_tuple>);
612 }
613
614 let mut iter = unsafe { Box::from_raw(iterator_holder.iter) };
616 match iter.next() {
617 Some(result) => {
618 Box::leak(iter);
621
622 #create_heap_tuple
623
624 let datum = pgx::heap_tuple_get_datum(heap_tuple);
625 unsafe { pgx::srf_return_next(#fcinfo_ident, &mut funcctx) };
627 datum
628 },
629 None => {
630 Box::leak(iter);
633
634 unsafe { pgx::srf_return_done(#fcinfo_ident, &mut funcctx) };
636 pgx::pg_return_null(#fcinfo_ident)
637 },
638 }
639 }
640 }
641 }
642 }
665 }
666}
667
668impl ToTokens for PgExtern {
669 fn to_tokens(&self, tokens: &mut TokenStream2) {
670 let original_func = &self.func;
671 let wrapper_func = self.wrapper_func();
672 let entity_func = self.entity_tokens();
673 let finfo_tokens = self.finfo_tokens();
674
675 let expansion = quote_spanned! { self.func.sig.span() =>
676 #original_func
677
678 #wrapper_func
679
680 #entity_func
681
682 #finfo_tokens
683 };
684 tokens.append_all(expansion);
685 }
686}
687
688impl Parse for PgExtern {
689 fn parse(input: ParseStream) -> Result<Self, syn::Error> {
690 let mut attrs = Vec::new();
691 let mut to_sql_config: Option<ToSqlConfig> = None;
692
693 let parser = Punctuated::<Attribute, Token![,]>::parse_terminated;
694 let punctuated_attrs = input.call(parser).ok().unwrap_or_default();
695 for pair in punctuated_attrs.into_pairs() {
696 match pair.into_value() {
697 Attribute::Sql(config) => {
698 to_sql_config.get_or_insert(config);
699 }
700 attr => {
701 attrs.push(attr);
702 }
703 }
704 }
705
706 let to_sql_config = to_sql_config.unwrap_or_default();
707
708 let func: syn::ItemFn = input.parse()?;
709
710 if !to_sql_config.overrides_default() {
711 crate::ident_is_acceptable_to_postgres(&func.sig.ident)?;
712 }
713
714 Ok(Self { attrs, func, to_sql_config })
715 }
716}