@@ -612,6 +612,8 @@ impl TypesResolver {
612612 fn push_type ( & mut self , original_name : impl Display , oid_expr : impl Display ) {
613613 use std:: fmt:: Write ;
614614
615+ tracing:: trace!( %original_name, %oid_expr, "push_type" ) ;
616+
615617 // Lazily push the preamble to `self.query` so we don't allocate in the fast path
616618 // (all types already known)
617619 if self . query . is_empty ( ) {
@@ -627,9 +629,10 @@ impl TypesResolver {
627629 typbasetype,\n \
628630 rngsubtype,\n \
629631 COALESCE(\
630- (SELECT array_agg(enumlabel) OVER (ORDER BY enumsortorder) \n \
632+ (SELECT array_agg(enumlabel) FROM (SELECT * \n \
631633 FROM pg_catalog.pg_enum\n \
632- WHERE enumtypid = pg_type.oid),\n \
634+ WHERE enumtypid = pg_type.oid\n \
635+ ORDER BY enumsortorder)),\n \
633636 '{{}}') enum_labels,\n \
634637 COALESCE(\n \
635638 (SELECT array_agg((attname, atttypid)) FROM (SELECT *\n \
@@ -650,10 +653,16 @@ impl TypesResolver {
650653 }
651654
652655 async fn fill_cache ( & mut self , conn : & mut PgConnection ) -> Result < ( ) , Error > {
653- let mut existing_dependencies = HashMap :: < Oid , Vec < TypeResolverRow > > :: new ( ) ;
656+ let mut missing_dependencies = HashMap :: < Oid , Vec < TypeResolverRow > > :: new ( ) ;
657+
658+ // Iteratively resolve types until all are resolved, or we hit a dead-end.
659+ // We statically cap the number of iterations in case we somehow encounter a circular type
660+ // dependency, which I *assume* Postgres should forbid.
661+ for _ in 0 ..64 {
662+ if self . query . is_empty ( ) {
663+ break ;
664+ }
654665
655- // Iteratively resolve types until all or resolved or we hit a dead-end
656- while !self . query . is_empty ( ) {
657666 // * Cancel-safety
658667 // * Makes this type reusable if we want to for whatever reason
659668 // * Avoids an allocation when converting to `SqlStr`
@@ -665,18 +674,26 @@ impl TypesResolver {
665674 LEFT JOIN pg_catalog.pg_range ON pg_type.oid = pg_range.rngtypid",
666675 ) ;
667676
668- let types = raw_sql ( AssertSqlSafe ( query) ) . fetch_all ( & mut * conn ) . await ? ;
677+ tracing :: trace! ( ? query, "fill_cache" ) ;
669678
670- let mut new_dependencies = HashMap :: < Oid , Vec < TypeResolverRow > > :: new ( ) ;
679+ let types = raw_sql ( AssertSqlSafe ( query ) ) . fetch_all ( & mut * conn ) . await ? ;
671680
672681 ' outer: for row in types {
673682 let mut type_row = TypeResolverRow :: from_row ( & row) ?;
674683
684+ tracing:: trace!( "type_row: {type_row:?}" ) ;
685+
675686 let mut resolved_dependencies = VecDeque :: new ( ) ;
676687
677688 loop {
678689 if let ControlFlow :: Break ( missing_oid) = conn. try_cache_type ( & type_row) ? {
679- new_dependencies
690+ tracing:: trace!(
691+ ty_name = type_row. catalog_name,
692+ missing_oid = missing_oid. 0 ,
693+ "type missing dependency"
694+ ) ;
695+
696+ missing_dependencies
680697 . entry ( missing_oid)
681698 . or_default ( )
682699 . push ( type_row) ;
@@ -687,28 +704,32 @@ impl TypesResolver {
687704 }
688705
689706 resolved_dependencies. extend (
690- existing_dependencies
707+ missing_dependencies
691708 . remove ( & type_row. oid )
692709 . unwrap_or_default ( ) ,
693710 ) ;
694711
695712 // Iteratively mark existing dependencies as resolved
696713 if let Some ( next_row) = resolved_dependencies. pop_back ( ) {
714+ tracing:: trace!(
715+ resolved_oid = type_row. oid. 0 ,
716+ ty_name = next_row. catalog_name,
717+ "resolved dependency"
718+ ) ;
719+
697720 type_row = next_row
698721 } else {
699722 break ;
700723 }
701724 }
702725 }
726+ }
703727
704- if !existing_dependencies. is_empty ( ) {
705- return Err ( Error :: Protocol ( format ! (
706- "unable to resolve type OIDs: {:?}" ,
707- existing_dependencies. keys( )
708- ) ) ) ;
709- }
710-
711- existing_dependencies = new_dependencies;
728+ if !missing_dependencies. is_empty ( ) {
729+ return Err ( Error :: Protocol ( format ! (
730+ "unable to resolve type OIDs: {:?}" ,
731+ missing_dependencies. keys( )
732+ ) ) ) ;
712733 }
713734
714735 Ok ( ( ) )
0 commit comments