All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.clickzetta.platform.operator.PartialRow Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package com.clickzetta.platform.operator;

import com.clickzetta.platform.common.ColumnSchema;
import com.clickzetta.platform.common.ColumnTypeAttributes;
import com.clickzetta.platform.common.Schema;
import com.clickzetta.platform.common.Type;
import com.clickzetta.platform.util.DateUtil;
import com.clickzetta.platform.util.DecimalUtil;
import com.clickzetta.platform.util.StringUtil;
import com.clickzetta.platform.util.TimestampUtil;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.*;

public class PartialRow {

  private final Schema schema;

  private final List varLengthData;
  private final byte[] rowAlloc;

  private final BitSet columnsBitSet;
  private final BitSet nullsBitSet;

  private boolean frozen = false;

  public static final byte[] EMPTY_ARRAY = new byte[0];

  public int maxChangedIndex;

  public PartialRow(Schema schema) {
    this.schema = schema;
    this.columnsBitSet = new BitSet(this.schema.getColumnCount());
    this.nullsBitSet = schema.hasNullableColumns() ?
        new BitSet(this.schema.getColumnCount()) : null;
    this.rowAlloc = new byte[schema.getRowSize()];
    this.varLengthData = Arrays.asList(new ByteBuffer[this.schema.getVarLengthColumnCount()]);

    // mark max changed index.
    this.maxChangedIndex = -1;
  }

  public PartialRow(PartialRow row) {
    this.schema = row.schema;
    this.maxChangedIndex = row.maxChangedIndex;

    this.varLengthData = Lists.newArrayListWithCapacity(row.varLengthData.size());
    for (ByteBuffer data: row.varLengthData) {
      if (data == null) {
        this.varLengthData.add(null);
      } else {
        data.reset();
        // Deep copy the ByteBuffer.
        ByteBuffer clone = ByteBuffer.allocate(data.remaining());
        clone.put(data);
        clone.flip();

        clone.mark();
        this.varLengthData.add(clone);
      }
    }

    this.rowAlloc = row.rowAlloc.clone();
    this.columnsBitSet = (BitSet) row.columnsBitSet.clone();
    this.nullsBitSet = row.nullsBitSet == null ? null : (BitSet) row.nullsBitSet.clone();
  }


