Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions csharp/src/StatementExecution/StatementExecutionStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,8 @@ private Schema GetSchemaFromManifest(ResultManifest manifest)
var fields = new List<Field>();
foreach (var column in manifest.Schema.Columns)
{
var arrowType = MapDatabricksTypeToArrowType(column.TypeName);
var typeName = column.TypeName ?? string.Empty;
var arrowType = MapDatabricksTypeToArrowType(typeName);
// Embed the SQL type name as Arrow field metadata so that consumers
// (e.g. the PowerBI connector's AdjustNativeTypes) can read it via
// the "Spark:DataType:SqlName" key — the same metadata the Databricks
Expand All @@ -494,7 +495,7 @@ private Schema GetSchemaFromManifest(ResultManifest manifest)
// it here for now. Add it if a consumer requires it (PECO-2950).
var metadata = new Dictionary<string, string>
{
["Spark:DataType:SqlName"] = ColumnMetadataHelper.GetSparkSqlName(column.TypeName ?? string.Empty)
["Spark:DataType:SqlName"] = ColumnMetadataHelper.GetSparkSqlName(typeName)
};
fields.Add(new Field(column.Name, arrowType, true, metadata));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,87 @@ public async Task ExecuteQuery_EmptyTable_SqlNameAliasesNormalized()
Assert.Equal("SMALLINT", fields[2].Metadata["Spark:DataType:SqlName"]);
}

[Fact]
public async Task ExecuteQuery_EmptyTable_ComplexAndDecimalTypesAreMappedCorrectly()
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why need add these, we already have csharp/test/ColumnMetadataHelperTests.cs which test all type mapping?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — removed the redundant Spark:DataType:SqlName assertion for DECIMAL (already covered by ColumnMetadataHelperTests).

Kept the two tests because they cover things ColumnMetadataHelperTests doesn't:

  • ComplexAndDecimalTypesAreMappedCorrectly: exercises MapDatabricksTypeToArrowType end-to-end (e.g. DECIMAL(10,2)Decimal128Type, ARRAYStringType) — the existing ArrowTypesAreMappedCorrectly test doesn't include these types
  • NullTypeName_TreatedAsUnknownType: verifies null TypeName doesn't throw — not covered anywhere else

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, removed it. The production change is just extracting a typeName local — the complex/decimal type mapping is out of scope.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved it — added [InlineData("", "")] to the existing GetBaseTypeName and GetSparkSqlName theories in ColumnMetadataHelperTests and removed the statement-level test entirely.

{
// Arrange: DECIMAL(10,2), ARRAY, MAP, STRUCT — all must survive the schema path
var manifest = BuildManifest(
("price", "DECIMAL(10,2)"),
("tags", "ARRAY<STRING>"),
("props", "MAP<STRING,INT>"),
("addr", "STRUCT<city:STRING,zip:INT>"));

var mockClient = new Mock<IStatementExecutionClient>();
mockClient
.Setup(c => c.ExecuteStatementAsync(
It.IsAny<ExecuteStatementRequest>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new ExecuteStatementResponse
{
StatementId = StatementId,
Status = new StatementStatus { State = "SUCCEEDED" },
Manifest = manifest,
Result = new ResultData { Attachment = null },
});

using var stmt = CreateStatement(mockClient.Object);
stmt.SqlQuery = "SELECT price, tags, props, addr FROM complex_table WHERE 0=1";

var queryResult = await stmt.ExecuteQueryAsync(CancellationToken.None);
var fields = queryResult.Stream!.Schema.FieldsList;

Assert.Equal(4, fields.Count);
Assert.IsType<Decimal128Type>(fields[0].DataType);
Assert.Equal("DECIMAL", fields[0].Metadata["Spark:DataType:SqlName"]);
// ARRAY, MAP, STRUCT are represented as STRING in Arrow for now
Assert.IsType<StringType>(fields[1].DataType);
Assert.IsType<StringType>(fields[2].DataType);
Assert.IsType<StringType>(fields[3].DataType);
}

[Fact]
public async Task ExecuteQuery_NullTypeName_TreatedAsUnknownType()
{
// Arrange: a column with a null TypeName must not throw — it falls back to StringType
var manifest = new ResultManifest
{
Format = "ARROW_STREAM",
Schema = new ResultSchema
{
Columns = new List<ColumnInfo>
{
new ColumnInfo { Name = "mystery", TypeName = null },
}
},
TotalRowCount = 0,
Chunks = new List<ResultChunk>(),
};

var mockClient = new Mock<IStatementExecutionClient>();
mockClient
.Setup(c => c.ExecuteStatementAsync(
It.IsAny<ExecuteStatementRequest>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new ExecuteStatementResponse
{
StatementId = StatementId,
Status = new StatementStatus { State = "SUCCEEDED" },
Manifest = manifest,
Result = new ResultData { Attachment = null },
});

using var stmt = CreateStatement(mockClient.Object);
stmt.SqlQuery = "SELECT mystery FROM weird_table WHERE 0=1";

var queryResult = await stmt.ExecuteQueryAsync(CancellationToken.None);
var fields = queryResult.Stream!.Schema.FieldsList;

Assert.Single(fields);
Assert.Equal("mystery", fields[0].Name);
Assert.IsType<StringType>(fields[0].DataType);
Assert.Equal(string.Empty, fields[0].Metadata["Spark:DataType:SqlName"]);
}

[Fact]
public async Task ExecuteQuery_NullManifest_ReturnsEmptySchema()
{
Expand Down
Loading