@@ -701,13 +701,49 @@ async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(strin
701701 return result ;
702702 }
703703
704+ /// <summary>
705+ /// Resolves a catalog pattern (from GetObjects) to the concrete catalog names to query.
706+ /// Centralizes the single wildcard rule for the catalog argument so SEA matches the
707+ /// objects Thrift would (Thrift expands <c>_</c>/<c>%</c> server-side; SEA's SHOW commands
708+ /// take the catalog as a literal backtick identifier, so we expand wildcards client-side):
709+ /// - null → returns null, meaning "all catalogs" (SHOW ... IN ALL CATALOGS).
710+ /// - no wildcard → returns the literal name as-is (no extra round-trip).
711+ /// - contains _ or % → runs SHOW CATALOGS LIKE pattern and returns matched names.
712+ /// </summary>
713+ private async Task < IReadOnlyList < string > ? > ResolveCatalogsAsync ( string ? catalogPattern , CancellationToken cancellationToken )
714+ {
715+ if ( catalogPattern == null )
716+ return null ;
717+
718+ if ( ! MetadataCommandBase . ContainsWildcard ( catalogPattern ) )
719+ return new [ ] { catalogPattern } ;
720+
721+ return await ( ( IGetObjectsDataProvider ) this ) . GetCatalogsAsync ( catalogPattern , cancellationToken ) . ConfigureAwait ( false ) ;
722+ }
723+
704724 async Task < IReadOnlyList < ( string catalog , string schema ) > > IGetObjectsDataProvider . GetSchemasAsync ( string ? catalogPattern , string ? schemaPattern , CancellationToken cancellationToken )
705725 {
706- // Note: catalogPattern comes from GetObjectsResultBuilder which resolves individual
707- // catalog names before calling this method. Despite the "pattern" name (from the
708- // IGetObjectsDataProvider interface), the value passed to ShowSchemasCommand is used
709- // as a literal catalog identifier (backtick-quoted), not a wildcard pattern.
710- string sql = new ShowSchemasCommand ( catalogPattern , schemaPattern ) . Build ( ) ;
726+ var result = new List < ( string , string ) > ( ) ;
727+
728+ // Expand catalog wildcards to concrete catalog names (see ResolveCatalogsAsync).
729+ // A null result means "all catalogs"; each concrete catalog is queried individually.
730+ var catalogs = await ResolveCatalogsAsync ( catalogPattern , cancellationToken ) . ConfigureAwait ( false ) ;
731+ if ( catalogs == null )
732+ {
733+ await CollectSchemasAsync ( null , schemaPattern , result , cancellationToken ) . ConfigureAwait ( false ) ;
734+ }
735+ else
736+ {
737+ foreach ( var catalog in catalogs )
738+ await CollectSchemasAsync ( catalog , schemaPattern , result , cancellationToken ) . ConfigureAwait ( false ) ;
739+ }
740+ return result ;
741+ }
742+
743+ private async Task CollectSchemasAsync ( string ? catalog , string ? schemaPattern ,
744+ List < ( string , string ) > result , CancellationToken cancellationToken )
745+ {
746+ string sql = new ShowSchemasCommand ( catalog , schemaPattern ) . Build ( ) ;
711747
712748 List < RecordBatch > batches ;
713749 try
@@ -716,14 +752,13 @@ async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(strin
716752 }
717753 catch ( DatabricksException ex ) when ( ex . IsObjectNotFoundException ( ) )
718754 {
719- return System . Array . Empty < ( string , string ) > ( ) ;
755+ return ;
720756 }
721757
722758 // SHOW SCHEMAS IN ALL CATALOGS returns 2 columns: databaseName, catalog
723759 // SHOW SCHEMAS IN `catalog` returns 1 column: databaseName
724- bool showSchemasInAllCatalogs = catalogPattern == null ;
760+ bool showSchemasInAllCatalogs = catalog == null ;
725761
726- var result = new List < ( string , string ) > ( ) ;
727762 foreach ( var batch in batches )
728763 {
729764 StringArray ? catalogArray = null ;
@@ -743,19 +778,38 @@ async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(strin
743778 for ( int i = 0 ; i < batch . Length ; i ++ )
744779 {
745780 if ( schemaArray . IsNull ( i ) ) continue ;
746- string catalog = catalogArray != null && ! catalogArray . IsNull ( i )
781+ string catalogValue = catalogArray != null && ! catalogArray . IsNull ( i )
747782 ? catalogArray . GetString ( i )
748- : catalogPattern ?? "" ;
749- result . Add ( ( catalog , schemaArray . GetString ( i ) ) ) ;
783+ : catalog ?? "" ;
784+ result . Add ( ( catalogValue , schemaArray . GetString ( i ) ) ) ;
750785 }
751786 }
752- return result ;
753787 }
754788
755789 async Task < IReadOnlyList < ( string catalog , string schema , string table , string tableType ) > > IGetObjectsDataProvider . GetTablesAsync (
756790 string ? catalogPattern , string ? schemaPattern , string ? tableNamePattern , IReadOnlyList < string > ? tableTypes , CancellationToken cancellationToken )
757791 {
758- string sql = new ShowTablesCommand ( catalogPattern , schemaPattern , tableNamePattern ) . Build ( ) ;
792+ var result = new List < ( string , string , string , string ) > ( ) ;
793+
794+ // Expand catalog wildcards to concrete catalog names (see ResolveCatalogsAsync).
795+ // A null result means "all catalogs"; each concrete catalog is queried individually.
796+ var catalogs = await ResolveCatalogsAsync ( catalogPattern , cancellationToken ) . ConfigureAwait ( false ) ;
797+ if ( catalogs == null )
798+ {
799+ await CollectTablesAsync ( null , schemaPattern , tableNamePattern , tableTypes , result , cancellationToken ) . ConfigureAwait ( false ) ;
800+ }
801+ else
802+ {
803+ foreach ( var catalog in catalogs )
804+ await CollectTablesAsync ( catalog , schemaPattern , tableNamePattern , tableTypes , result , cancellationToken ) . ConfigureAwait ( false ) ;
805+ }
806+ return result ;
807+ }
808+
809+ private async Task CollectTablesAsync ( string ? catalog , string ? schemaPattern , string ? tableNamePattern ,
810+ IReadOnlyList < string > ? tableTypes , List < ( string , string , string , string ) > result , CancellationToken cancellationToken )
811+ {
812+ string sql = new ShowTablesCommand ( catalog , schemaPattern , tableNamePattern ) . Build ( ) ;
759813
760814 List < RecordBatch > batches ;
761815 try
@@ -764,9 +818,8 @@ async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(strin
764818 }
765819 catch ( DatabricksException ex ) when ( ex . IsObjectNotFoundException ( ) )
766820 {
767- return System . Array . Empty < ( string , string , string , string ) > ( ) ;
821+ return ;
768822 }
769- var result = new List < ( string , string , string , string ) > ( ) ;
770823 foreach ( var batch in batches )
771824 {
772825 var catalogArray = TryGetColumn < StringArray > ( batch , "catalogName" ) ;
@@ -795,18 +848,31 @@ async Task<IReadOnlyList<string>> IGetObjectsDataProvider.GetCatalogsAsync(strin
795848 tableArray . GetString ( i ) , tableType ) ) ;
796849 }
797850 }
798- return result ;
799851 }
800852
801853 async Task IGetObjectsDataProvider . PopulateColumnInfoAsync ( string ? catalogPattern , string ? schemaPattern ,
802854 string ? tablePattern , string ? columnPattern ,
803855 Dictionary < string , Dictionary < string , Dictionary < string , TableInfo > > > catalogMap ,
804856 CancellationToken cancellationToken )
805857 {
806- List < RecordBatch > batches ;
858+ // Expand catalog wildcards to concrete catalog names (see ResolveCatalogsAsync) so a
859+ // wildcard catalog isn't sent to SHOW COLUMNS as a literal backtick identifier. A null
860+ // resolution means "all catalogs" — ExecuteShowColumnsAsync(null, ...) handles that by
861+ // iterating every catalog itself.
862+ var catalogs = await ResolveCatalogsAsync ( catalogPattern , cancellationToken ) . ConfigureAwait ( false ) ;
863+
864+ List < RecordBatch > batches = new List < RecordBatch > ( ) ;
807865 try
808866 {
809- batches = await ExecuteShowColumnsAsync ( catalogPattern , schemaPattern , tablePattern , columnPattern , cancellationToken ) . ConfigureAwait ( false ) ;
867+ if ( catalogs == null )
868+ {
869+ batches . AddRange ( await ExecuteShowColumnsAsync ( null , schemaPattern , tablePattern , columnPattern , cancellationToken ) . ConfigureAwait ( false ) ) ;
870+ }
871+ else
872+ {
873+ foreach ( var catalog in catalogs )
874+ batches . AddRange ( await ExecuteShowColumnsAsync ( catalog , schemaPattern , tablePattern , columnPattern , cancellationToken ) . ConfigureAwait ( false ) ) ;
875+ }
810876 }
811877 catch ( DatabricksException ex ) when ( ex . IsObjectNotFoundException ( ) )
812878 {
0 commit comments