@@ -70,8 +70,8 @@ protected virtual void SetStatementProperties(TExecuteStatementReq statement)
7070 }
7171
7272 /// <summary>
73- /// Gets the schema from metadata response. Base implementation uses the standard schema parser .
74- /// Subclasses can override to support alternative schema parsing strategies .
73+ /// Gets the schema from metadata response. Base implementation uses traditional Thrift schema.
74+ /// Subclasses can override to support Arrow schema parsing.
7575 /// </summary>
7676 /// <param name="metadata">The metadata response containing schema information</param>
7777 /// <returns>The Arrow schema</returns>
@@ -442,16 +442,15 @@ private async Task<QueryResult> ExecuteMetadataCommandQuery(CancellationToken ca
442442 /// since the backend treats these as exact match queries rather than pattern matches.
443443 protected virtual async Task < QueryResult > GetCrossReferenceAsForeignTableAsync ( CancellationToken cancellationToken = default )
444444 {
445- IResponse response = await Connection . GetCrossReferenceAsync (
446- null ,
447- null ,
448- null ,
449- CatalogName ,
450- SchemaName ,
451- TableName ,
445+ var response = await Connection . GetCrossReferenceAsync (
446+ null , null , null ,
447+ CatalogName , SchemaName , TableName ,
448+ cancellationToken ) ;
449+ return await GetMetadataAsRecordBatch (
450+ response ,
451+ Connection . ConvertToForeignKeyRecords ,
452+ Metadata . MetadataSchemaBuilder . BuildFlatForeignKeysSchema ,
452453 cancellationToken ) ;
453-
454- return await GetQueryResult ( response , cancellationToken ) ;
455454 }
456455
457456 /// <summary>
@@ -461,16 +460,15 @@ protected virtual async Task<QueryResult> GetCrossReferenceAsForeignTableAsync(C
461460 /// </summary>
462461 protected virtual async Task < QueryResult > GetCrossReferenceAsync ( CancellationToken cancellationToken = default )
463462 {
464- IResponse response = await Connection . GetCrossReferenceAsync (
465- CatalogName ,
466- SchemaName ,
467- TableName ,
468- ForeignCatalogName ,
469- ForeignSchemaName ,
470- ForeignTableName ,
463+ var response = await Connection . GetCrossReferenceAsync (
464+ CatalogName , SchemaName , TableName ,
465+ ForeignCatalogName , ForeignSchemaName , ForeignTableName ,
466+ cancellationToken ) ;
467+ return await GetMetadataAsRecordBatch (
468+ response ,
469+ Connection . ConvertToForeignKeyRecords ,
470+ Metadata . MetadataSchemaBuilder . BuildFlatForeignKeysSchema ,
471471 cancellationToken ) ;
472-
473- return await GetQueryResult ( response , cancellationToken ) ;
474472 }
475473
476474 /// <summary>
@@ -480,43 +478,52 @@ protected virtual async Task<QueryResult> GetCrossReferenceAsync(CancellationTok
480478 /// </summary>
481479 protected virtual async Task < QueryResult > GetPrimaryKeysAsync ( CancellationToken cancellationToken = default )
482480 {
483- IResponse response = await Connection . GetPrimaryKeysAsync (
484- CatalogName ,
485- SchemaName ,
486- TableName ,
481+ var response = await Connection . GetPrimaryKeysAsync (
482+ CatalogName , SchemaName , TableName , cancellationToken ) ;
483+ return await GetMetadataAsRecordBatch (
484+ response ,
485+ Connection . ConvertToPrimaryKeyRecords ,
486+ Metadata . MetadataSchemaBuilder . BuildFlatPrimaryKeysSchema ,
487487 cancellationToken ) ;
488-
489- return await GetQueryResult ( response , cancellationToken ) ;
490488 }
491489
492490 protected virtual async Task < QueryResult > GetCatalogsAsync ( CancellationToken cancellationToken = default )
493491 {
494- IResponse response = await Connection . GetCatalogsAsync ( cancellationToken ) ;
495-
496- return await GetQueryResult ( response , cancellationToken ) ;
492+ var response = await Connection . GetCatalogsAsync ( cancellationToken ) ;
493+ return await GetMetadataAsRecordBatch (
494+ response ,
495+ Connection . ConvertToCatalogRecords ,
496+ Metadata . MetadataSchemaBuilder . BuildFlatCatalogsSchema ,
497+ cancellationToken ) ;
497498 }
498499
499500 protected virtual async Task < QueryResult > GetSchemasAsync ( CancellationToken cancellationToken = default )
500501 {
501- IResponse response = await Connection . GetSchemasAsync (
502+ var response = await Connection . GetSchemasAsync (
502503 EscapePatternWildcardsInName ( CatalogName ) ,
503504 EscapePatternWildcardsInName ( SchemaName ) ,
504505 cancellationToken ) ;
505-
506- return await GetQueryResult ( response , cancellationToken ) ;
506+ return await GetMetadataAsRecordBatch (
507+ response ,
508+ Connection . ConvertToSchemaRecords ,
509+ Metadata . MetadataSchemaBuilder . BuildFlatSchemasSchema ,
510+ cancellationToken ) ;
507511 }
508512
509513 protected virtual async Task < QueryResult > GetTablesAsync ( CancellationToken cancellationToken = default )
510514 {
511- List < string > ? tableTypesList = this . TableTypes ? . Split ( ',' ) . ToList ( ) ;
512- IResponse response = await Connection . GetTablesAsync (
515+ var tableTypesList = this . TableTypes ? . Split ( ',' ) . ToList ( ) ;
516+ var response = await Connection . GetTablesAsync (
513517 EscapePatternWildcardsInName ( CatalogName ) ,
514518 EscapePatternWildcardsInName ( SchemaName ) ,
515519 EscapePatternWildcardsInName ( TableName ) ,
516520 tableTypesList ,
517521 cancellationToken ) ;
518-
519- return await GetQueryResult ( response , cancellationToken ) ;
522+ return await GetMetadataAsRecordBatch (
523+ response ,
524+ Connection . ConvertToTableRecords ,
525+ Metadata . MetadataSchemaBuilder . BuildFlatTablesSchema ,
526+ cancellationToken ) ;
520527 }
521528
522529 protected virtual async Task < QueryResult > GetColumnsAsync ( CancellationToken cancellationToken = default )
@@ -557,17 +564,45 @@ private async Task<Schema> GetResultSetSchemaAsync(TOperationHandle operationHan
557564 return GetSchemaFromMetadata ( response ) ;
558565 }
559566
560- private async Task < QueryResult > GetQueryResult ( IResponse response , CancellationToken cancellationToken )
567+ /// <summary>
568+ /// Generic helper for metadata methods that convert TRowSet to RecordBatch using metadata records.
569+ /// Handles both direct results and poll-fetch paths.
570+ /// Virtual to allow protocol-specific customization (e.g., SEA).
571+ /// </summary>
572+ /// <typeparam name="TRecord">The metadata record type (e.g., CatalogMetadataRecord)</typeparam>
573+ /// <param name="response">The Thrift response from the metadata RPC call</param>
574+ /// <param name="converter">Function to convert TRowSet to list of metadata records</param>
575+ /// <param name="builder">Function to build RecordBatch from metadata records</param>
576+ /// <param name="cancellationToken">Cancellation token</param>
577+ /// <returns>QueryResult containing the metadata as RecordBatch</returns>
578+ protected virtual async Task < QueryResult > GetMetadataAsRecordBatch < TRecord > (
579+ IResponse response ,
580+ Func < TRowSet , List < TRecord > > converter ,
581+ Func < IEnumerable < TRecord > , RecordBatch > builder ,
582+ CancellationToken cancellationToken )
561583 {
562- if ( Connection . TryGetDirectResults ( response . DirectResults , out QueryResult ? result ) )
584+ TRowSet ? rowSet ;
585+
586+ // Try direct results first (fast path)
587+ if ( Connection . TryGetDirectResults ( response . DirectResults , out TGetResultSetMetadataResp ? metadata , out rowSet ) )
563588 {
564- return result ! ;
589+ var records = converter ( rowSet ! ) ;
590+ var batch = builder ( records ) ;
591+ // Extract column arrays from RecordBatch for HiveInfoArrowStream
592+ var arrays = Enumerable . Range ( 0 , batch . ColumnCount ) . Select ( i => batch . Column ( i ) ) . ToList ( ) ;
593+ return new QueryResult ( batch . Length , new HiveServer2Connection . HiveInfoArrowStream ( batch . Schema , arrays ) ) ;
565594 }
566595
596+ // Poll and fetch path (slow path)
567597 await HiveServer2Connection . PollForResponseAsync ( response . OperationHandle ! , Connection . Client , PollTimeMilliseconds , cancellationToken ) ;
568- Schema schema = await GetResultSetSchemaAsync ( response . OperationHandle ! , Connection . Client , cancellationToken ) ;
569-
570- return new QueryResult ( - 1 , Connection . NewReader ( this , schema , response ) ) ;
598+ metadata = await HiveServer2Connection . GetResultSetMetadataAsync ( response . OperationHandle ! , Connection . Client , cancellationToken ) ;
599+ rowSet = await Connection . FetchResultsAsync ( response . OperationHandle ! , BatchSize , cancellationToken ) ;
600+
601+ var recordList = converter ( rowSet ) ;
602+ var recordBatch = builder ( recordList ) ;
603+ // Extract column arrays from RecordBatch for HiveInfoArrowStream
604+ var columnArrays = Enumerable . Range ( 0 , recordBatch . ColumnCount ) . Select ( i => recordBatch . Column ( i ) ) . ToList ( ) ;
605+ return new QueryResult ( recordBatch . Length , new HiveServer2Connection . HiveInfoArrowStream ( recordBatch . Schema , columnArrays ) ) ;
571606 }
572607
573608 protected internal QueryResult EnhanceGetColumnsResult ( Schema originalSchema , IReadOnlyList < IArrowArray > originalData ,
@@ -624,7 +659,9 @@ protected internal QueryResult EnhanceGetColumnsResult(Schema originalSchema, IR
624659 customData : null
625660 ) ;
626661
627- // Extract values with fallback to protocol-provided values
662+ // Extract values with fallback to Thrift-provided values
663+ // This preserves the existing behavior where parsed values take precedence,
664+ // but Thrift values are used if parsing fails or returns null
628665 string baseTypeName = record . BaseTypeName ?? typeName ?? string . Empty ;
629666 int finalColumnSize = record . XdbcColumnSize ?? columnSize ;
630667 int finalDecimalDigits = record . XdbcDecimalDigits ?? decimalDigits ;
0 commit comments