This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-java.git
The following commit(s) were added to refs/heads/main by this push:
new 8314c191 GH-737: [FlightSQL] Allow returning column remarks in
FlightSQL's CommandGetTables (#727)
8314c191 is described below
commit 8314c1912ed501171a2085feb1909b07892a6a25
Author: Mateusz Rzeszutek <[email protected]>
AuthorDate: Sat May 3 11:01:23 2025 +0200
GH-737: [FlightSQL] Allow returning column remarks in FlightSQL's
CommandGetTables (#727)
Resolves #737
## What's Changed
This is an implementation of
https://github.com/apache/arrow/pull/46110 for Java
---
arrow-format/FlightSql.proto | 4 ++
.../tests/FlightSqlScenarioProducer.java | 2 +
.../arrow/driver/jdbc/ArrowDatabaseMetadata.java | 6 +++
.../arrow/driver/jdbc/utils/ConvertUtils.java | 4 ++
.../driver/jdbc/ArrowDatabaseMetadataTest.java | 53 +++++++++++++++++-----
.../arrow/driver/jdbc/utils/ConvertUtilsTest.java | 2 +
.../arrow/flight/sql/FlightSqlColumnMetadata.java | 21 +++++++++
7 files changed, 80 insertions(+), 12 deletions(-)
diff --git a/arrow-format/FlightSql.proto b/arrow-format/FlightSql.proto
index 3568d851..566230c2 100644
--- a/arrow-format/FlightSql.proto
+++ b/arrow-format/FlightSql.proto
@@ -1212,6 +1212,7 @@ message CommandGetDbSchemas {
* - ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE - "1" indicates if the column is
case-sensitive, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_READ_ONLY - "1" indicates if the column is read
only, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_SEARCHABLE - "1" indicates if the column is
searchable via WHERE clause, "0" otherwise.
+ * - ARROW:FLIGHT:SQL:REMARKS - A comment describing the column.
* The returned data should be ordered by catalog_name, db_schema_name,
table_name, then table_type, followed by table_schema if requested.
*/
message CommandGetTables {
@@ -1678,6 +1679,7 @@ message ActionEndSavepointRequest {
* - ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE - "1" indicates if the column is
case-sensitive, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_READ_ONLY - "1" indicates if the column is
read only, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_SEARCHABLE - "1" indicates if the column is
searchable via WHERE clause, "0" otherwise.
+ * - ARROW:FLIGHT:SQL:REMARKS - A comment describing the column.
* - GetFlightInfo: execute the query.
*/
message CommandStatementQuery {
@@ -1703,6 +1705,7 @@ message CommandStatementQuery {
* - ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE - "1" indicates if the column is
case-sensitive, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_READ_ONLY - "1" indicates if the column is
read only, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_SEARCHABLE - "1" indicates if the column is
searchable via WHERE clause, "0" otherwise.
+ * - ARROW:FLIGHT:SQL:REMARKS - A comment describing the column.
* - GetFlightInfo: execute the query.
* - DoPut: execute the query.
*/
@@ -1739,6 +1742,7 @@ message TicketStatementQuery {
* - ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE - "1" indicates if the column is
case-sensitive, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_READ_ONLY - "1" indicates if the column is
read only, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_SEARCHABLE - "1" indicates if the column is
searchable via WHERE clause, "0" otherwise.
+ * - ARROW:FLIGHT:SQL:REMARKS - A comment describing the column.
*
* If the schema is retrieved after parameter values have been bound with
DoPut, then the server should account
* for the parameters when determining the schema.
diff --git
a/flight/flight-integration-tests/src/main/java/org/apache/arrow/flight/integration/tests/FlightSqlScenarioProducer.java
b/flight/flight-integration-tests/src/main/java/org/apache/arrow/flight/integration/tests/FlightSqlScenarioProducer.java
index be746b57..e400c031 100644
---
a/flight/flight-integration-tests/src/main/java/org/apache/arrow/flight/integration/tests/FlightSqlScenarioProducer.java
+++
b/flight/flight-integration-tests/src/main/java/org/apache/arrow/flight/integration/tests/FlightSqlScenarioProducer.java
@@ -98,6 +98,7 @@ public class FlightSqlScenarioProducer implements
FlightSqlProducer {
.isSearchable(true)
.catalogName("catalog_test")
.precision(100)
+ .remarks("test column")
.build()
.getMetadataMap()),
null)));
@@ -126,6 +127,7 @@ public class FlightSqlScenarioProducer implements
FlightSqlProducer {
.isSearchable(true)
.catalogName("catalog_test")
.precision(100)
+ .remarks("test column")
.build()
.getMetadataMap()),
null)));
diff --git
a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
index 3f072d07..7185ddfe 100644
---
a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
+++
b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
@@ -1066,6 +1066,7 @@ public class ArrowDatabaseMetadata extends
AvaticaDatabaseMetaData {
(VarCharVector) currentRoot.getVector("IS_AUTOINCREMENT");
final VarCharVector isGeneratedColumnVector =
(VarCharVector) currentRoot.getVector("IS_GENERATEDCOLUMN");
+ final VarCharVector remarksVector = (VarCharVector)
currentRoot.getVector("REMARKS");
for (int i = 0; i < tableColumnsSize; i++, ordinalIndex++) {
final Field field = tableColumns.get(i);
@@ -1139,6 +1140,11 @@ public class ArrowDatabaseMetadata extends
AvaticaDatabaseMetaData {
isAutoincrementVector.setSafe(insertIndex, EMPTY_BYTE_ARRAY);
}
+ String remarks = columnMetadata.getRemarks();
+ if (remarks != null) {
+ remarksVector.setSafe(insertIndex, remarks.getBytes(CHARSET));
+ }
+
// Fields also don't hold information about IS_AUTOINCREMENT and
IS_GENERATEDCOLUMN,
// so we're setting an empty string (as bytes), which means it couldn't
be determined.
isGeneratedColumnVector.setSafe(insertIndex, EMPTY_BYTE_ARRAY);
diff --git
a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java
b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java
index 17b0f42d..5dd4c69c 100644
---
a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java
+++
b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java
@@ -136,6 +136,10 @@ public final class ConvertUtils {
if (searchable != null) {
builder.setSearchable(searchable);
}
+ final String remarks = columnMetadata.getRemarks();
+ if (remarks != null) {
+ builder.setLabel(remarks);
+ }
}
/**
diff --git
a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
index 70d3bcbd..81579cc3 100644
---
a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
+++
b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
@@ -26,7 +26,6 @@ import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static java.util.stream.IntStream.range;
import static
org.apache.arrow.driver.jdbc.utils.MockFlightSqlProducer.serializeSchema;
-import static
org.apache.arrow.flight.sql.impl.FlightSql.CommandGetCrossReference;
import static
org.apache.arrow.flight.sql.impl.FlightSql.SqlSupportsConvert.SQL_CONVERT_BIGINT_VALUE;
import static
org.apache.arrow.flight.sql.impl.FlightSql.SqlSupportsConvert.SQL_CONVERT_BIT_VALUE;
import static
org.apache.arrow.flight.sql.impl.FlightSql.SqlSupportsConvert.SQL_CONVERT_INTEGER_VALUE;
@@ -55,9 +54,11 @@ import
org.apache.arrow.driver.jdbc.utils.MockFlightSqlProducer;
import org.apache.arrow.driver.jdbc.utils.ResultSetTestUtils;
import org.apache.arrow.driver.jdbc.utils.ThrowableAssertionUtils;
import org.apache.arrow.flight.FlightProducer.ServerStreamListener;
+import org.apache.arrow.flight.sql.FlightSqlColumnMetadata;
import org.apache.arrow.flight.sql.FlightSqlProducer.Schemas;
import org.apache.arrow.flight.sql.impl.FlightSql;
import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetCatalogs;
+import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetCrossReference;
import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetDbSchemas;
import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetExportedKeys;
import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetImportedKeys;
@@ -79,6 +80,7 @@ import org.apache.arrow.vector.types.TimeUnit;
import org.apache.arrow.vector.types.Types;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.FieldType;
import org.apache.arrow.vector.types.pojo.Schema;
import org.apache.arrow.vector.util.Text;
import org.junit.jupiter.api.AfterAll;
@@ -322,7 +324,7 @@ public class ArrowDatabaseMetadataTest {
expectedGetColumnsDecimalDigits.get(i % 3),
expectedGetColumnsRadix.get(i % 3),
!Objects.equals(expectedGetColumnsIsNullable.get(i % 3),
"NO") ? 1 : 0,
- null,
+ format("column description #%d", (i % 3) + 1),
null,
null,
null,
@@ -419,17 +421,44 @@ public class ArrowDatabaseMetadataTest {
try (final BufferAllocator allocator = new RootAllocator();
final VectorSchemaRoot root =
VectorSchemaRoot.create(Schemas.GET_TABLES_SCHEMA,
allocator)) {
+ final Field field1 =
+ new Field(
+ "column_1",
+ new FieldType(
+ true,
+ ArrowType.Decimal.createDecimal(5, 2, 128),
+ null,
+ new FlightSqlColumnMetadata.Builder()
+ .remarks("column description #1")
+ .build()
+ .getMetadataMap()),
+ null);
+ final Field field2 =
+ new Field(
+ "column_2",
+ new FieldType(
+ true,
+ new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC"),
+ null,
+ new FlightSqlColumnMetadata.Builder()
+ .remarks("column description #2")
+ .build()
+ .getMetadataMap()),
+ null);
+ final Field field3 =
+ new Field(
+ "column_3",
+ new FieldType(
+ false,
+ Types.MinorType.INT.getType(),
+ null,
+ new FlightSqlColumnMetadata.Builder()
+ .remarks("column description #3")
+ .build()
+ .getMetadataMap()),
+ null);
final byte[] filledTableSchemaBytes =
- copyFrom(
- serializeSchema(
- new Schema(
- Arrays.asList(
- Field.nullable(
- "column_1",
ArrowType.Decimal.createDecimal(5, 2, 128)),
- Field.nullable(
- "column_2",
- new
ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC")),
- Field.notNullable("column_3",
Types.MinorType.INT.getType())))))
+ copyFrom(serializeSchema(new Schema(Arrays.asList(field1,
field2, field3))))
.toByteArray();
final VarCharVector catalogName = (VarCharVector)
root.getVector("catalog_name");
final VarCharVector schemaName = (VarCharVector)
root.getVector("db_schema_name");
diff --git
a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java
b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java
index f6f549b5..b6fdc996 100644
---
a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java
+++
b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java
@@ -46,6 +46,7 @@ public class ConvertUtilsTest {
.isSearchable(true)
.precision(20)
.scale(10)
+ .remarks("test column")
.build();
ConvertUtils.setOnColumnMetaDataBuilder(builder,
expectedColumnMetaData.getMetadataMap());
assertBuilder(builder, expectedColumnMetaData);
@@ -119,5 +120,6 @@ public class ConvertUtilsTest {
assertThat(flightSqlColumnMetaData.isReadOnly(),
equalTo(builder.getReadOnly()));
assertThat(precision == null ? 0 : precision,
equalTo(builder.getPrecision()));
assertThat(scale == null ? 0 : scale, equalTo(builder.getScale()));
+ assertThat(flightSqlColumnMetaData.getRemarks(),
equalTo(builder.getLabel()));
}
}
diff --git
a/flight/flight-sql/src/main/java/org/apache/arrow/flight/sql/FlightSqlColumnMetadata.java
b/flight/flight-sql/src/main/java/org/apache/arrow/flight/sql/FlightSqlColumnMetadata.java
index 1bcc55a6..3a969e10 100644
---
a/flight/flight-sql/src/main/java/org/apache/arrow/flight/sql/FlightSqlColumnMetadata.java
+++
b/flight/flight-sql/src/main/java/org/apache/arrow/flight/sql/FlightSqlColumnMetadata.java
@@ -53,6 +53,7 @@ public class FlightSqlColumnMetadata {
private static final String IS_CASE_SENSITIVE =
"ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE";
private static final String IS_READ_ONLY = "ARROW:FLIGHT:SQL:IS_READ_ONLY";
private static final String IS_SEARCHABLE = "ARROW:FLIGHT:SQL:IS_SEARCHABLE";
+ private static final String REMARKS = "ARROW:FLIGHT:SQL:REMARKS";
private static final String BOOLEAN_TRUE_STR = "1";
private static final String BOOLEAN_FALSE_STR = "0";
@@ -193,6 +194,15 @@ public class FlightSqlColumnMetadata {
return stringToBoolean(value);
}
+ /**
+ * Returns the comment describing the column.
+ *
+ * @return The comment describing the column.
+ */
+ public String getRemarks() {
+ return metadataMap.get(REMARKS);
+ }
+
/** Builder of FlightSqlColumnMetadata, used on FlightSqlProducer
implementations. */
public static class Builder {
private final Map<String, String> metadataMap;
@@ -312,6 +322,17 @@ public class FlightSqlColumnMetadata {
return this;
}
+ /**
+ * Sets the comment describing the column.
+ *
+ * @param remarks The comment describing the column.
+ * @return This builder.
+ */
+ public Builder remarks(String remarks) {
+ metadataMap.put(REMARKS, remarks);
+ return this;
+ }
+
/**
* Builds a new instance of FlightSqlColumnMetadata.
*