Skip to content
Open
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
30 changes: 30 additions & 0 deletions table/src/main/java/tech/ydb/table/values/DecimalValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,36 @@ public ValueProtos.Value toPb() {
return ProtoValue.fromDecimal(high, low);
}

@Override
public int compareTo(Value<?> other) {
if (other == null) {
throw new NullPointerException("Cannot compare with null value");
}

if (other instanceof OptionalValue) {
OptionalValue optional = (OptionalValue) other;
if (!optional.isPresent()) {
throw new NullPointerException("Cannot compare value " + this + " with NULL");
}
return compareTo(optional.get());
}

if (!(other instanceof DecimalValue)) {
throw new IllegalArgumentException("Cannot compare DecimalValue with " + other.getClass().getSimpleName());
}

DecimalValue decimal = (DecimalValue) other;

// Fast way to compare decimals with the same scale or with special values
boolean isSpecial = isNan() || isInf() || isNegativeInf();
boolean otherIsSpecial = decimal.isNan() || decimal.isInf() || decimal.isNegativeInf();
if (isSpecial || otherIsSpecial || (getType().getScale() == decimal.getType().getScale())) {
return high != decimal.high ? Long.compare(high, decimal.high) : Long.compare(low, decimal.low);
}

return toBigDecimal().compareTo(decimal.toBigDecimal());
}

/**
* Write long to a big-endian buffer.
*/
Expand Down
43 changes: 43 additions & 0 deletions table/src/main/java/tech/ydb/table/values/DictValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.annotation.Nullable;

Expand Down Expand Up @@ -116,4 +117,46 @@ public ValueProtos.Value toPb() {
}
return builder.build();
}

@Override
public int compareTo(Value<?> other) {
if (other == null) {
throw new NullPointerException("Cannot compare with null value");
}

if (other instanceof OptionalValue) {
OptionalValue optional = (OptionalValue) other;
if (!optional.isPresent()) {
throw new NullPointerException("Cannot compare value " + this + " with NULL");
}
return compareTo(optional.get());
}

if (!type.equals(other.getType())) {
throw new IllegalArgumentException("Cannot compare value " + type + " with " + other.getType());
}

DictValue otherDict = (DictValue) other;

// Sort entries by keys
Set<Value<?>> keys = new TreeSet<>();
keys.addAll(items.keySet());
keys.addAll(otherDict.keySet());

for (Value<?> key: keys) {
if (!otherDict.items.containsKey(key)) {
return 1;
}
if (!items.containsKey(key)) {
return -1;
}

int valueComparison = items.get(key).compareTo(otherDict.items.get(key));
if (valueComparison != 0) {
return valueComparison;
}
}

return 0;
}
}
27 changes: 27 additions & 0 deletions table/src/main/java/tech/ydb/table/values/ListValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,31 @@ public ValueProtos.Value toPb() {
}
return builder.build();
}

@Override
public int compareTo(Value<?> other) {
if (other == null) {
throw new NullPointerException("Cannot compare with null value");
}

if (other instanceof OptionalValue) {
OptionalValue optional = (OptionalValue) other;
if (!optional.isPresent()) {
throw new NullPointerException("Cannot compare value " + this + " with NULL");
}
return compareTo(optional.get());
}

ListValue list = (ListValue) other;

int minLength = Math.min(items.length, list.items.length);
for (int i = 0; i < minLength; i++) {
int itemComparison = items[i].compareTo(list.items[i]);
if (itemComparison != 0) {
return itemComparison;
}
}

return Integer.compare(items.length, list.items.length);
}
}
22 changes: 22 additions & 0 deletions table/src/main/java/tech/ydb/table/values/NullValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,26 @@ public NullType getType() {
public ValueProtos.Value toPb() {
return ProtoValue.nullValue();
}

@Override
public int compareTo(Value<?> other) {
if (other == null) {
throw new NullPointerException("Cannot compare with null value");
}

if (other instanceof OptionalValue) {
OptionalValue optional = (OptionalValue) other;
if (!optional.isPresent()) {
return 0;
}
return compareTo(optional.get());
}

if (other instanceof VoidValue || other instanceof NullValue) {
// All VoidValue and NullValue are equal
return 0;
}

throw new IllegalArgumentException("Cannot compare value " + getType() + " with " + other.getType());
}
}
24 changes: 24 additions & 0 deletions table/src/main/java/tech/ydb/table/values/OptionalValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,28 @@ public ValueProtos.Value toPb() {

return ProtoValue.optional();
}

