From 81ba72a80f0ddfc9696e032112dc4b47a4beb64f Mon Sep 17 00:00:00 2001 From: "Eric B. Ridge" Date: Mon, 31 Jul 2023 13:47:11 -0400 Subject: [PATCH] Fix issue #1076: Properly handle dependency graph of `Result` When a `#[pg_extern]` function returns `Result`, the entity graph was using the "type_id" of that whole type instead of just `T`, which is what would be in the graph. This detects that type from the first argument of the return type and uses it in the code generation. --- pgrx-sql-entity-graph/src/used_type.rs | 36 ++++++++++++++++++++++++-- pgrx-tests/src/tests/result_tests.rs | 19 ++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/pgrx-sql-entity-graph/src/used_type.rs b/pgrx-sql-entity-graph/src/used_type.rs index 5c53a1081..f0aec6846 100644 --- a/pgrx-sql-entity-graph/src/used_type.rs +++ b/pgrx-sql-entity-graph/src/used_type.rs @@ -31,6 +31,7 @@ use super::metadata::FunctionMetadataTypeEntity; pub struct UsedType { pub original_ty: syn::Type, pub resolved_ty: syn::Type, + pub resolved_ty_inner: Option, /// Set via `composite_type!()` pub composite_type: Option, /// Set via `VariadicArray` or `variadic!()` @@ -283,12 +284,43 @@ impl UsedType { original => (original, false, None, false), }; - Ok(Self { original_ty, resolved_ty, optional, result, variadic, default, composite_type }) + // if the Type is like `Result`, this finds the `T` + let mut resolved_ty_inner: Option = None; + if result { + if let syn::Type::Path(tp) = &resolved_ty { + if let Some(first_segment) = + tp.path.segments.first().map(|segment| Some(segment)).unwrap_or(None) + { + if let syn::PathArguments::AngleBracketed(ab) = &first_segment.arguments { + if let Some(first_arg) = + ab.args.first().map(|arg| Some(arg)).unwrap_or(None) + { + if let syn::GenericArgument::Type(ty) = first_arg { + resolved_ty_inner = Some(ty.clone()); + } + } + } + } + } + } + + Ok(Self { + original_ty, + resolved_ty, + resolved_ty_inner, + optional, + result, + variadic, + default, + composite_type, + }) } pub fn entity_tokens(&self) -> syn::Expr { let mut resolved_ty = self.resolved_ty.clone(); + let mut resolved_ty_inner = self.resolved_ty_inner.clone().unwrap_or(resolved_ty.clone()); staticize_lifetimes(&mut resolved_ty); + staticize_lifetimes(&mut resolved_ty_inner); let resolved_ty_string = resolved_ty.to_token_stream().to_string().replace(" ", ""); let composite_type = self.composite_type.clone().map(|v| v.expr); let composite_type_iter = composite_type.iter(); @@ -299,7 +331,7 @@ impl UsedType { syn::parse_quote! { ::pgrx::pgrx_sql_entity_graph::UsedTypeEntity { ty_source: #resolved_ty_string, - ty_id: core::any::TypeId::of::<#resolved_ty>(), + ty_id: core::any::TypeId::of::<#resolved_ty_inner>(), full_path: core::any::type_name::<#resolved_ty>(), module_path: { let ty_name = core::any::type_name::<#resolved_ty>(); diff --git a/pgrx-tests/src/tests/result_tests.rs b/pgrx-tests/src/tests/result_tests.rs index 99778154f..cc169b995 100644 --- a/pgrx-tests/src/tests/result_tests.rs +++ b/pgrx-tests/src/tests/result_tests.rs @@ -7,6 +7,25 @@ //LICENSE All rights reserved. //LICENSE //LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file. + +use pgrx::prelude::*; +use serde::*; + +// +// the ResultTestsA type and the corresponding function test issue #1076. Should that regress +// it's likely that `CREATE EXTENSION` will fail with: +// +// ERROR SQLSTATE[42710]: type "resulttestsa" already exists +// + +#[derive(Debug, Serialize, Deserialize, PostgresType)] +pub struct ResultTestsA; + +#[pg_extern] +fn result_tests_a_func() -> Result { + Ok(ResultTestsA) +} + #[cfg(any(test, feature = "pg_test"))] #[pgrx::pg_schema] mod tests {