Skip to content

Commit

Permalink
Fix issue pgcentralfoundation#1076: Properly handle dependency graph …
Browse files Browse the repository at this point in the history
…of `Result<T, _>`

When a `#[pg_extern]` function returns `Result<T, E>`, 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.
  • Loading branch information
eeeebbbbrrrr committed Jul 31, 2023
1 parent 22d05ed commit 81ba72a
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 2 deletions.
36 changes: 34 additions & 2 deletions pgrx-sql-entity-graph/src/used_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<syn::Type>,
/// Set via `composite_type!()`
pub composite_type: Option<CompositeTypeMacro>,
/// Set via `VariadicArray` or `variadic!()`
Expand Down Expand Up @@ -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<T, E>`, this finds the `T`
let mut resolved_ty_inner: Option<syn::Type> = 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();
Expand All @@ -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>();
Expand Down
19 changes: 19 additions & 0 deletions pgrx-tests/src/tests/result_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ResultTestsA, spi::Error> {
Ok(ResultTestsA)
}

#[cfg(any(test, feature = "pg_test"))]
#[pgrx::pg_schema]
mod tests {
Expand Down

0 comments on commit 81ba72a

Please sign in to comment.