  public void addBoolean(int columnIndex, boolean val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.BOOL);
    rowAlloc[getPositionInRowAllocAndSetBitSet(columnIndex)] = (byte) (val ? 1 : 0);
  }


  public void addBoolean(String columnName, boolean val) {
    addBoolean(schema.getColumnIndex(columnName), val);
  }


  public boolean getBoolean(String columnName) {
    return getBoolean(this.schema.getColumnIndex(columnName));
  }


  public boolean getBoolean(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.BOOL);
    checkValue(columnIndex);
    byte b = rowAlloc[schema.getColumnOffset(columnIndex)];
    return b == 1;
  }


  public void addByte(int columnIndex, byte val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.INT8);
    rowAlloc[getPositionInRowAllocAndSetBitSet(columnIndex)] = val;
  }


  public void addByte(String columnName, byte val) {
    addByte(schema.getColumnIndex(columnName), val);
  }


  public byte getByte(String columnName) {
    return getByte(this.schema.getColumnIndex(columnName));
  }


  public byte getByte(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.INT8);
    checkValue(columnIndex);
    return rowAlloc[schema.getColumnOffset(columnIndex)];
  }


  public void addShort(int columnIndex, short val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.INT16);
    Bytes.setShort(rowAlloc, val, getPositionInRowAllocAndSetBitSet(columnIndex));
  }


  public void addShort(String columnName, short val) {
    addShort(schema.getColumnIndex(columnName), val);
  }


  public short getShort(String columnName) {
    return getShort(this.schema.getColumnIndex(columnName));
  }


  public short getShort(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.INT16);
    checkValue(columnIndex);
    return Bytes.getShort(rowAlloc, schema.getColumnOffset(columnIndex));
  }


  public void addInt(int columnIndex, int val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.INT32);
    Bytes.setInt(rowAlloc, val, getPositionInRowAllocAndSetBitSet(columnIndex));
  }


  public void addInt(String columnName, int val) {
    addInt(schema.getColumnIndex(columnName), val);
  }


  public int getInt(String columnName) {
    return getInt(this.schema.getColumnIndex(columnName));
  }


  public int getInt(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.INT32);
    checkValue(columnIndex);
    return Bytes.getInt(rowAlloc, schema.getColumnOffset(columnIndex));
  }


  public void addLong(int columnIndex, long val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.INT64, Type.UNIXTIME_MICROS);
    Bytes.setLong(rowAlloc, val, getPositionInRowAllocAndSetBitSet(columnIndex));
  }


  public void addLong(String columnName, long val) {
    addLong(schema.getColumnIndex(columnName), val);
  }


  public long getLong(String columnName) {
    return getLong(this.schema.getColumnIndex(columnName));
  }


  public long getLong(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.INT64, Type.UNIXTIME_MICROS);
    checkColumnExists(schema.getColumnByIndex(columnIndex));
    checkValue(columnIndex);
    return Bytes.getLong(rowAlloc, schema.getColumnOffset(columnIndex));
  }


  public void addFloat(int columnIndex, float val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.FLOAT);
    Bytes.setFloat(rowAlloc, val, getPositionInRowAllocAndSetBitSet(columnIndex));
  }


  public void addFloat(String columnName, float val) {
    addFloat(schema.getColumnIndex(columnName), val);
  }


  public float getFloat(String columnName) {
    return getFloat(this.schema.getColumnIndex(columnName));
  }


  public float getFloat(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.FLOAT);
    checkValue(columnIndex);
    return Bytes.getFloat(rowAlloc, schema.getColumnOffset(columnIndex));
  }

  public void addDouble(int columnIndex, double val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.DOUBLE);
    Bytes.setDouble(rowAlloc, val, getPositionInRowAllocAndSetBitSet(columnIndex));
  }


  public void addDouble(String columnName, double val) {
    addDouble(schema.getColumnIndex(columnName), val);
  }


  public double getDouble(String columnName) {
    return getDouble(this.schema.getColumnIndex(columnName));
  }


  public double getDouble(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.DOUBLE);
    checkValue(columnIndex);
    return Bytes.getDouble(rowAlloc, schema.getColumnOffset(columnIndex));
  }


  public void addDecimal(int columnIndex, BigDecimal val) {
    checkNotFrozen();
    ColumnSchema column = schema.getColumnByIndex(columnIndex);
    ColumnTypeAttributes typeAttributes = column.getTypeAttributes();
    checkColumn(column, Type.DECIMAL);
    BigDecimal coercedVal = DecimalUtil.coerce(val,typeAttributes.getPrecision(),
        typeAttributes.getScale());
    Bytes.setBigDecimal(rowAlloc, coercedVal, typeAttributes.getPrecision(),
        getPositionInRowAllocAndSetBitSet(columnIndex));
  }


  public void addDecimal(String columnName, BigDecimal val) {
    addDecimal(schema.getColumnIndex(columnName), val);
  }


  public BigDecimal getDecimal(String columnName) {
    return getDecimal(this.schema.getColumnIndex(columnName));
  }


  public BigDecimal getDecimal(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.DECIMAL);
    checkColumnExists(schema.getColumnByIndex(columnIndex));
    checkValue(columnIndex);
    ColumnSchema column = schema.getColumnByIndex(columnIndex);
    ColumnTypeAttributes typeAttributes = column.getTypeAttributes();
    return Bytes.getDecimal(rowAlloc, schema.getColumnOffset(columnIndex),
        typeAttributes.getPrecision(), typeAttributes.getScale());
  }


  public void addTimestamp(int columnIndex, Timestamp val) {
    checkNotFrozen();
    ColumnSchema column = schema.getColumnByIndex(columnIndex);
    checkColumn(column, Type.UNIXTIME_MICROS);
    long micros = TimestampUtil.timestampToMicros(val);
    Bytes.setLong(rowAlloc, micros, getPositionInRowAllocAndSetBitSet(columnIndex));
  }


  public void addTimestamp(String columnName, Timestamp val) {
    addTimestamp(schema.getColumnIndex(columnName), val);
  }


  public Timestamp getTimestamp(String columnName) {
    return getTimestamp(this.schema.getColumnIndex(columnName));
  }


  public Timestamp getTimestamp(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.UNIXTIME_MICROS);
    checkColumnExists(schema.getColumnByIndex(columnIndex));
    checkValue(columnIndex);
    long micros = Bytes.getLong(rowAlloc, schema.getColumnOffset(columnIndex));
    return TimestampUtil.microsToTimestamp(micros);
  }


  public void addDate(int columnIndex, Date val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.DATE);
    int days = DateUtil.sqlDateToEpochDays(val);
    Bytes.setInt(rowAlloc, days, getPositionInRowAllocAndSetBitSet(columnIndex));
  }


  public void addDate(String columnName, Date val) {
    addDate(schema.getColumnIndex(columnName), val);
  }


  public Date getDate(String columnName) {
    return getDate(this.schema.getColumnIndex(columnName));
  }


  public Date getDate(int columnIndex) {
    checkColumnExists(schema.getColumnByIndex(columnIndex));
    checkColumn(schema.getColumnByIndex(columnIndex), Type.DATE);
    checkValue(columnIndex);
    int days = Bytes.getInt(rowAlloc, schema.getColumnOffset(columnIndex));
    return DateUtil.epochDaysToSqlDate(days);
  }


  public void addString(int columnIndex, String val) {
    addStringUtf8(columnIndex, Bytes.fromString(val));
  }


  public void addString(String columnName, String val) {
    addStringUtf8(columnName, Bytes.fromString(val));
  }


  public void addVarchar(int columnIndex, String val) {
    ColumnSchema column = schema.getColumnByIndex(columnIndex);
    checkColumn(column, Type.VARCHAR);
    checkNotFrozen();
    int length = column.getTypeAttributes().getLength();
    if (length < val.length()) {
      val = val.substring(0, length);
    }
    byte[] bytes = Bytes.fromString(val);
    addVarLengthData(columnIndex, bytes);
  }


  public void addVarchar(String columnName, String val) {
    addVarchar(schema.getColumnIndex(columnName), val);
  }


  public String getString(String columnName) {
    return getString(this.schema.getColumnIndex(columnName));
  }


  public String getString(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.STRING);
    checkValue(columnIndex);
    return new String(getVarLengthData(columnIndex).array(), StandardCharsets.UTF_8);
  }


  public String getVarchar(String columnName) {
    return getVarchar(this.schema.getColumnIndex(columnName));
  }


  public String getVarchar(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.VARCHAR);
    checkValue(columnIndex);
    return new String(getVarLengthData(columnIndex).array(), StandardCharsets.UTF_8);
  }


  public void addStringUtf8(int columnIndex, byte[] val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.STRING);
    addVarLengthData(columnIndex, val);
  }


  public void addStringUtf8(String columnName, byte[] val) {
    addStringUtf8(schema.getColumnIndex(columnName), val);
  }


  public void addBinary(int columnIndex, byte[] val) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.BINARY);
    addVarLengthData(columnIndex, val);
  }


  public void addBinary(int columnIndex, ByteBuffer value) {
    checkNotFrozen();
    checkColumn(schema.getColumnByIndex(columnIndex), Type.BINARY);
    addVarLengthData(columnIndex, value);
  }


  public void addBinary(String columnName, byte[] val) {
    addBinary(schema.getColumnIndex(columnName), val);
  }


  public void addBinary(String columnName, ByteBuffer value) {
    addBinary(schema.getColumnIndex(columnName), value);
  }


  public byte[] getBinaryCopy(String columnName) {
    return getBinaryCopy(this.schema.getColumnIndex(columnName));
  }


  public byte[] getBinaryCopy(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.BINARY);
    checkValue(columnIndex);
    byte[] data = getVarLengthData(columnIndex).array();
    byte[] ret = new byte[data.length];
    System.arraycopy(data, 0, ret, 0, data.length);
    return ret;
  }


  public ByteBuffer getBinary(String columnName) {
    return getBinary(this.schema.getColumnIndex(columnName));
  }


  public ByteBuffer getBinary(int columnIndex) {
    checkColumn(schema.getColumnByIndex(columnIndex), Type.BINARY);
    checkValue(columnIndex);
    return getVarLengthData(columnIndex);
  }

  private void addVarLengthData(int columnIndex, byte[] val) {
    addVarLengthData(columnIndex, ByteBuffer.wrap(val));
  }

  private void addVarLengthData(int columnIndex, ByteBuffer val) {
    ByteBuffer duplicate = val.duplicate();
    duplicate.mark();

    varLengthData.set(this.schema.getVarLengthColumnOffsetToAllVarCharColumns(columnIndex), duplicate);
    getPositionInRowAllocAndSetBitSet(columnIndex);
  }


  List getVarLengthData() {
    return varLengthData;
  }

  private ByteBuffer getVarLengthData(int columnIndex) {
    return varLengthData.get(this.schema.getVarLengthColumnOffsetToAllVarCharColumns(columnIndex)).duplicate();
  }

  public void setNull(String columnName) {
    setNull(this.schema.getColumnIndex(columnName));
  }

  public void setNull(int columnIndex) {
    assert nullsBitSet != null;
    ColumnSchema column = schema.getColumnByIndex(columnIndex);
    checkNotFrozen();
    checkColumnExists(column);
    if (!column.isNullable()) {
      throw new IllegalArgumentException(column.getName() + " cannot be set to null");
    }
    columnsBitSet.set(columnIndex);
    nullsBitSet.set(columnIndex);
  }

  public boolean isNull(String columnName) {
    return isNull(this.schema.getColumnIndex(columnName));
  }

  public boolean isNull(int columnIndex) {
    checkColumnExists(schema.getColumnByIndex(columnIndex));
    return schema.getColumnByIndex(columnIndex).isNullable() && isSetToNull(columnIndex);
  }


  public void addObject(String columnName, Object val) {
    addObject(this.schema.getColumnIndex(columnName), val);
  }


  public void addObject(int columnIndex, Object val) {
    checkNotFrozen();
    ColumnSchema col = schema.getColumnByIndex(columnIndex);
    checkColumnExists(col);
    maxChangedIndex = Math.max(maxChangedIndex, columnIndex);
    try {
      if (val == null) {
        setNull(columnIndex);
        return;
      }
      switch (col.getType()) {
        case BOOL:
          addBoolean(columnIndex, (Boolean) val);
          break;
        case INT8:
          addByte(columnIndex, (Byte) val);
          break;
        case INT16:
          addShort(columnIndex, (Short) val);
          break;
        case INT32:
          addInt(columnIndex, (Integer) val);
          break;
        case INT64:
          addLong(columnIndex, (Long) val);
          break;
        case UNIXTIME_MICROS:
          if (val instanceof Timestamp) {
            addTimestamp(columnIndex, (Timestamp) val);
          } else {
            addLong(columnIndex, (Long) val);
          }
          break;
        case FLOAT:
          addFloat(columnIndex, (Float) val);
          break;
        case DOUBLE:
          addDouble(columnIndex, (Double) val);
          break;
        case STRING:
          addString(columnIndex, (String) val);
          break;
        case VARCHAR:
          addVarchar(columnIndex, (String) val);
          break;
        case DATE:
          if (val instanceof Integer) {
            val = DateUtil.epochDaysToSqlDate((int) val);
          }
          addDate(columnIndex, (Date) val);
          break;
        case BINARY:
          if (val instanceof byte[]) {
            addBinary(columnIndex, (byte[]) val);
          } else {
            addBinary(columnIndex, (ByteBuffer) val);
          }
          break;
        case DECIMAL:
          addDecimal(columnIndex, (BigDecimal) val);
          break;
        default:
          throw new IllegalArgumentException("Unsupported column type: " + col.getType());
      }
    } catch (ClassCastException e) {
      throw new IllegalArgumentException(
          "Value type does not match column type " + col.getType() +
              " for column " + col.getName());
    }
  }

  public Object getObject(String columnName) {
    return getObject(this.schema.getColumnIndex(columnName));
  }


  public Object getObject(int columnIndex) {
    checkColumnExists(schema.getColumnByIndex(columnIndex));
    if (isNull(columnIndex) || !isSet(columnIndex)) {
      return null;
    }
    Type type = schema.getColumnByIndex(columnIndex).getType();
    switch (type) {
      case BOOL: return getBoolean(columnIndex);
      case INT8: return getByte(columnIndex);
      case INT16: return getShort(columnIndex);
      case INT32: return getInt(columnIndex);
      case INT64: return getLong(columnIndex);
      case DATE: return getDate(columnIndex);
      case UNIXTIME_MICROS: return getTimestamp(columnIndex);
      case FLOAT: return getFloat(columnIndex);
      case DOUBLE: return getDouble(columnIndex);
      case VARCHAR: return getVarchar(columnIndex);
      case STRING: return getString(columnIndex);
      case BINARY: return getBinaryCopy(columnIndex);
      case DECIMAL: return getDecimal(columnIndex);
      default: throw new UnsupportedOperationException("Unsupported type: " + type);
    }
  }


  private void checkColumn(ColumnSchema column, Type... types) {
    checkColumnExists(column);
    for (Type type : types) {
      if (column.getType().equals(type)) {
        return;
      }
    }
    throw new IllegalArgumentException(String.format("%s isn't %s, it's %s", column.getName(),
        Arrays.toString(types), column.getType().getName()));
  }


  private void checkColumnExists(ColumnSchema column) {
    if (column == null) {
      throw new IllegalArgumentException("Column name isn't present in the table's schema");
    }
  }


  private void checkValue(int columnIndex) {
    if (!isSet(columnIndex)) {
      throw new IllegalArgumentException("Column value is not set");
    }

    if (isNull(columnIndex)) {
      throw new IllegalArgumentException("Column value is null");
    }
  }


  private void checkNotFrozen() {
    if (frozen) {
      throw new IllegalStateException("This row was already applied and cannot be modified.");
    }
  }

  private int getPositionInRowAllocAndSetBitSet(int columnIndex) {
    columnsBitSet.set(columnIndex);
    return schema.getColumnOffset(columnIndex);
  }


  public boolean isSet(String columnName) {
    return isSet(this.schema.getColumnIndex(columnName));
  }


  public boolean isSet(int columnIndex) {
    checkColumnExists(schema.getColumnByIndex(columnIndex));
    return this.columnsBitSet.get(columnIndex);
  }


  boolean isSetToNull(int column) {
    if (this.nullsBitSet == null) {
      return false;
    }
    return this.nullsBitSet.get(column);
  }


  public byte[] encodePrimaryKey() {
    return KeyEncoder.encodePrimaryKey(this);
  }

  @Override
  public String toString() {
    int numCols = schema.getColumnCount();
    StringBuilder sb = new StringBuilder();
    sb.append('(');
    boolean first = true;
    for (int idx = 0; idx < numCols; ++idx) {
      if (!columnsBitSet.get(idx)) {
        continue;
      }

      if (first) {
        first = false;
      } else {
        sb.append(", ");
      }

      ColumnSchema col = schema.getColumnByIndex(idx);
      sb.append(col.getType().getName());
      if (col.getTypeAttributes() != null) {
        sb.append(col.getTypeAttributes().toStringForType(col.getType()));
      }
      sb.append(' ');
      sb.append(col.getName());
      sb.append('=');

      appendCellValueDebugString(idx, sb);
    }
    sb.append(')');
    return sb.toString();
  }


  public String stringifyRowKey() {
    int numRowKeys = schema.getPrimaryKeyColumnCount();
    List idxs = new ArrayList<>(numRowKeys);
    for (int i = 0; i < numRowKeys; i++) {
      idxs.add(i);
    }

    StringBuilder sb = new StringBuilder();
    sb.append("(");
    appendDebugString(idxs, sb);
    sb.append(")");
    return sb.toString();
  }


  void appendDebugString(List idxs, StringBuilder sb) {
    boolean first = true;
    for (int idx : idxs) {
      if (first) {
        first = false;
      } else {
        sb.append(", ");
      }

      ColumnSchema col = schema.getColumnByIndex(idx);

      sb.append(col.getType().getName());
      sb.append(' ');
      sb.append(col.getName());
      sb.append('=');

      appendCellValueDebugString(idx, sb);
    }
  }


  void appendShortDebugString(List idxs, StringBuilder sb) {
    boolean first = true;
    for (int idx : idxs) {
      if (first) {
        first = false;
      } else {
        sb.append(", ");
      }
      appendCellValueDebugString(idx, sb);
    }
  }


  void appendCellValueDebugString(Integer idx, StringBuilder sb) {
    ColumnSchema col = schema.getColumnByIndex(idx);
    Preconditions.checkState(columnsBitSet.get(idx), "Column %s is not set", col.getName());

    if (nullsBitSet != null && nullsBitSet.get(idx)) {
      sb.append("NULL");
      return;
    }

    switch (col.getType()) {
      case BOOL:
        sb.append(Bytes.getBoolean(rowAlloc, schema.getColumnOffset(idx)));
        return;
      case INT8:
        sb.append(Bytes.getByte(rowAlloc, schema.getColumnOffset(idx)));
        return;
      case INT16:
        sb.append(Bytes.getShort(rowAlloc, schema.getColumnOffset(idx)));
        return;
      case INT32:
        sb.append(Bytes.getInt(rowAlloc, schema.getColumnOffset(idx)));
        return;
      case INT64:
        sb.append(Bytes.getLong(rowAlloc, schema.getColumnOffset(idx)));
        return;
      case DATE:
        sb.append(DateUtil.epochDaysToDateString(
            Bytes.getInt(rowAlloc, schema.getColumnOffset(idx))));
        return;
      case UNIXTIME_MICROS:
        sb.append(TimestampUtil.timestampToString(
            Bytes.getLong(rowAlloc, schema.getColumnOffset(idx))));
        return;
      case FLOAT:
        sb.append(Bytes.getFloat(rowAlloc, schema.getColumnOffset(idx)));
        return;
      case DOUBLE:
        sb.append(Bytes.getDouble(rowAlloc, schema.getColumnOffset(idx)));
        return;
      case DECIMAL:
        ColumnTypeAttributes typeAttributes = col.getTypeAttributes();
        sb.append(Bytes.getDecimal(rowAlloc, schema.getColumnOffset(idx),
            typeAttributes.getPrecision(), typeAttributes.getScale()));
        return;
      case VARCHAR:
      case BINARY:
      case STRING:
        ByteBuffer value = getVarLengthData().get(this.schema.getVarLengthColumnOffsetToAllVarCharColumns(idx)).duplicate();
        value.reset(); // Make sure we start at the beginning.
        byte[] data = new byte[value.limit() - value.position()];
        value.get(data);
        if (col.getType() == Type.STRING || col.getType() == Type.VARCHAR) {
          sb.append('"');
          StringUtil.appendEscapedSQLString(Bytes.getString(data), sb);
          sb.append('"');
        } else {
          sb.append(Bytes.pretty(data));
        }
        return;
      default:
        throw new RuntimeException("unreachable");
    }
  }


  void setMin(int index) {
    ColumnSchema column = schema.getColumnByIndex(index);
    Type type = column.getType();
    switch (type) {
      case BOOL:
        addBoolean(index, false);
        break;
      case INT8:
        addByte(index, Byte.MIN_VALUE);
        break;
      case INT16:
        addShort(index, Short.MIN_VALUE);
        break;
      case INT32:
        addInt(index, Integer.MIN_VALUE);
        break;
      case DATE:
        addDate(index, DateUtil.epochDaysToSqlDate(DateUtil.MIN_DATE_VALUE));
        break;
      case INT64:
      case UNIXTIME_MICROS:
        addLong(index, Long.MIN_VALUE);
        break;
      case FLOAT:
        addFloat(index, -Float.MAX_VALUE);
        break;
      case DOUBLE:
        addDouble(index, -Double.MAX_VALUE);
        break;
      case DECIMAL:
        ColumnTypeAttributes typeAttributes = column.getTypeAttributes();
        addDecimal(index,
            DecimalUtil.minValue(typeAttributes.getPrecision(), typeAttributes.getScale()));
        break;
      case STRING:
        addStringUtf8(index, EMPTY_ARRAY);
        break;
      case BINARY:
        addBinary(index, EMPTY_ARRAY);
        break;
      case VARCHAR:
        addVarchar(index, "");
        break;
      default:
        throw new RuntimeException("unreachable");
    }
  }


  void setRaw(int index, byte[] value) {
    ColumnSchema column = schema.getColumnByIndex(index);
    Type type = column.getType();
    switch (type) {
      case BOOL:
      case INT8:
      case INT16:
      case INT32:
      case INT64:
      case DATE:
      case UNIXTIME_MICROS:
      case FLOAT:
      case DOUBLE:
      case DECIMAL: {
        Preconditions.checkArgument(value.length == column.getTypeSize());
        System.arraycopy(value, 0, rowAlloc,
            getPositionInRowAllocAndSetBitSet(index), value.length);
        break;
      }
      case VARCHAR:
      case STRING:
      case BINARY: {
        addVarLengthData(index, value);
        break;
      }
      default:
        throw new RuntimeException("unreachable");
    }
  }


  boolean incrementColumn(int index) {
    ColumnSchema column = schema.getColumnByIndex(index);
    Type type = column.getType();
    Preconditions.checkState(isSet(index));
    int offset = schema.getColumnOffset(index);
    switch (type) {
      case BOOL: {
        boolean isFalse = rowAlloc[offset] == 0;
        rowAlloc[offset] = 1;
        return isFalse;
      }
      case INT8: {
        byte existing = rowAlloc[offset];
        if (existing == Byte.MAX_VALUE) {
          return false;
        }
        rowAlloc[offset] = (byte) (existing + 1);
        return true;
      }
      case INT16: {
        short existing = Bytes.getShort(rowAlloc, offset);
        if (existing == Short.MAX_VALUE) {
          return false;
        }
        Bytes.setShort(rowAlloc, (short) (existing + 1), offset);
        return true;
      }
      case INT32: {
        int existing = Bytes.getInt(rowAlloc, offset);
        if (existing == Integer.MAX_VALUE) {
          return false;
        }
        Bytes.setInt(rowAlloc, existing + 1, offset);
        return true;
      }
      case DATE: {
        int existing = Bytes.getInt(rowAlloc, offset);
        if (existing == DateUtil.MAX_DATE_VALUE) {
          return false;
        }
        Bytes.setInt(rowAlloc, existing + 1, offset);
        return true;
      }
      case INT64:
      case UNIXTIME_MICROS: {
        long existing = Bytes.getLong(rowAlloc, offset);
        if (existing == Long.MAX_VALUE) {
          return false;
        }
        Bytes.setLong(rowAlloc, existing + 1, offset);
        return true;
      }
      case FLOAT: {
        float existing = Bytes.getFloat(rowAlloc, offset);
        float incremented = Math.nextAfter(existing, Float.POSITIVE_INFINITY);
        if (existing == incremented) {
          return false;
        }
        Bytes.setFloat(rowAlloc, incremented, offset);
        return true;
      }
      case DOUBLE: {
        double existing = Bytes.getDouble(rowAlloc, offset);
        double incremented = Math.nextAfter(existing, Double.POSITIVE_INFINITY);
        if (existing == incremented) {
          return false;
        }
        Bytes.setDouble(rowAlloc, incremented, offset);
        return true;
      }
      case DECIMAL: {
        int precision = column.getTypeAttributes().getPrecision();
        int scale = column.getTypeAttributes().getScale();
        BigDecimal existing = Bytes.getDecimal(rowAlloc, offset, precision, scale);
        BigDecimal max = DecimalUtil.maxValue(precision, scale);
        if (existing.equals(max)) {
          return false;
        }
        BigDecimal smallest = DecimalUtil.smallestValue(scale);
        Bytes.setBigDecimal(rowAlloc, existing.add(smallest), precision, offset);
        return true;
      }
      case VARCHAR:
      case STRING:
      case BINARY: {
        ByteBuffer data = varLengthData.get(this.schema.getVarLengthColumnOffsetToAllVarCharColumns(index));
        data.reset();
        int len = data.limit() - data.position();
        byte[] incremented = new byte[len + 1];
        System.arraycopy(data.array(), data.arrayOffset() + data.position(), incremented, 0, len);
        addVarLengthData(index, incremented);
        return true;
      }
      default:
        throw new RuntimeException("unreachable");
    }
  }

  static boolean isIncremented(PartialRow lower, PartialRow upper, List indexes) {
    boolean equals = false;
    ListIterator iter = indexes.listIterator(indexes.size());
    while (iter.hasPrevious()) {
      int index = iter.previous();
      if (equals) {
        if (isCellEqual(lower, upper, index)) {
          continue;
        }
        return false;
      }

      if (!lower.isSet(index) && !upper.isSet(index)) {
        continue;
      }
      if (!isCellIncremented(lower, upper, index)) {
        return false;
      }
      equals = true;
    }
    return equals;
  }


  private static boolean isCellEqual(PartialRow a, PartialRow b, int index) {
    Preconditions.checkArgument(a.getSchema().equals(b.getSchema()));
    Preconditions.checkArgument(a.getSchema().getColumnByIndex(index).isKey());
    Preconditions.checkArgument(a.isSet(index));
    Preconditions.checkArgument(b.isSet(index));

    ColumnSchema column = a.getSchema().getColumnByIndex(index);
    Type type = column.getType();
    int offset = a.getSchema().getColumnOffset(index);

    switch (type) {
      case BOOL:
      case INT8:
        return a.rowAlloc[offset] == b.rowAlloc[offset];
      case INT16:
        return Bytes.getShort(a.rowAlloc, offset) == Bytes.getShort(b.rowAlloc, offset);
      case DATE:
      case INT32:
        return Bytes.getInt(a.rowAlloc, offset) == Bytes.getInt(b.rowAlloc, offset);
      case INT64:
      case UNIXTIME_MICROS:
        return Bytes.getLong(a.rowAlloc, offset) == Bytes.getLong(b.rowAlloc, offset);
      case FLOAT:
        return Bytes.getFloat(a.rowAlloc, offset) == Bytes.getFloat(b.rowAlloc, offset);
      case DOUBLE:
        return Bytes.getDouble(a.rowAlloc, offset) == Bytes.getDouble(b.rowAlloc, offset);
      case DECIMAL:
        ColumnTypeAttributes typeAttributes = column.getTypeAttributes();
        int precision = typeAttributes.getPrecision();
        int scale = typeAttributes.getScale();
        return Bytes.getDecimal(a.rowAlloc, offset, precision, scale)
            .equals(Bytes.getDecimal(b.rowAlloc, offset, precision, scale));
      case VARCHAR:
      case STRING:
      case BINARY: {
        ByteBuffer dataA = a.varLengthData.get(a.schema.getVarLengthColumnOffsetToAllVarCharColumns(index)).duplicate();
        ByteBuffer dataB = b.varLengthData.get(b.schema.getVarLengthColumnOffsetToAllVarCharColumns(index)).duplicate();
        dataA.reset();
        dataB.reset();
        int lenA = dataA.limit() - dataA.position();
        int lenB = dataB.limit() - dataB.position();

        if (lenA != lenB) {
          return false;
        }
        for (int i = 0; i < lenA; i++) {
          if (dataA.get(dataA.position() + i) != dataB.get(dataB.position() + i)) {
            return false;
          }
        }
        return true;
      }
      default:
        throw new RuntimeException("unreachable");
    }
  }

  private static boolean isCellIncremented(PartialRow lower, PartialRow upper, int index) {
    Preconditions.checkArgument(lower.getSchema().equals(upper.getSchema()));
    Preconditions.checkArgument(lower.getSchema().getColumnByIndex(index).isKey());
    Preconditions.checkArgument(lower.isSet(index));
    Preconditions.checkArgument(upper.isSet(index));

    ColumnSchema column = lower.getSchema().getColumnByIndex(index);
    Type type = column.getType();
    int offset = lower.getSchema().getColumnOffset(index);

    switch (type) {
      case BOOL:
        return lower.rowAlloc[offset] + 1 == upper.rowAlloc[offset];
      case INT8: {
        byte val = lower.rowAlloc[offset];
        return val != Byte.MAX_VALUE && val + 1 == upper.rowAlloc[offset];
      }
      case INT16: {
        short val = Bytes.getShort(lower.rowAlloc, offset);
        return val != Short.MAX_VALUE && val + 1 == Bytes.getShort(upper.rowAlloc, offset);
      }
      case INT32: {
        int val = Bytes.getInt(lower.rowAlloc, offset);
        return val != Integer.MAX_VALUE && val + 1 == Bytes.getInt(upper.rowAlloc, offset);
      }
      case DATE: {
        int val = Bytes.getInt(lower.rowAlloc, offset);
        return val != DateUtil.MAX_DATE_VALUE && val + 1 == Bytes.getInt(upper.rowAlloc, offset);
      }
      case INT64:
      case UNIXTIME_MICROS: {
        long val = Bytes.getLong(lower.rowAlloc, offset);
        return val != Long.MAX_VALUE && val + 1 == Bytes.getLong(upper.rowAlloc, offset);
      }
      case FLOAT: {
        float val = Bytes.getFloat(lower.rowAlloc, offset);
        return val != Float.POSITIVE_INFINITY &&
               Math.nextAfter(val, Float.POSITIVE_INFINITY) ==
                   Bytes.getFloat(upper.rowAlloc, offset);
      }
      case DOUBLE: {
        double val = Bytes.getDouble(lower.rowAlloc, offset);
        return val != Double.POSITIVE_INFINITY &&
               Math.nextAfter(val, Double.POSITIVE_INFINITY) ==
                   Bytes.getDouble(upper.rowAlloc, offset);
      }
      case DECIMAL: {
        ColumnTypeAttributes typeAttributes = column.getTypeAttributes();
        int precision = typeAttributes.getPrecision();
        int scale = typeAttributes.getScale();
        BigDecimal val = Bytes.getDecimal(lower.rowAlloc, offset, precision, scale);
        BigDecimal smallestVal = DecimalUtil.smallestValue(scale);
        return val.add(smallestVal).equals(
                Bytes.getDecimal(upper.rowAlloc, offset, precision, scale));
      }
      case VARCHAR:
      case STRING:
      case BINARY: {
        // Check that b is 1 byte bigger than a, the extra byte is 0, and the other bytes are equal.
        ByteBuffer dataA = lower.varLengthData.get(lower.schema.getVarLengthColumnOffsetToAllVarCharColumns(index)).duplicate();
        ByteBuffer dataB = upper.varLengthData.get(upper.schema.getVarLengthColumnOffsetToAllVarCharColumns(index)).duplicate();
        dataA.reset();
        dataB.reset();
        int lenA = dataA.limit() - dataA.position();
        int lenB = dataB.limit() - dataB.position();

        if (lenA == Integer.MAX_VALUE ||
            lenA + 1 != lenB ||
            dataB.get(dataB.limit() - 1) != 0) {
          return false;
        }

        for (int i = 0; i < lenA; i++) {
          if (dataA.get(dataA.position() + i) != dataB.get(dataB.position() + i)) {
            return false;
          }
        }
        return true;
      }
      default:
        throw new RuntimeException("unreachable");
    }
  }

  public Schema getSchema() {
    return schema;
  }


  public byte[] getRowAlloc() {
    return rowAlloc;
  }


  BitSet getColumnsBitSet() {
    return columnsBitSet;
  }


  BitSet getNullsBitSet() {
    return nullsBitSet;
  }


  void freeze() {
    this.frozen = true;
  }

  public void reset() {
    this.columnsBitSet.clear();
    if (this.nullsBitSet != null) {
      this.nullsBitSet.clear();
    }
    Arrays.fill(this.rowAlloc, (byte) 0);
    for (ByteBuffer buffer : this.varLengthData) {
      buffer.clear();
    }

    // mark max changed index.
    this.maxChangedIndex = -1;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy