blob: b3753a14cbf3b9dc47ade0cb9bdce4320e3b0072 [file] [log] [blame]
/*
* 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.ignite.internal.schema.registry;
import static org.apache.ignite.internal.catalog.commands.CatalogUtils.isSupportedColumnTypeChange;
import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.BitSet;
import java.util.UUID;
import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
import org.apache.ignite.internal.schema.BinaryRowConverter;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.BinaryTupleSchema;
import org.apache.ignite.internal.schema.BinaryTupleSchema.Element;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.InvalidTypeException;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.SchemaException;
import org.apache.ignite.internal.schema.mapping.ColumnMapper;
import org.apache.ignite.internal.schema.row.Row;
import org.apache.ignite.internal.type.NativeTypeSpec;
import org.apache.ignite.sql.ColumnType;
import org.jetbrains.annotations.Nullable;
/**
* Adapter for row of older schema.
*/
public class UpgradingRowAdapter extends Row {
/** Column mapper. */
private final ColumnMapper mapper;
/** Adapter schema. */
private final SchemaDescriptor newSchema;
private final BinaryTupleSchema newBinaryTupleSchema;
private UpgradingRowAdapter(SchemaDescriptor newSchema, BinaryTupleSchema newBinaryTupleSchema, Row row, ColumnMapper mapper) {
super(false, row.schema(), row.binaryTupleSchema(), row);
this.newSchema = newSchema;
this.mapper = mapper;
this.newBinaryTupleSchema = newBinaryTupleSchema;
}
/**
* Creates an adapter that converts a given {@code row} to a new schema.
*
* @param newSchema New schema that the {@code row} will be converted to.
* @param mapper Column mapper for converting columns to the new schema.
* @param row Row to convert.
* @return Adapter that converts a given {@code row} to a new schema.
*/
public static UpgradingRowAdapter upgradeRow(SchemaDescriptor newSchema, ColumnMapper mapper, Row row) {
return new UpgradingRowAdapter(newSchema, BinaryTupleSchema.createRowSchema(newSchema), row, mapper);
}
/**
* Creates an adapter that converts a given {@code row}, that only contains a key component, to a new schema.
*
* @param newSchema New schema that the {@code row} will be converted to.
* @param mapper Column mapper for converting columns to the new schema.
* @param row Row to convert, that only contains a key component.
* @return Adapter that converts a given {@code row} to a new schema.
*/
public static UpgradingRowAdapter upgradeKeyOnlyRow(SchemaDescriptor newSchema, ColumnMapper mapper, Row row) {
return new UpgradingRowAdapter(newSchema, BinaryTupleSchema.createKeySchema(newSchema), row, mapper);
}
/** {@inheritDoc} */
@Override
public SchemaDescriptor schema() {
return newSchema;
}
/** {@inheritDoc} */
@Override
public int schemaVersion() {
return newSchema.version();
}
/**
* Map column.
*
* @param colIdx Column index in source schema.
* @return Column index in target schema.
*/
private int mapColumn(int colIdx) throws InvalidTypeException {
return mapper.map(colIdx);
}
/** {@inheritDoc} */
@Override
public Object value(int colIdx) {
int mappedId = mapColumn(colIdx);
return mappedId < 0
? mapper.mappedColumn(colIdx).defaultValue()
: newBinaryTupleSchema.value(this, colIdx);
}
/** {@inheritDoc} */
@Override
public boolean booleanValue(int colIdx) {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.BOOLEAN != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (boolean) column.defaultValue() : super.booleanValue(mappedId);
}
/** {@inheritDoc} */
@Override
public Boolean booleanValueBoxed(int colIdx) {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.BOOLEAN != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (Boolean) column.defaultValue() : super.booleanValueBoxed(mappedId);
}
/** {@inheritDoc} */
@Override
public byte byteValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.INT8);
return mappedId < 0 ? (byte) column.defaultValue() : super.byteValue(mappedId);
}
/** {@inheritDoc} */
@Override
public Byte byteValueBoxed(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.INT8);
return mappedId < 0 ? (Byte) column.defaultValue() : super.byteValueBoxed(mappedId);
}
/** {@inheritDoc} */
@Override
public short shortValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.INT16);
return mappedId < 0 ? (short) column.defaultValue() : super.shortValue(mappedId);
}
/** {@inheritDoc} */
@Override
public Short shortValueBoxed(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.INT16);
return mappedId < 0 ? (Short) column.defaultValue() : super.shortValueBoxed(mappedId);
}
/** {@inheritDoc} */
@Override
public int intValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.INT32);
return mappedId < 0 ? (int) column.defaultValue() : super.intValue(mappedId);
}
/** {@inheritDoc} */
@Override
public Integer intValueBoxed(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.INT32);
return mappedId < 0 ? (Integer) column.defaultValue() : super.intValueBoxed(mappedId);
}
/** {@inheritDoc} */
@Override
public long longValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.INT64);
return mappedId < 0 ? (long) column.defaultValue() : super.longValue(mappedId);
}
/** {@inheritDoc} */
@Override
public Long longValueBoxed(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.INT64);
return mappedId < 0 ? (Long) column.defaultValue() : super.longValueBoxed(mappedId);
}
/** {@inheritDoc} */
@Override
public float floatValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.FLOAT);
return mappedId < 0 ? (float) column.defaultValue() : super.floatValue(mappedId);
}
/** {@inheritDoc} */
@Override
public Float floatValueBoxed(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.FLOAT);
return mappedId < 0 ? (Float) column.defaultValue() : super.floatValueBoxed(mappedId);
}
/** {@inheritDoc} */
@Override
public double doubleValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.DOUBLE);
return mappedId < 0 ? (double) column.defaultValue() : super.doubleValue(mappedId);
}
/** {@inheritDoc} */
@Override
public Double doubleValueBoxed(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
ensureTypeConversionAllowed(column.type().spec().asColumnType(), ColumnType.DOUBLE);
return mappedId < 0 ? (Double) column.defaultValue() : super.doubleValueBoxed(mappedId);
}
@Override
public BigDecimal decimalValue(int colIdx) {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.DECIMAL != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (BigDecimal) column.defaultValue() : super.decimalValue(mappedId);
}
@Override
public BigDecimal decimalValue(int colIdx, int scale) {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.DECIMAL != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (BigDecimal) column.defaultValue() : super.decimalValue(mappedId, scale);
}
/** {@inheritDoc} */
@Override
public BigInteger numberValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.NUMBER != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (BigInteger) column.defaultValue() : super.numberValue(mappedId);
}
/** {@inheritDoc} */
@Override
public String stringValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.STRING != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (String) column.defaultValue() : super.stringValue(mappedId);
}
/** {@inheritDoc} */
@Override
public byte[] bytesValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.BYTES != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (byte[]) column.defaultValue() : super.bytesValue(mappedId);
}
/** {@inheritDoc} */
@Override
public UUID uuidValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.UUID != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (UUID) column.defaultValue() : super.uuidValue(mappedId);
}
/** {@inheritDoc} */
@Override
public BitSet bitmaskValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.BITMASK != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (BitSet) column.defaultValue() : super.bitmaskValue(mappedId);
}
/** {@inheritDoc} */
@Override
public LocalDate dateValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.DATE != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (LocalDate) column.defaultValue() : super.dateValue(mappedId);
}
/** {@inheritDoc} */
@Override
public LocalTime timeValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.TIME != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (LocalTime) column.defaultValue() : super.timeValue(mappedId);
}
/** {@inheritDoc} */
@Override
public LocalDateTime dateTimeValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.DATETIME != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (LocalDateTime) column.defaultValue() : super.dateTimeValue(mappedId);
}
/** {@inheritDoc} */
@Override
public Instant timestampValue(int colIdx) throws InvalidTypeException {
int mappedId = mapColumn(colIdx);
Column column = mappedId < 0 ? mapper.mappedColumn(colIdx) : super.schema().column(mappedId);
if (NativeTypeSpec.TIMESTAMP != column.type().spec()) {
throw new SchemaException("Type conversion is not supported yet.");
}
return mappedId < 0 ? (Instant) column.defaultValue() : super.timestampValue(mappedId);
}
@Override
public boolean hasNullValue(int colIdx) {
int mappedId = mapColumn(colIdx);
return mappedId < 0
? mapper.mappedColumn(colIdx).defaultValue() == null
: super.hasNullValue(mappedId);
}
@Override
public int elementCount() {
return newBinaryTupleSchema.elementCount();
}
/** {@inheritDoc} */
@Override
public @Nullable BinaryTuple binaryTuple() {
// Underlying binary tuple can not be used directly.
return null;
}
/** {@inheritDoc} */
@Override
public ByteBuffer byteBuffer() {
// TODO: IGNITE-22156 Replace inheritance with delegation and drop this code.
int size = newBinaryTupleSchema.elementCount();
var builder = new BinaryTupleBuilder(size);
for (int col = 0; col < size; col++) {
Element element = newBinaryTupleSchema.element(col);
BinaryRowConverter.appendValue(builder, element, value(col));
}
return new BinaryTuple(size, builder.build()).byteBuffer();
}
/** {@inheritDoc} */
@Override
public int tupleSliceLength() {
throw new UnsupportedOperationException("Underlying binary can't be accessed directly.");
}
/** {@inheritDoc} */
@Override
public ByteBuffer tupleSlice() {
throw new UnsupportedOperationException("Underlying binary can't be accessed directly.");
}
private void ensureTypeConversionAllowed(ColumnType from, ColumnType to) throws InvalidTypeException {
if (!isSupportedColumnTypeChange(from, to)) {
throw new SchemaException(format("Type conversion is not allowed: {} -> {}", from, to));
}
}
}