This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch branch-lance-namepspace-dev
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-lance-namepspace-dev by
this push:
new d8a4ded1be [#8962] improvement(lance): supports more dataTypes for the
Lance rest server (#8963)
d8a4ded1be is described below
commit d8a4ded1be927f24fd4ea490a6db36024731d83b
Author: mchades <[email protected]>
AuthorDate: Fri Oct 31 10:13:25 2025 +0800
[#8962] improvement(lance): supports more dataTypes for the Lance rest
server (#8963)
### What changes were proposed in this pull request?
supports more dataTypes for the Lance rest server
### Why are the changes needed?
Fix: #8962
### Does this PR introduce _any_ user-facing change?
yes, more dataTypes supported
### How was this patch tested?
tests added
---
.../catalog-generic-lakehouse/build.gradle.kts | 1 +
.../lakehouse/lance/LanceCatalogOperations.java | 1 +
lance/lance-common/build.gradle.kts | 4 +
.../lance/common/ops/LanceNamespaceOperations.java | 3 +-
.../common/ops/arrow/ArrowRecordBatchList.java | 40 ----
.../gravitino/GravitinoLanceNamespaceWrapper.java | 224 ++++-----------------
.../ops/gravitino}/LanceDataTypeConverter.java | 136 ++++++++++++-
.../gravitino/lance/common/TestArrowIPC.java | 83 --------
.../TestGravitinoLanceNamespaceWrapper.java | 76 +++++++
.../ops/gravitino}/TestLanceDataTypeConverter.java | 156 +++++++++++++-
.../service/rest/LanceNamespaceOperations.java | 2 +-
.../lance/service/rest/LanceTableOperations.java | 13 +-
12 files changed, 409 insertions(+), 330 deletions(-)
diff --git a/catalogs/catalog-generic-lakehouse/build.gradle.kts
b/catalogs/catalog-generic-lakehouse/build.gradle.kts
index 704dbda7e3..7dd0a4bc16 100644
--- a/catalogs/catalog-generic-lakehouse/build.gradle.kts
+++ b/catalogs/catalog-generic-lakehouse/build.gradle.kts
@@ -35,6 +35,7 @@ dependencies {
implementation(project(":core")) {
exclude("*")
}
+ implementation(project(":lance:lance-common"))
implementation(libs.bundles.log4j)
implementation(libs.cglib)
diff --git
a/catalogs/catalog-generic-lakehouse/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceCatalogOperations.java
b/catalogs/catalog-generic-lakehouse/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceCatalogOperations.java
index e27f8032ab..0ed83457a4 100644
---
a/catalogs/catalog-generic-lakehouse/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceCatalogOperations.java
+++
b/catalogs/catalog-generic-lakehouse/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceCatalogOperations.java
@@ -57,6 +57,7 @@ import org.apache.gravitino.exceptions.NoSuchEntityException;
import org.apache.gravitino.exceptions.NoSuchSchemaException;
import org.apache.gravitino.exceptions.NoSuchTableException;
import org.apache.gravitino.exceptions.TableAlreadyExistsException;
+import org.apache.gravitino.lance.common.ops.gravitino.LanceDataTypeConverter;
import org.apache.gravitino.meta.AuditInfo;
import org.apache.gravitino.meta.TableEntity;
import org.apache.gravitino.rel.Column;
diff --git a/lance/lance-common/build.gradle.kts
b/lance/lance-common/build.gradle.kts
index 43cf5f42b8..6e18f3981f 100644
--- a/lance/lance-common/build.gradle.kts
+++ b/lance/lance-common/build.gradle.kts
@@ -26,6 +26,9 @@ plugins {
dependencies {
implementation(project(":clients:client-java-runtime", configuration =
"shadow"))
+ implementation(project(":common")) {
+ exclude("*")
+ }
implementation(project(":core")) {
exclude("*")
}
@@ -37,5 +40,6 @@ dependencies {
testImplementation(project(":server-common"))
testImplementation(libs.junit.jupiter.api)
+ testImplementation(libs.junit.jupiter.params)
testRuntimeOnly(libs.junit.jupiter.engine)
}
diff --git
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/LanceNamespaceOperations.java
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/LanceNamespaceOperations.java
index 49141665a5..36d55c03e9 100644
---
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/LanceNamespaceOperations.java
+++
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/LanceNamespaceOperations.java
@@ -49,5 +49,6 @@ public interface LanceNamespaceOperations {
void namespaceExists(String namespaceId, String delimiter) throws
LanceNamespaceException;
- ListTablesResponse listTables(String id, String delimiter, String pageToken,
Integer limit);
+ ListTablesResponse listTables(
+ String namespaceId, String delimiter, String pageToken, Integer limit);
}
diff --git
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/arrow/ArrowRecordBatchList.java
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/arrow/ArrowRecordBatchList.java
deleted file mode 100644
index b0c6a089d5..0000000000
---
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/arrow/ArrowRecordBatchList.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.gravitino.lance.common.ops.arrow;
-
-import java.util.List;
-import org.apache.arrow.vector.VectorSchemaRoot;
-import org.apache.arrow.vector.types.pojo.Schema;
-
-public class ArrowRecordBatchList {
- private final Schema schema;
-
- @SuppressWarnings("unused")
- private final List<VectorSchemaRoot> recordBatches;
-
- public Schema getSchema() {
- return schema;
- }
-
- public ArrowRecordBatchList(Schema schema, List<VectorSchemaRoot>
recordBatches) {
- this.schema = schema;
- this.recordBatches = recordBatches;
- }
-}
diff --git
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceNamespaceWrapper.java
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceNamespaceWrapper.java
index fe6404a424..d3ddbb0ede 100644
---
a/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceNamespaceWrapper.java
+++
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceNamespaceWrapper.java
@@ -20,8 +20,10 @@ package org.apache.gravitino.lance.common.ops.gravitino;
import static
org.apache.gravitino.lance.common.config.LanceConfig.METALAKE_NAME;
import static
org.apache.gravitino.lance.common.config.LanceConfig.NAMESPACE_BACKEND_URI;
+import static
org.apache.gravitino.lance.common.ops.gravitino.LanceDataTypeConverter.CONVERTER;
import static org.apache.gravitino.rel.Column.DEFAULT_VALUE_NOT_SET;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
@@ -37,12 +39,11 @@ import
com.lancedb.lance.namespace.model.DescribeNamespaceResponse;
import com.lancedb.lance.namespace.model.DescribeTableResponse;
import com.lancedb.lance.namespace.model.DropNamespaceRequest;
import com.lancedb.lance.namespace.model.DropNamespaceResponse;
-import com.lancedb.lance.namespace.model.JsonArrowDataType;
-import com.lancedb.lance.namespace.model.JsonArrowField;
import com.lancedb.lance.namespace.model.JsonArrowSchema;
import com.lancedb.lance.namespace.model.ListNamespacesResponse;
import com.lancedb.lance.namespace.model.ListTablesResponse;
import com.lancedb.lance.namespace.util.CommonUtil;
+import com.lancedb.lance.namespace.util.JsonArrowSchemaConverter;
import com.lancedb.lance.namespace.util.PageUtil;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
@@ -59,17 +60,8 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
-import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.ipc.ArrowStreamReader;
-import org.apache.arrow.vector.types.DateUnit;
-import org.apache.arrow.vector.types.FloatingPointPrecision;
-import org.apache.arrow.vector.types.TimeUnit;
-import org.apache.arrow.vector.types.pojo.ArrowType;
-import org.apache.arrow.vector.types.pojo.ArrowType.Bool;
-import org.apache.arrow.vector.types.pojo.ArrowType.FloatingPoint;
-import org.apache.arrow.vector.types.pojo.ArrowType.Int;
import org.apache.arrow.vector.types.pojo.Field;
-import org.apache.arrow.vector.types.pojo.FieldType;
import org.apache.commons.lang3.StringUtils;
import org.apache.gravitino.Catalog;
import org.apache.gravitino.CatalogChange;
@@ -84,18 +76,12 @@ import
org.apache.gravitino.exceptions.NoSuchSchemaException;
import org.apache.gravitino.exceptions.NonEmptyCatalogException;
import org.apache.gravitino.exceptions.NonEmptySchemaException;
import org.apache.gravitino.exceptions.SchemaAlreadyExistsException;
-import org.apache.gravitino.json.JsonUtils;
import org.apache.gravitino.lance.common.config.LanceConfig;
import org.apache.gravitino.lance.common.ops.LanceNamespaceOperations;
import org.apache.gravitino.lance.common.ops.LanceTableOperations;
import org.apache.gravitino.lance.common.ops.NamespaceWrapper;
-import org.apache.gravitino.lance.common.ops.arrow.ArrowRecordBatchList;
import org.apache.gravitino.rel.Column;
import org.apache.gravitino.rel.Table;
-import org.apache.gravitino.rel.types.Type;
-import org.apache.gravitino.rel.types.Types;
-import org.apache.gravitino.rel.types.Types.FixedType;
-import org.apache.gravitino.rel.types.Types.UnparsedType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -105,6 +91,11 @@ public class GravitinoLanceNamespaceWrapper extends
NamespaceWrapper
private static final Logger LOG =
LoggerFactory.getLogger(GravitinoLanceNamespaceWrapper.class);
private GravitinoClient client;
+ @VisibleForTesting
+ GravitinoLanceNamespaceWrapper() {
+ super(null);
+ }
+
public GravitinoLanceNamespaceWrapper(LanceConfig config) {
super(config);
}
@@ -498,8 +489,8 @@ public class GravitinoLanceNamespaceWrapper extends
NamespaceWrapper
@Override
public ListTablesResponse listTables(
- String id, String delimiter, String pageToken, Integer limit) {
- ObjectIdentifier nsId = ObjectIdentifier.of(id, Pattern.quote(delimiter));
+ String namespaceId, String delimiter, String pageToken, Integer limit) {
+ ObjectIdentifier nsId = ObjectIdentifier.of(namespaceId,
Pattern.quote(delimiter));
Preconditions.checkArgument(
nsId.levels() <= 2, "Expected at most 2-level namespace but got: %s",
nsId.levels());
String catalogName = nsId.levelAtListPos(0);
@@ -508,9 +499,9 @@ public class GravitinoLanceNamespaceWrapper extends
NamespaceWrapper
List<String> tables =
Arrays.stream(catalog.asTableCatalog().listTables(Namespace.of(schemaName)))
.map(ident -> Joiner.on(delimiter).join(catalogName, schemaName,
ident.name()))
+ .sorted()
.collect(Collectors.toList());
- Collections.sort(tables);
PageUtil.Page page = PageUtil.splitPage(tables, pageToken,
PageUtil.normalizePageSize(limit));
ListNamespacesResponse response = new ListNamespacesResponse();
response.setNamespaces(Sets.newHashSet(page.items()));
@@ -540,29 +531,6 @@ public class GravitinoLanceNamespaceWrapper extends
NamespaceWrapper
return response;
}
- private JsonArrowSchema toJsonArrowSchema(Column[] columns) {
- List<JsonArrowField> fields = new ArrayList<>();
- for (Column column : columns) {
- ArrowType arrowType = fromGravitinoType(column.dataType());
- FieldType fieldType = new FieldType(column.nullable(), arrowType, null,
null);
- Field field = new Field(column.name(), fieldType, null);
-
- JsonArrowDataType jsonArrowDataType = new JsonArrowDataType();
- // other filed needs to be set accordingly such as list, map, struct
- jsonArrowDataType.setType(arrowType.toString());
-
- JsonArrowField arrowField = new JsonArrowField();
- arrowField.setName(field.getName());
- arrowField.setType(jsonArrowDataType);
-
- fields.add(arrowField);
- }
-
- JsonArrowSchema jsonArrowSchema = new JsonArrowSchema();
- jsonArrowSchema.setFields(fields);
- return jsonArrowSchema;
- }
-
@Override
public CreateTableResponse createTable(
String tableId,
@@ -585,8 +553,8 @@ public class GravitinoLanceNamespaceWrapper extends
NamespaceWrapper
// Parser column information.
List<Column> columns = Lists.newArrayList();
if (arrowStreamBody != null) {
- ArrowRecordBatchList recordBatchList =
parseArrowIpcStream(arrowStreamBody);
- columns = extractColumns(recordBatchList);
+ org.apache.arrow.vector.types.pojo.Schema schema =
parseArrowIpcStream(arrowStreamBody);
+ columns = extractColumns(schema);
}
String catalogName = nsId.levelAtListPos(0);
@@ -646,157 +614,45 @@ public class GravitinoLanceNamespaceWrapper extends
NamespaceWrapper
return response;
}
- private ArrowRecordBatchList parseArrowIpcStream(byte[] stream) {
+ private JsonArrowSchema toJsonArrowSchema(Column[] columns) {
+ List<Field> fields =
+ Arrays.stream(columns)
+ .map(col -> CONVERTER.toArrowField(col.name(), col.dataType(),
col.nullable()))
+ .collect(Collectors.toList());
+
+ return JsonArrowSchemaConverter.convertToJsonArrowSchema(
+ new org.apache.arrow.vector.types.pojo.Schema(fields));
+ }
+
+ @VisibleForTesting
+ org.apache.arrow.vector.types.pojo.Schema parseArrowIpcStream(byte[] stream)
{
+ org.apache.arrow.vector.types.pojo.Schema schema;
+
try (BufferAllocator allocator = new RootAllocator();
ByteArrayInputStream bais = new ByteArrayInputStream(stream);
ArrowStreamReader reader = new ArrowStreamReader(bais, allocator)) {
-
- org.apache.arrow.vector.types.pojo.Schema schema =
reader.getVectorSchemaRoot().getSchema();
- List<VectorSchemaRoot> batches = new ArrayList<>();
-
- while (reader.loadNextBatch()) {
- VectorSchemaRoot root = reader.getVectorSchemaRoot();
- if (root.getRowCount() > 0) {
- batches.add(root);
- }
- }
- return new ArrowRecordBatchList(schema, batches);
+ schema = reader.getVectorSchemaRoot().getSchema();
} catch (Exception e) {
throw new RuntimeException("Failed to parse Arrow IPC stream", e);
}
+
+ Preconditions.checkArgument(schema != null, "No schema found in Arrow IPC
stream");
+ return schema;
}
- private List<Column> extractColumns(ArrowRecordBatchList recordBatchList) {
+ private List<Column>
extractColumns(org.apache.arrow.vector.types.pojo.Schema arrowSchema) {
List<Column> columns = new ArrayList<>();
- org.apache.arrow.vector.types.pojo.Schema arrowSchema =
recordBatchList.getSchema();
for (org.apache.arrow.vector.types.pojo.Field field :
arrowSchema.getFields()) {
- columns.add(toGravitinoColumn(field));
+ columns.add(
+ Column.of(
+ field.getName(),
+ CONVERTER.toGravitino(field),
+ null,
+ field.isNullable(),
+ false,
+ DEFAULT_VALUE_NOT_SET));
}
return columns;
}
-
- private Column toGravitinoColumn(Field field) {
- return Column.of(
- field.getName(),
- toGravitinoType(field),
- field.getMetadata().get("comment"),
- field.isNullable(),
- false,
- DEFAULT_VALUE_NOT_SET);
- }
-
- private ArrowType fromGravitinoType(Type type) {
- switch (type.name()) {
- case BOOLEAN:
- return Bool.INSTANCE;
- case BYTE:
- return new Int(8, true);
- case SHORT:
- return new Int(16, true);
- case INTEGER:
- return new Int(32, true);
- case LONG:
- return new Int(64, true);
- case FLOAT:
- return new FloatingPoint(FloatingPointPrecision.SINGLE);
- case DOUBLE:
- return new FloatingPoint(FloatingPointPrecision.DOUBLE);
- case DECIMAL:
- // Lance uses FIXED_SIZE_BINARY for decimal types
- return new ArrowType.FixedSizeBinary(16); // assuming 16 bytes for
decimal
- case DATE:
- return new ArrowType.Date(DateUnit.DAY);
- case TIME:
- return new ArrowType.Time(TimeUnit.MILLISECOND, 32);
- case TIMESTAMP:
- return new ArrowType.Timestamp(TimeUnit.MILLISECOND, null);
- case VARCHAR:
- case STRING:
- return new ArrowType.Utf8();
- case FIXED:
- FixedType fixedType = (FixedType) type;
- return new ArrowType.FixedSizeBinary(fixedType.length());
- case BINARY:
- return new ArrowType.Binary();
- case UNPARSED:
- String typeStr = ((UnparsedType) type).unparsedType().toString();
- try {
- Type t = JsonUtils.anyFieldMapper().readValue(typeStr, Type.class);
- if (t instanceof Types.ListType) {
- return ArrowType.List.INSTANCE;
- } else if (t instanceof Types.MapType) {
- return new ArrowType.Map(false);
- } else if (t instanceof Types.StructType) {
- return ArrowType.Struct.INSTANCE;
- } else {
- throw new UnsupportedOperationException(
- "Unsupported UnparsedType conversion: " + t.simpleString());
- }
- } catch (Exception e) {
- // FixedSizeListArray(integer, 3)
- if (typeStr.startsWith("FixedSizeListArray")) {
- int size =
- Integer.parseInt(
- typeStr.substring(typeStr.indexOf(',') + 1,
typeStr.indexOf(')')).trim());
- return new ArrowType.FixedSizeList(size);
- }
- throw new UnsupportedOperationException("Failed to parse
UnparsedType: " + typeStr, e);
- }
- default:
- throw new UnsupportedOperationException("Unsupported Gravitino type: "
+ type.name());
- }
- }
-
- private Type toGravitinoType(Field field) {
- FieldType parentType = field.getFieldType();
- ArrowType arrowType = parentType.getType();
- if (arrowType instanceof Bool) {
- return Types.BooleanType.get();
- } else if (arrowType instanceof Int) {
- Int intType = (Int) arrowType;
- switch (intType.getBitWidth()) {
- case 8 -> {
- return Types.ByteType.get();
- }
- case 16 -> {
- return Types.ShortType.get();
- }
- case 32 -> {
- return Types.IntegerType.get();
- }
- case 64 -> {
- return Types.LongType.get();
- }
- default -> throw new UnsupportedOperationException(
- "Unsupported Int bit width: " + intType.getBitWidth());
- }
- } else if (arrowType instanceof FloatingPoint) {
- FloatingPoint floatingPoint = (FloatingPoint) arrowType;
- switch (floatingPoint.getPrecision()) {
- case SINGLE:
- return Types.FloatType.get();
- case DOUBLE:
- return Types.DoubleType.get();
- default:
- throw new UnsupportedOperationException(
- "Unsupported FloatingPoint precision: " +
floatingPoint.getPrecision());
- }
- } else if (arrowType instanceof ArrowType.FixedSizeBinary) {
- ArrowType.FixedSizeBinary fixedSizeBinary = (ArrowType.FixedSizeBinary)
arrowType;
- return Types.FixedType.of(fixedSizeBinary.getByteWidth());
- } else if (arrowType instanceof ArrowType.Date) {
- return Types.DateType.get();
- } else if (arrowType instanceof ArrowType.Time) {
- return Types.TimeType.get();
- } else if (arrowType instanceof ArrowType.Timestamp) {
- return Types.TimestampType.withoutTimeZone();
- } else if (arrowType instanceof ArrowType.Utf8) {
- return Types.StringType.get();
- } else if (arrowType instanceof ArrowType.Binary) {
- return Types.BinaryType.get();
- } else {
- return Types.UnparsedType.of(arrowType.toString());
- }
- }
}
diff --git
a/catalogs/catalog-generic-lakehouse/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceDataTypeConverter.java
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/LanceDataTypeConverter.java
similarity index 61%
rename from
catalogs/catalog-generic-lakehouse/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceDataTypeConverter.java
rename to
lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/LanceDataTypeConverter.java
index 9cd5783de1..52d52d38fb 100644
---
a/catalogs/catalog-generic-lakehouse/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceDataTypeConverter.java
+++
b/lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/LanceDataTypeConverter.java
@@ -17,9 +17,9 @@
* under the License.
*/
-package org.apache.gravitino.catalog.lakehouse.lance;
+package org.apache.gravitino.lance.common.ops.gravitino;
-import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.Arrays;
@@ -37,14 +37,14 @@ import org.apache.arrow.vector.types.pojo.ArrowType.Int;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
import org.apache.gravitino.connector.DataTypeConverter;
-import org.apache.gravitino.json.JsonUtils;
import org.apache.gravitino.rel.types.Type;
import org.apache.gravitino.rel.types.Types;
import org.apache.gravitino.rel.types.Types.FixedType;
-public class LanceDataTypeConverter implements DataTypeConverter<ArrowType,
ArrowType> {
+public class LanceDataTypeConverter implements DataTypeConverter<ArrowType,
Field> {
public static final LanceDataTypeConverter CONVERTER = new
LanceDataTypeConverter();
+ private static final ObjectMapper mapper = new ObjectMapper();
public Field toArrowField(String name, Type type, boolean nullable) {
switch (type.name()) {
@@ -115,8 +115,8 @@ public class LanceDataTypeConverter implements
DataTypeConverter<ArrowType, Arro
Types.ExternalType externalType = (Types.ExternalType) type;
Field field;
try {
- field =
JsonUtils.anyFieldMapper().readValue(externalType.catalogString(), Field.class);
- } catch (JsonProcessingException e) {
+ field = mapper.readValue(externalType.catalogString(), Field.class);
+ } catch (Exception e) {
throw new RuntimeException(
"Failed to parse external type catalog string: " +
externalType.catalogString(), e);
}
@@ -202,9 +202,125 @@ public class LanceDataTypeConverter implements
DataTypeConverter<ArrowType, Arro
}
@Override
- public Type toGravitino(ArrowType arrowType) {
- // since the table metadata will load from Gravitino storage directly, we
don't need to
- // implement this method for now.
- throw new UnsupportedOperationException("toGravitino is not implemented
yet.");
+ public Type toGravitino(Field arrowField) {
+ FieldType fieldType = arrowField.getFieldType();
+ switch (fieldType.getType().getTypeID()) {
+ case Map:
+ Field structField = arrowField.getChildren().get(0);
+ Type keyType = toGravitino(structField.getChildren().get(0));
+ Type valueType = toGravitino(structField.getChildren().get(1));
+ boolean valueNullable = structField.getChildren().get(1).isNullable();
+ return Types.MapType.of(keyType, valueType, valueNullable);
+
+ case List:
+ Type elementType = toGravitino(arrowField.getChildren().get(0));
+ boolean containsNull = arrowField.getChildren().get(0).isNullable();
+ return Types.ListType.of(elementType, containsNull);
+
+ case Struct:
+ Types.StructType.Field[] fields =
+ arrowField.getChildren().stream()
+ .map(
+ child ->
+ Types.StructType.Field.of(
+ child.getName(),
+ toGravitino(child),
+ child.isNullable(),
+ null /*comment*/))
+ .toArray(Types.StructType.Field[]::new);
+ return Types.StructType.of(fields);
+
+ case Union:
+ List<Type> types =
arrowField.getChildren().stream().map(this::toGravitino).toList();
+ return Types.UnionType.of(types.toArray(new Type[0]));
+
+ case Bool:
+ return Types.BooleanType.get();
+
+ case Int:
+ Int intType = (Int) fieldType.getType();
+ switch (intType.getBitWidth()) {
+ case 8:
+ return intType.getIsSigned() ? Types.ByteType.get() :
Types.ByteType.unsigned();
+ case 8 * 2:
+ return intType.getIsSigned() ? Types.ShortType.get() :
Types.ShortType.unsigned();
+ case 8 * 4:
+ return intType.getIsSigned() ? Types.IntegerType.get() :
Types.IntegerType.unsigned();
+ case 8 * 8:
+ return intType.getIsSigned() ? Types.LongType.get() :
Types.LongType.unsigned();
+ }
+ break;
+
+ case FloatingPoint:
+ FloatingPoint floatingPoint = (FloatingPoint) fieldType.getType();
+ switch (floatingPoint.getPrecision()) {
+ case SINGLE:
+ return Types.FloatType.get();
+ case DOUBLE:
+ return Types.DoubleType.get();
+ default:
+ // fallthrough
+ }
+ break;
+
+ case Utf8:
+ return Types.StringType.get();
+ case Binary:
+ return Types.BinaryType.get();
+ case Decimal:
+ ArrowType.Decimal decimalType = (ArrowType.Decimal)
fieldType.getType();
+ return Types.DecimalType.of(decimalType.getPrecision(),
decimalType.getScale());
+ case Date:
+ if (((ArrowType.Date) fieldType.getType()).getUnit() == DateUnit.DAY) {
+ return Types.DateType.get();
+ }
+ break;
+ case Timestamp:
+ ArrowType.Timestamp timestampType = (ArrowType.Timestamp)
fieldType.getType();
+ int precision =
+ switch (timestampType.getUnit()) {
+ case SECOND -> 0;
+ case MILLISECOND -> 3;
+ case MICROSECOND -> 6;
+ case NANOSECOND -> 9;
+ };
+ boolean hasTimeZone = timestampType.getTimezone() != null;
+ return hasTimeZone
+ ? Types.TimestampType.withTimeZone(precision)
+ : Types.TimestampType.withoutTimeZone(precision);
+ case Time:
+ ArrowType.Time timeType = (ArrowType.Time) fieldType.getType();
+ if (timeType.getUnit() == TimeUnit.NANOSECOND &&
timeType.getBitWidth() == 8 * 8) {
+ return Types.TimeType.get();
+ }
+ break;
+ case Null:
+ return Types.NullType.get();
+ case Interval:
+ IntervalUnit intervalUnit = ((ArrowType.Interval)
fieldType.getType()).getUnit();
+ if (intervalUnit == IntervalUnit.YEAR_MONTH) {
+ return Types.IntervalYearType.get();
+ }
+ break;
+ case Duration:
+ TimeUnit timeUnit = ((ArrowType.Duration)
fieldType.getType()).getUnit();
+ if (timeUnit == TimeUnit.MICROSECOND) {
+ return Types.IntervalDayType.get();
+ }
+ break;
+ case FixedSizeBinary:
+ ArrowType.FixedSizeBinary fixedSizeBinary =
(ArrowType.FixedSizeBinary) fieldType.getType();
+ return Types.FixedType.of(fixedSizeBinary.getByteWidth());
+ default:
+ // fallthrough
+ }
+
+ String typeString;
+ try {
+ typeString = mapper.writeValueAsString(arrowField);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to serialize Arrow field to string.",
e);
+ }
+ return Types.ExternalType.of(typeString);
}
}
diff --git
a/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/TestArrowIPC.java
b/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/TestArrowIPC.java
deleted file mode 100644
index 71f1bfc587..0000000000
---
a/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/TestArrowIPC.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.gravitino.lance.common;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.util.Arrays;
-import org.apache.arrow.memory.BufferAllocator;
-import org.apache.arrow.memory.RootAllocator;
-import org.apache.arrow.vector.IntVector;
-import org.apache.arrow.vector.VarCharVector;
-import org.apache.arrow.vector.VectorSchemaRoot;
-import org.apache.arrow.vector.ipc.ArrowStreamWriter;
-import org.apache.arrow.vector.types.pojo.ArrowType;
-import org.apache.arrow.vector.types.pojo.Field;
-import org.apache.arrow.vector.types.pojo.Schema;
-import org.apache.arrow.vector.util.Text;
-import org.junit.jupiter.api.Test;
-
-public class TestArrowIPC {
-
- private static final String FILENAME = "/tmp/initial_data.arrow";
- private static final int RECORD_COUNT = 3;
-
- @Test
- void testIPC() throws Exception {
- try (BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE)) {
-
- Schema schema =
- new Schema(
- Arrays.asList(
- Field.nullable("id", new ArrowType.Int(32, true)),
- Field.nullable("value", new ArrowType.Utf8())));
-
- try (VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator))
{
- IntVector idVector = (IntVector) root.getVector("id");
- VarCharVector valueVector = (VarCharVector) root.getVector("value");
-
- idVector.allocateNew();
- valueVector.allocateNew();
-
- for (int i = 0; i < RECORD_COUNT; i++) {
- idVector.setSafe(i, i + 1);
- valueVector.setSafe(i, new Text("Row_" + (i + 1)));
- }
-
- idVector.setValueCount(RECORD_COUNT);
- valueVector.setValueCount(RECORD_COUNT);
- root.setRowCount(RECORD_COUNT);
-
- File outFile = new File(FILENAME);
- try (FileOutputStream fos = new FileOutputStream(outFile);
- ArrowStreamWriter writer = new ArrowStreamWriter(root, null, fos))
{
-
- writer.start();
- writer.writeBatch();
- writer.end();
- }
-
- System.out.println(
- "✅ Successfully generated Arrow IPC Stream file: " +
outFile.getAbsolutePath());
- System.out.println("--- Ready for cURL test ---");
- }
- }
- }
-}
diff --git
a/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestGravitinoLanceNamespaceWrapper.java
b/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestGravitinoLanceNamespaceWrapper.java
new file mode 100644
index 0000000000..b0ddb980ab
--- /dev/null
+++
b/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestGravitinoLanceNamespaceWrapper.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.lance.common.ops.gravitino;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.channels.Channels;
+import java.util.Arrays;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.ipc.ArrowStreamWriter;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.Schema;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestGravitinoLanceNamespaceWrapper {
+
+ @Test
+ public void testParseArrowIpcStream() throws Exception {
+ Schema schema =
+ new Schema(
+ Arrays.asList(
+ Field.nullable("id", new ArrowType.Int(32, true)),
+ Field.nullable("value", new ArrowType.Utf8())));
+
+ GravitinoLanceNamespaceWrapper wrapper = new
GravitinoLanceNamespaceWrapper();
+ byte[] ipcStream = generateIpcStream(schema);
+ Schema parsedSchema = wrapper.parseArrowIpcStream(ipcStream);
+
+ Assertions.assertEquals(schema, parsedSchema);
+ }
+
+ private byte[] generateIpcStream(Schema arrowSchema) throws IOException {
+ try (BufferAllocator allocator = new RootAllocator()) {
+
+ // Create an empty VectorSchemaRoot with the schema
+ try (VectorSchemaRoot root = VectorSchemaRoot.create(arrowSchema,
allocator)) {
+ // Allocate empty vectors (0 rows)
+ root.allocateNew();
+ root.setRowCount(0);
+
+ // Write to IPC stream
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ try (ArrowStreamWriter writer =
+ new ArrowStreamWriter(root, null,
Channels.newChannel(outputStream))) {
+ writer.start();
+ writer.writeBatch();
+ writer.end();
+ }
+
+ return outputStream.toByteArray();
+ }
+ } catch (Exception e) {
+ throw new IOException("Failed to create empty Arrow IPC stream: " +
e.getMessage(), e);
+ }
+ }
+}
diff --git
a/catalogs/catalog-generic-lakehouse/src/test/java/org/apache/gravitino/catalog/lakehouse/lance/TestLanceDataTypeConverter.java
b/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestLanceDataTypeConverter.java
similarity index 69%
rename from
catalogs/catalog-generic-lakehouse/src/test/java/org/apache/gravitino/catalog/lakehouse/lance/TestLanceDataTypeConverter.java
rename to
lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestLanceDataTypeConverter.java
index cf28ee7434..9908f8feff 100644
---
a/catalogs/catalog-generic-lakehouse/src/test/java/org/apache/gravitino/catalog/lakehouse/lance/TestLanceDataTypeConverter.java
+++
b/lance/lance-common/src/test/java/org/apache/gravitino/lance/common/ops/gravitino/TestLanceDataTypeConverter.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.gravitino.catalog.lakehouse.lance;
+package org.apache.gravitino.lance.common.ops.gravitino;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -24,13 +24,18 @@ import static
org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.arrow.vector.complex.MapVector;
+import org.apache.arrow.vector.types.DateUnit;
+import org.apache.arrow.vector.types.FloatingPointPrecision;
import org.apache.arrow.vector.types.TimeUnit;
import org.apache.arrow.vector.types.UnionMode;
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.gravitino.rel.types.Type;
import org.apache.gravitino.rel.types.Types;
import org.junit.jupiter.api.DisplayName;
@@ -270,10 +275,11 @@ public class TestLanceDataTypeConverter {
assertThrows(UnsupportedOperationException.class, () ->
CONVERTER.fromGravitino(unparsedType));
}
- @Test
- void testToGravitinoNotImplemented() {
- assertThrows(
- UnsupportedOperationException.class, () ->
CONVERTER.toGravitino(ArrowType.Utf8.INSTANCE));
+ @ParameterizedTest(name = "[{index}] {0}")
+ @MethodSource("toGravitinoArguments")
+ void testToGravitino(String testName, Field arrowField, Type
expectedGravitinoType) {
+ Type convertedType = CONVERTER.toGravitino(arrowField);
+ assertEquals(expectedGravitinoType, convertedType);
}
private static Stream<Arguments> toArrowFieldArguments() {
@@ -324,4 +330,144 @@ public class TestLanceDataTypeConverter {
true,
UNION_VALIDATOR));
}
+
+ private static Stream<Arguments> toGravitinoArguments() {
+ return Stream.of(
+ // Simple Types
+ Arguments.of(
+ "Boolean",
+ new Field("bool_col", new FieldType(true, ArrowType.Bool.INSTANCE,
null), null),
+ Types.BooleanType.get()),
+ Arguments.of(
+ "Byte",
+ new Field("byte_col", new FieldType(true, new ArrowType.Int(8,
true), null), null),
+ Types.ByteType.get()),
+ Arguments.of(
+ "Short",
+ new Field("short_col", new FieldType(true, new ArrowType.Int(16,
true), null), null),
+ Types.ShortType.get()),
+ Arguments.of(
+ "Integer",
+ new Field("int_col", new FieldType(true, new ArrowType.Int(32,
true), null), null),
+ Types.IntegerType.get()),
+ Arguments.of(
+ "Long",
+ new Field("long_col", new FieldType(true, new ArrowType.Int(64,
true), null), null),
+ Types.LongType.get()),
+ Arguments.of(
+ "Float",
+ new Field(
+ "float_col",
+ new FieldType(
+ true, new
ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE), null),
+ null),
+ Types.FloatType.get()),
+ Arguments.of(
+ "Double",
+ new Field(
+ "double_col",
+ new FieldType(
+ true, new
ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE), null),
+ null),
+ Types.DoubleType.get()),
+ Arguments.of(
+ "String",
+ new Field("string_col", new FieldType(true,
ArrowType.Utf8.INSTANCE, null), null),
+ Types.StringType.get()),
+ Arguments.of(
+ "Binary",
+ new Field("binary_col", new FieldType(true,
ArrowType.Binary.INSTANCE, null), null),
+ Types.BinaryType.get()),
+ Arguments.of(
+ "Decimal",
+ new Field(
+ "decimal_col", new FieldType(true, new ArrowType.Decimal(10,
2, 128), null), null),
+ Types.DecimalType.of(10, 2)),
+ Arguments.of(
+ "Date",
+ new Field(
+ "date_col", new FieldType(true, new
ArrowType.Date(DateUnit.DAY), null), null),
+ Types.DateType.get()),
+ Arguments.of(
+ "Timestamp without TZ",
+ new Field(
+ "ts_col",
+ new FieldType(true, new
ArrowType.Timestamp(TimeUnit.MICROSECOND, null), null),
+ null),
+ Types.TimestampType.withoutTimeZone(6)),
+ Arguments.of(
+ "Timestamp with TZ",
+ new Field(
+ "tstz_col",
+ new FieldType(true, new
ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC"), null),
+ null),
+ Types.TimestampType.withTimeZone(3)),
+ Arguments.of(
+ "Time",
+ new Field(
+ "time_col",
+ new FieldType(true, new ArrowType.Time(TimeUnit.NANOSECOND,
64), null),
+ null),
+ Types.TimeType.get()),
+ Arguments.of(
+ "Fixed",
+ new Field(
+ "fixed_col", new FieldType(true, new
ArrowType.FixedSizeBinary(16), null), null),
+ Types.FixedType.of(16)),
+ // Complex Types
+ Arguments.of(
+ "List",
+ new Field(
+ "list_col",
+ new FieldType(false, ArrowType.List.INSTANCE, null),
+ Collections.singletonList(
+ new Field(
+ "element", new FieldType(true, new ArrowType.Int(32,
true), null), null))),
+ Types.ListType.of(Types.IntegerType.get(), true)),
+ Arguments.of(
+ "Map",
+ new Field(
+ "map_col",
+ new FieldType(true, new ArrowType.Map(false), null),
+ Collections.singletonList(
+ new Field(
+ MapVector.DATA_VECTOR_NAME,
+ new FieldType(false, ArrowType.Struct.INSTANCE, null),
+ Arrays.asList(
+ new Field(
+ MapVector.KEY_NAME,
+ new FieldType(false, ArrowType.Utf8.INSTANCE,
null),
+ null),
+ new Field(
+ MapVector.VALUE_NAME,
+ new FieldType(true, new ArrowType.Int(32,
true), null),
+ null))))),
+ Types.MapType.of(Types.StringType.get(), Types.IntegerType.get(),
true)),
+ Arguments.of(
+ "Struct",
+ new Field(
+ "struct_col",
+ new FieldType(true, ArrowType.Struct.INSTANCE, null),
+ Arrays.asList(
+ new Field("id", new FieldType(false, new ArrowType.Int(64,
true), null), null),
+ new Field("name", new FieldType(true,
ArrowType.Utf8.INSTANCE, null), null))),
+ SIMPLE_STRUCT),
+ Arguments.of(
+ "Union",
+ new Field(
+ "union_col",
+ new FieldType(true, new ArrowType.Union(UnionMode.Sparse, new
int[] {1, 2}), null),
+ Arrays.asList(
+ new Field(
+ "integer", new FieldType(true, new ArrowType.Int(32,
true), null), null),
+ new Field("string", new FieldType(true,
ArrowType.Utf8.INSTANCE, null), null))),
+ Types.UnionType.of(Types.IntegerType.get(),
Types.StringType.get())),
+ // External Type
+ Arguments.of(
+ "External (LargeUtf8)",
+ new Field(
+ "external_col", new FieldType(true,
ArrowType.LargeUtf8.INSTANCE, null), null),
+ Types.ExternalType.of(
+
"{\"name\":\"external_col\",\"nullable\":true,\"type\":{\"name\":\"largeutf8\"},\"children\":[]}")));
+ }
}
diff --git
a/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceNamespaceOperations.java
b/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceNamespaceOperations.java
index 4c3b96ce9a..1ae9637b20 100644
---
a/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceNamespaceOperations.java
+++
b/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceNamespaceOperations.java
@@ -167,7 +167,7 @@ public class LanceNamespaceOperations {
@ResponseMetered(name = "list-tables", absolute = true)
public Response listTables(
@PathParam("id") String namespaceId,
- @DefaultValue("$") @QueryParam("delimiter") String delimiter,
+ @DefaultValue(NAMESPACE_DELIMITER_DEFAULT) @QueryParam("delimiter")
String delimiter,
@QueryParam("page_token") String pageToken,
@QueryParam("limit") Integer limit) {
try {
diff --git
a/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java
b/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java
index dd10a31534..690cb8759e 100644
---
a/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java
+++
b/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java
@@ -18,6 +18,8 @@
*/
package org.apache.gravitino.lance.service.rest;
+import static
org.apache.gravitino.lance.common.ops.NamespaceWrapper.NAMESPACE_DELIMITER_DEFAULT;
+
import com.codahale.metrics.annotation.ResponseMetered;
import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -59,7 +61,7 @@ public class LanceTableOperations {
@ResponseMetered(name = "describe-table", absolute = true)
public Response describeTable(
@PathParam("id") String tableId,
- @DefaultValue("$") @QueryParam("delimiter") String delimiter) {
+ @DefaultValue(NAMESPACE_DELIMITER_DEFAULT) @QueryParam("delimiter")
String delimiter) {
try {
DescribeTableResponse response =
lanceNamespace.asTableOps().describeTable(tableId, delimiter);
@@ -78,14 +80,14 @@ public class LanceTableOperations {
public Response createTable(
@PathParam("id") String tableId,
@QueryParam("mode") @DefaultValue("create") String mode, // create,
exist_ok, overwrite
- @QueryParam("delimiter") @DefaultValue("$") String delimiter,
+ @QueryParam("delimiter") @DefaultValue(NAMESPACE_DELIMITER_DEFAULT)
String delimiter,
@HeaderParam("x-lance-table-location") String tableLocation,
@HeaderParam("x-lance-table-properties") String tableProperties,
@HeaderParam("x-lance-root-catalog") String rootCatalog,
byte[] arrowStreamBody) {
try {
Map<String, String> props =
- JsonUtil.mapper().readValue(tableProperties, new
TypeReference<Map<String, String>>() {});
+ JsonUtil.mapper().readValue(tableProperties, new TypeReference<>()
{});
CreateTableResponse response =
lanceNamespace
.asTableOps()
@@ -104,7 +106,7 @@ public class LanceTableOperations {
public Response createEmptyTable(
@PathParam("id") String tableId,
@QueryParam("mode") @DefaultValue("create") String mode, // create,
exist_ok, overwrite
- @QueryParam("delimiter") @DefaultValue("$") String delimiter,
+ @QueryParam("delimiter") @DefaultValue(NAMESPACE_DELIMITER_DEFAULT)
String delimiter,
@HeaderParam("x-lance-table-location") String tableLocation,
@HeaderParam("x-lance-root-catalog") String rootCatalog,
@HeaderParam("x-lance-table-properties") String tableProperties) {
@@ -112,8 +114,7 @@ public class LanceTableOperations {
Map<String, String> props =
StringUtils.isBlank(tableProperties)
? Map.of()
- : JsonUtil.mapper()
- .readValue(tableProperties, new TypeReference<Map<String,
String>>() {});
+ : JsonUtil.mapper().readValue(tableProperties, new
TypeReference<>() {});
CreateTableResponse response =
lanceNamespace
.asTableOps()