@Override
public int compareTo(Value<?> other) {
if (other == null) {
throw new NullPointerException("Cannot compare with null value");
}

if (other instanceof OptionalValue) {
OptionalValue optional = (OptionalValue) other;
if (optional.value == null) {
if (value == null) {
return 0;
}
throw new NullPointerException("Cannot compare value " + value + " with NULL");
}
return compareTo(optional.value);
}

if (value == null) {
throw new NullPointerException("Cannot compare NULL with value " + other);
}

return value.compareTo(other);
}
}
112 changes: 112 additions & 0 deletions table/src/main/java/tech/ydb/table/values/PrimitiveValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.protobuf.UnsafeByteOperations;

import tech.ydb.proto.ValueProtos;
import tech.ydb.table.utils.LittleEndian;
import tech.ydb.table.values.proto.ProtoValue;


Expand Down Expand Up @@ -423,6 +424,117 @@ private static void checkType(PrimitiveType expected, PrimitiveType actual) {
}
}

@Override
public int compareTo(Value<?> other) {
if (other == null) {
throw new NullPointerException("Cannot compare with null value");
}

if (other instanceof OptionalValue) {
OptionalValue optional = (OptionalValue) other;
if (!optional.isPresent()) {
throw new NullPointerException("Cannot compare value " + this + " with NULL");
}
return compareTo(optional.get());
}

if (!getType().equals(other.getType())) {
throw new IllegalArgumentException("Cannot compare value " + getType() + " with " + other.getType());
}

PrimitiveValue otherValue = (PrimitiveValue) other;

// Compare based on the actual primitive type
switch (getType()) {
case Bool:
return Boolean.compare(getBool(), otherValue.getBool());
case Int8:
return Byte.compare(getInt8(), otherValue.getInt8());
case Uint8:
return Integer.compare(getUint8(), otherValue.getUint8());
case Int16:
return Short.compare(getInt16(), otherValue.getInt16());
case Uint16:
return Integer.compare(getUint16(), otherValue.getUint16());
case Int32:
return Integer.compare(getInt32(), otherValue.getInt32());
case Uint32:
return Long.compare(getUint32(), otherValue.getUint32());
case Int64:
return Long.compare(getInt64(), otherValue.getInt64());
case Uint64:
return Long.compareUnsigned(getUint64(), otherValue.getUint64());
case Float:
return Float.compare(getFloat(), otherValue.getFloat());
case Double:
return Double.compare(getDouble(), otherValue.getDouble());
case Bytes:
return compareArrays(getBytesUnsafe(), otherValue.getBytesUnsafe());
case Yson:
return compareArrays(getYsonUnsafe(), otherValue.getYsonUnsafe());
case Text:
return getText().compareTo(otherValue.getText());
case Json:
return getJson().compareTo(otherValue.getJson());
case JsonDocument:
return getJsonDocument().compareTo(otherValue.getJsonDocument());
case Uuid:
return compareUUID(this, otherValue);
case Date:
return getDate().compareTo(otherValue.getDate());
case Date32:
return getDate32().compareTo(otherValue.getDate32());
case Datetime:
return getDatetime().compareTo(otherValue.getDatetime());
case Datetime64:
return getDatetime64().compareTo(otherValue.getDatetime64());
case Timestamp:
return getTimestamp().compareTo(otherValue.getTimestamp());
case Timestamp64:
return getTimestamp64().compareTo(otherValue.getTimestamp64());
case Interval:
return getInterval().compareTo(otherValue.getInterval());
case Interval64:
return getInterval64().compareTo(otherValue.getInterval64());
case TzDate:
return getTzDate().compareTo(otherValue.getTzDate());
case TzDatetime:
return getTzDatetime().compareTo(otherValue.getTzDatetime());
case TzTimestamp:
return getTzTimestamp().compareTo(otherValue.getTzTimestamp());
default:
throw new UnsupportedOperationException("Comparison not supported for type: " + getType());
}
}

