Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,15 @@ private void loadBuffers(
Iterator<ArrowFieldNode> nodes,
CompressionCodec codec,
Iterator<Long> variadicBufferCounts) {
FieldVector storageVector = vector;
while (storageVector instanceof ExtensionTypeVector) {
storageVector = ((ExtensionTypeVector<?>) storageVector).getUnderlyingVector();
}
checkArgument(nodes.hasNext(), "no more field nodes for field %s and vector %s", field, vector);
ArrowFieldNode fieldNode = nodes.next();
// variadicBufferLayoutCount will be 0 for vectors of a type except BaseVariableWidthViewVector
// Only view storage has variadic buffers.
long variadicBufferLayoutCount = 0;
if (vector instanceof BaseVariableWidthViewVector) {
if (storageVector instanceof BaseVariableWidthViewVector) {
if (variadicBufferCounts.hasNext()) {
variadicBufferLayoutCount = variadicBufferCounts.next();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,18 @@ private void appendNodes(
List<ArrowFieldNode> nodes,
List<ArrowBuf> buffers,
List<Long> variadicBufferCounts) {
FieldVector storageVector = vector;
while (storageVector instanceof ExtensionTypeVector) {
storageVector = ((ExtensionTypeVector<?>) storageVector).getUnderlyingVector();
}
nodes.add(
new ArrowFieldNode(vector.getValueCount(), includeNullCount ? vector.getNullCount() : -1));
List<ArrowBuf> fieldBuffers = vector.getFieldBuffers();
long variadicBufferCount = getVariadicBufferCount(vector);
long variadicBufferCount = getVariadicBufferCount(storageVector);
int expectedBufferCount =
(int) (TypeLayout.getTypeBufferCount(vector.getField().getType()) + variadicBufferCount);
// only update variadicBufferCounts for vectors that have variadic buffers
if (vector instanceof BaseVariableWidthViewVector) {
if (storageVector instanceof BaseVariableWidthViewVector) {
variadicBufferCounts.add(variadicBufferCount);
}
if (fieldBuffers.size() != expectedBufferCount) {
Expand Down
132 changes: 132 additions & 0 deletions vector/src/main/java/org/apache/arrow/vector/extension/JsonType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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.arrow.vector.extension;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Collections;
import java.util.Objects;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.ExtensionTypeRegistry;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;

/**
* Canonical extension type for UTF-8 encoded RFC 8259 JSON values.
*
* <p>The storage type is {@link ArrowType.Utf8}, {@link ArrowType.LargeUtf8}, or {@link
* ArrowType.Utf8View}. Values use the corresponding string vector; this type does not parse or
* validate individual JSON values.
*
* <p>Register the type before reading schemas containing {@code arrow.json}:
*
* <pre>{@code
* JsonType.ensureRegistered();
* Field field = Field.nullable("json", new JsonType(ArrowType.Utf8.INSTANCE));
* try (JsonVector vector = (JsonVector) field.createVector(allocator)) {
* VarCharVector storage = (VarCharVector) vector.getUnderlyingVector();
* storage.setSafe(0, "{}".getBytes(java.nio.charset.StandardCharsets.UTF_8));
* vector.setValueCount(1);
* Text value = vector.getObject(0);
* }
* }</pre>
*/
public class JsonType extends ArrowType.ExtensionType {
public static final String EXTENSION_NAME = "arrow.json";
private static final ObjectMapper MAPPER =
new ObjectMapper().enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
private final ArrowType storageType;

/** Register a prototype that can deserialize all supported JSON storage types. */
public static void ensureRegistered() {
ExtensionTypeRegistry.register(new JsonType(ArrowType.Utf8.INSTANCE));
}

/**
* Create a JSON type backed by the specified string type.
*
* @param storageType Utf8, LargeUtf8, or Utf8View
* @throws IllegalArgumentException if the storage type is not a supported string type
*/
public JsonType(ArrowType storageType) {
Objects.requireNonNull(storageType, "storageType");
if (!(storageType instanceof ArrowType.Utf8)
&& !(storageType instanceof ArrowType.LargeUtf8)
&& !(storageType instanceof ArrowType.Utf8View)) {
throw new IllegalArgumentException(
"arrow.json requires Utf8, LargeUtf8, or Utf8View storage, got " + storageType);
}
this.storageType = storageType;
}

@Override
public ArrowType storageType() {
return storageType;
}

@Override
public String extensionName() {
return EXTENSION_NAME;
}

@Override
public boolean extensionEquals(ExtensionType other) {
return other instanceof JsonType && storageType.equals(other.storageType());
}

@Override
public String serialize() {
return "";
}

@Override
public ArrowType deserialize(ArrowType storageType, String serializedData) {
JsonType type = new JsonType(storageType);
if (serializedData == null) {
throw new InvalidExtensionMetadataException("arrow.json metadata must not be null");
}
if (!serializedData.isEmpty()) {
try {
JsonNode metadata = MAPPER.readTree(serializedData);
if (metadata == null || !metadata.isObject()) {
throw new InvalidExtensionMetadataException("arrow.json metadata must be a JSON object");
}
} catch (JsonProcessingException e) {
throw new InvalidExtensionMetadataException("arrow.json metadata is invalid", e);
}
}
return type;
}

@Override
public boolean isComplex() {
return false;
}

@Override
public FieldVector getNewVector(String name, FieldType fieldType, BufferAllocator allocator) {
Field field = new Field(name, fieldType, Collections.emptyList());
FieldType storageFieldType =
new FieldType(fieldType.isNullable(), storageType, fieldType.getDictionary(), null);
FieldVector storage = storageFieldType.createNewSingleVector(name, allocator, null);
return new JsonVector(field, allocator, storage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* 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.arrow.vector.extension;

import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.util.hash.ArrowBufHasher;
import org.apache.arrow.vector.ExtensionTypeVector;
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.ValueIterableVector;
import org.apache.arrow.vector.ValueVector;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.util.CallBack;
import org.apache.arrow.vector.util.Text;
import org.apache.arrow.vector.util.TransferPair;

/**
* A JSON extension vector backed by a string vector.
*
* <p>Use {@link Field#createVector(BufferAllocator)} with a {@link JsonType} field to create an
* instance. Write UTF-8 JSON through {@link #getUnderlyingVector()}; values are not parsed or
* validated.
*/
public class JsonVector extends ExtensionTypeVector<FieldVector>
implements ValueIterableVector<Text> {
private final Field field;

JsonVector(Field field, BufferAllocator allocator, FieldVector underlyingVector) {
super(field, allocator, underlyingVector);
this.field = field;
}

@Override
public Field getField() {
return field;
}

@Override
public Text getObject(int index) {
return (Text) getUnderlyingVector().getObject(index);
}

@Override
public TransferPair getTransferPair(BufferAllocator allocator) {
return getTransferPair(field, allocator);
}

@Override
public TransferPair getTransferPair(String name, BufferAllocator allocator) {
return getTransferPair(new Field(name, field.getFieldType(), field.getChildren()), allocator);
}

@Override
public TransferPair getTransferPair(String name, BufferAllocator allocator, CallBack callBack) {
return getTransferPair(name, allocator);
}

@Override
public TransferPair getTransferPair(Field targetField, BufferAllocator allocator) {
return makeTransferPair(targetField.createVector(allocator));
}

@Override
public TransferPair getTransferPair(
Field targetField, BufferAllocator allocator, CallBack callBack) {
return getTransferPair(targetField, allocator);
}

@Override
public TransferPair makeTransferPair(ValueVector target) {
return new TransferImpl((JsonVector) target);
}

@Override
public int hashCode(int index) {
return hashCode(index, null);
}

@Override
public int hashCode(int index, ArrowBufHasher hasher) {
return getUnderlyingVector().hashCode(index, hasher);
}

private class TransferImpl implements TransferPair {
private final JsonVector to;
private final TransferPair storagePair;

TransferImpl(JsonVector to) {
this.to = to;
this.storagePair = getUnderlyingVector().makeTransferPair(to.getUnderlyingVector());
}

@Override
public void transfer() {
storagePair.transfer();
}

@Override
public void splitAndTransfer(int startIndex, int length) {
storagePair.splitAndTransfer(startIndex, length);
}

@Override
public JsonVector getTo() {
return to;
}

@Override
public void copyValueSafe(int fromIndex, int toIndex) {
storagePair.copyValueSafe(fromIndex, toIndex);
}
}
}
Loading
Loading