@SuppressWarnings("deprecation")
private static int compareUUID(PrimitiveValue a, PrimitiveValue b) {
long ah = LittleEndian.bswap(a.getUuidHigh());
long bh = LittleEndian.bswap(b.getUuidHigh());
long al = LittleEndian.bswap(a.getUuidLow());
long bl = LittleEndian.bswap(b.getUuidLow());

return (al != bl) ? Long.compareUnsigned(al, bl) : Long.compareUnsigned(ah, bh);
}

private static int compareArrays(byte[] a, byte[] b) {
if (a == b) {
return 0;
}

int i = 0;
int len = Math.min(a.length, b.length);
while (i < len && a[i] == b[i]) {
i++;
}

if (i < len) {
return Byte.compare(a[i], b[i]);
}

return a.length - b.length;
}

// -- implementations --

private static final class Bool extends PrimitiveValue {
Expand Down
29 changes: 29 additions & 0 deletions table/src/main/java/tech/ydb/table/values/StructValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,35 @@ public ValueProtos.Value toPb() {
return builder.build();
}

@Override
public int compareTo(Value<?> other) {
if (other == null) {
throw new NullPointerException("Cannot compare with null value");
}

if (other instanceof OptionalValue) {
OptionalValue optional = (OptionalValue) other;
if (!optional.isPresent()) {
throw new NullPointerException("Cannot compare value " + this + " with NULL");
}
return compareTo(optional.get());
}

if (!type.equals(other.getType())) {
throw new IllegalArgumentException("Cannot compare value " + type + " with " + other.getType());
}

StructValue struct = (StructValue) other;
for (int i = 0; i < type.getMembersCount(); i++) {
int memberComparison = members[i].compareTo(struct.members[i]);
if (memberComparison != 0) {
return memberComparison;
}
}

return 0;
}

private static StructValue newStruct(String[] names, Value<?>[] values) {
Arrays2.sortBothByFirst(names, values);
final Type[] types = new Type[values.length];
Expand Down
30 changes: 30 additions & 0 deletions table/src/main/java/tech/ydb/table/values/TupleValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,34 @@ private static TupleValue fromArray(Value<?>... items) {
}
return new TupleValue(TupleType.ofOwn(types), items);
}

@Override
public int compareTo(Value<?> other) {
if (other == null) {
throw new NullPointerException("Cannot compare with null value");
}

if (other instanceof OptionalValue) {
OptionalValue optional = (OptionalValue) other;
if (!optional.isPresent()) {
throw new NullPointerException("Cannot compare value " + this + " with NULL");
}
return compareTo(optional.get());
}

if (!type.equals(other.getType())) {
throw new IllegalArgumentException("Cannot compare value " + type + " with " + other.getType());
}

TupleValue otherTuple = (TupleValue) other;

for (int i = 0; i < getType().getElementsCount(); i++) {
int itemComparison = items[i].compareTo(otherTuple.items[i]);
if (itemComparison != 0) {
return itemComparison;
}
}

return 0;
}
}
2 changes: 1 addition & 1 deletion table/src/main/java/tech/ydb/table/values/Value.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @author Sergey Polovko
* @param <T> type of value
*/
public interface Value<T extends Type> extends Serializable {
public interface Value<T extends Type> extends Serializable, Comparable<Value<?>> {

Value<?>[] EMPTY_ARRAY = {};

Expand Down
Loading