http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/DecimalType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/DecimalType.java b/src/java/org/apache/cassandra/db/marshal/DecimalType.java index f1586e0..b98bf00 100644 --- a/src/java/org/apache/cassandra/db/marshal/DecimalType.java +++ b/src/java/org/apache/cassandra/db/marshal/DecimalType.java @@ -18,6 +18,8 @@ package org.apache.cassandra.db.marshal; import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; @@ -29,7 +31,7 @@ import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; -public class DecimalType extends AbstractType<BigDecimal> +public class DecimalType extends NumberType<BigDecimal> { public static final DecimalType instance = new DecimalType(); @@ -40,6 +42,12 @@ public class DecimalType extends AbstractType<BigDecimal> return true; } + @Override + public boolean isFloatingPoint() + { + return true; + } + public int compareCustom(ByteBuffer o1, ByteBuffer o2) { if (!o1.hasRemaining() || !o2.hasRemaining()) @@ -95,4 +103,70 @@ public class DecimalType extends AbstractType<BigDecimal> { return DecimalSerializer.instance; } + + @Override + protected int toInt(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected float toFloat(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected long toLong(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected double toDouble(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected BigInteger toBigInteger(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected BigDecimal toBigDecimal(ByteBuffer value) + { + return compose(value); + } + + public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigDecimal(left).add(rightType.toBigDecimal(right), MathContext.DECIMAL128)); + } + + public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigDecimal(left).subtract(rightType.toBigDecimal(right), MathContext.DECIMAL128)); + } + + public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigDecimal(left).multiply(rightType.toBigDecimal(right), MathContext.DECIMAL128)); + } + + public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigDecimal(left).divide(rightType.toBigDecimal(right), MathContext.DECIMAL128)); + } + + public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigDecimal(left).remainder(rightType.toBigDecimal(right), MathContext.DECIMAL128)); + } + + public ByteBuffer negate(ByteBuffer input) + { + return decompose(toBigDecimal(input).negate()); + } }
http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/DoubleType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/DoubleType.java b/src/java/org/apache/cassandra/db/marshal/DoubleType.java index d2309ee..b72d3e9 100644 --- a/src/java/org/apache/cassandra/db/marshal/DoubleType.java +++ b/src/java/org/apache/cassandra/db/marshal/DoubleType.java @@ -28,7 +28,7 @@ import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; -public class DoubleType extends AbstractType<Double> +public class DoubleType extends NumberType<Double> { public static final DoubleType instance = new DoubleType(); @@ -39,6 +39,12 @@ public class DoubleType extends AbstractType<Double> return true; } + @Override + public boolean isFloatingPoint() + { + return true; + } + public int compareCustom(ByteBuffer o1, ByteBuffer o2) { if (!o1.hasRemaining() || !o2.hasRemaining()) @@ -53,17 +59,14 @@ public class DoubleType extends AbstractType<Double> if (source.isEmpty()) return ByteBufferUtil.EMPTY_BYTE_BUFFER; - Double d; try { - d = Double.valueOf(source); + return decompose(Double.valueOf(source)); } catch (NumberFormatException e1) { throw new MarshalException(String.format("Unable to make double from '%s'", source), e1); } - - return decompose(d); } @Override @@ -100,8 +103,62 @@ public class DoubleType extends AbstractType<Double> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 8; } + + @Override + protected int toInt(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected float toFloat(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected long toLong(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected double toDouble(ByteBuffer value) + { + return ByteBufferUtil.toDouble(value); + } + + public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toDouble(left) + rightType.toDouble(right)); + } + + public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toDouble(left) - rightType.toDouble(right)); + } + + public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toDouble(left) * rightType.toDouble(right)); + } + + public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toDouble(left) / rightType.toDouble(right)); + } + + public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toDouble(left) % rightType.toDouble(right)); + } + + public ByteBuffer negate(ByteBuffer input) + { + return ByteBufferUtil.bytes(-toDouble(input)); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/EmptyType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/EmptyType.java b/src/java/org/apache/cassandra/db/marshal/EmptyType.java index c653084..87b3a7f 100644 --- a/src/java/org/apache/cassandra/db/marshal/EmptyType.java +++ b/src/java/org/apache/cassandra/db/marshal/EmptyType.java @@ -78,7 +78,7 @@ public class EmptyType extends AbstractType<Void> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 0; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/FloatType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/FloatType.java b/src/java/org/apache/cassandra/db/marshal/FloatType.java index 5445bbc..33d8344 100644 --- a/src/java/org/apache/cassandra/db/marshal/FloatType.java +++ b/src/java/org/apache/cassandra/db/marshal/FloatType.java @@ -29,7 +29,7 @@ import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; -public class FloatType extends AbstractType<Float> +public class FloatType extends NumberType<Float> { public static final FloatType instance = new FloatType(); @@ -40,6 +40,12 @@ public class FloatType extends AbstractType<Float> return true; } + @Override + public boolean isFloatingPoint() + { + return true; + } + public int compareCustom(ByteBuffer o1, ByteBuffer o2) { if (!o1.hasRemaining() || !o2.hasRemaining()) @@ -56,8 +62,7 @@ public class FloatType extends AbstractType<Float> try { - float f = Float.parseFloat(source); - return ByteBufferUtil.bytes(f); + return decompose(Float.parseFloat(source)); } catch (NumberFormatException e1) { @@ -99,8 +104,56 @@ public class FloatType extends AbstractType<Float> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 4; } + + @Override + protected int toInt(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected float toFloat(ByteBuffer value) + { + return ByteBufferUtil.toFloat(value); + } + + @Override + protected double toDouble(ByteBuffer value) + { + return toFloat(value); + } + + public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toFloat(left) + rightType.toFloat(right)); + } + + public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toFloat(left) - rightType.toFloat(right)); + } + + public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toFloat(left) * rightType.toFloat(right)); + } + + public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toFloat(left) / rightType.toFloat(right)); + } + + public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toFloat(left) % rightType.toFloat(right)); + } + + public ByteBuffer negate(ByteBuffer input) + { + return ByteBufferUtil.bytes(-toFloat(input)); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/Int32Type.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/Int32Type.java b/src/java/org/apache/cassandra/db/marshal/Int32Type.java index 1c8c93e..a66f9dc 100644 --- a/src/java/org/apache/cassandra/db/marshal/Int32Type.java +++ b/src/java/org/apache/cassandra/db/marshal/Int32Type.java @@ -22,13 +22,13 @@ import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.Constants; import org.apache.cassandra.cql3.Term; -import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.Int32Serializer; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.transport.ProtocolVersion; +import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.utils.ByteBufferUtil; -public class Int32Type extends AbstractType<Integer> +public class Int32Type extends NumberType<Integer> { public static final Int32Type instance = new Int32Type(); @@ -112,8 +112,50 @@ public class Int32Type extends AbstractType<Integer> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 4; } + + @Override + protected int toInt(ByteBuffer value) + { + return ByteBufferUtil.toInt(value); + } + + @Override + protected float toFloat(ByteBuffer value) + { + return toInt(value); + } + + public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toInt(left) + rightType.toInt(right)); + } + + public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toInt(left) - rightType.toInt(right)); + } + + public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toInt(left) * rightType.toInt(right)); + } + + public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toInt(left) / rightType.toInt(right)); + } + + public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toInt(left) % rightType.toInt(right)); + } + + public ByteBuffer negate(ByteBuffer input) + { + return ByteBufferUtil.bytes(-toInt(input)); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/IntegerType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/IntegerType.java b/src/java/org/apache/cassandra/db/marshal/IntegerType.java index 944a231..e2b8518 100644 --- a/src/java/org/apache/cassandra/db/marshal/IntegerType.java +++ b/src/java/org/apache/cassandra/db/marshal/IntegerType.java @@ -17,6 +17,7 @@ */ package org.apache.cassandra.db.marshal; +import java.math.BigDecimal; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -29,7 +30,7 @@ import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; -public final class IntegerType extends AbstractType<BigInteger> +public final class IntegerType extends NumberType<BigInteger> { public static final IntegerType instance = new IntegerType(); @@ -184,4 +185,70 @@ public final class IntegerType extends AbstractType<BigInteger> { return IntegerSerializer.instance; } + + @Override + protected int toInt(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected float toFloat(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected long toLong(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected double toDouble(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected BigInteger toBigInteger(ByteBuffer value) + { + return compose(value); + } + + @Override + protected BigDecimal toBigDecimal(ByteBuffer value) + { + return new BigDecimal(compose(value)); + } + + public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigInteger(left).add(rightType.toBigInteger(right))); + } + + public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigInteger(left).subtract(rightType.toBigInteger(right))); + } + + public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigInteger(left).multiply(rightType.toBigInteger(right))); + } + + public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigInteger(left).divide(rightType.toBigInteger(right))); + } + + public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return decompose(leftType.toBigInteger(left).remainder(rightType.toBigInteger(right))); + } + + public ByteBuffer negate(ByteBuffer input) + { + return decompose(toBigInteger(input).negate()); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java index 70767d4..de32a56 100644 --- a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java @@ -86,7 +86,7 @@ public class LexicalUUIDType extends AbstractType<UUID> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 16; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/LongType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java index c852461..ef96e2e 100644 --- a/src/java/org/apache/cassandra/db/marshal/LongType.java +++ b/src/java/org/apache/cassandra/db/marshal/LongType.java @@ -28,7 +28,7 @@ import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; -public class LongType extends AbstractType<Long> +public class LongType extends NumberType<Long> { public static final LongType instance = new LongType(); @@ -120,8 +120,56 @@ public class LongType extends AbstractType<Long> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 8; } + + @Override + protected int toInt(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected float toFloat(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + @Override + protected long toLong(ByteBuffer value) + { + return ByteBufferUtil.toLong(value); + } + + public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toLong(left) + rightType.toLong(right)); + } + + public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toLong(left) - rightType.toLong(right)); + } + + public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toLong(left) * rightType.toLong(right)); + } + + public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toLong(left) / rightType.toLong(right)); + } + + public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes(leftType.toLong(left) % rightType.toLong(right)); + } + + public ByteBuffer negate(ByteBuffer input) + { + return ByteBufferUtil.bytes(-toLong(input)); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/NumberType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/NumberType.java b/src/java/org/apache/cassandra/db/marshal/NumberType.java new file mode 100644 index 0000000..9e7697f --- /dev/null +++ b/src/java/org/apache/cassandra/db/marshal/NumberType.java @@ -0,0 +1,223 @@ +/* + * 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.cassandra.db.marshal; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; + +/** + * Base type for the numeric types. + */ +public abstract class NumberType<T extends Number> extends AbstractType<T> +{ + protected NumberType(ComparisonType comparisonType) + { + super(comparisonType); + } + + /** + * Checks if this type support floating point numbers. + * @return {@code true} if this type support floating point numbers, {@code false} otherwise. + */ + public boolean isFloatingPoint() + { + return false; + } + + /** + * Converts the specified value into a <code>BigInteger</code> if allowed. + * + * @param value the value to convert + * @return the converted value + * @throws UnsupportedOperationException if the value cannot be converted without losing precision + */ + protected BigInteger toBigInteger(ByteBuffer value) + { + return BigInteger.valueOf(toLong(value)); + } + + /** + * Converts the specified value into a <code>BigDecimal</code>. + * + * @param value the value to convert + * @return the converted value + */ + protected BigDecimal toBigDecimal(ByteBuffer value) + { + double d = toDouble(value); + + if (Double.isNaN(d)) + throw new NumberFormatException("A NaN cannot be converted into a decimal"); + + if (Double.isInfinite(d)) + throw new NumberFormatException("An infinite number cannot be converted into a decimal"); + + return BigDecimal.valueOf(d); + } + + /** + * Converts the specified value into a <code>byte</code> if allowed. + * + * @param value the value to convert + * @return the converted value + * @throws UnsupportedOperationException if the value cannot be converted without losing precision + */ + protected byte toByte(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + /** + * Converts the specified value into a <code>short</code> if allowed. + * + * @param value the value to convert + * @return the converted value + * @throws UnsupportedOperationException if the value cannot be converted without losing precision + */ + protected short toShort(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + /** + * Converts the specified value into an <code>int</code> if allowed. + * + * @param value the value to convert + * @return the converted value + * @throws UnsupportedOperationException if the value cannot be converted without losing precision + */ + protected int toInt(ByteBuffer value) + { + throw new UnsupportedOperationException(); + } + + /** + * Converts the specified value into a <code>long</code> if allowed. + * + * @param value the value to convert + * @return the converted value + * @throws UnsupportedOperationException if the value cannot be converted without losing precision + */ + protected long toLong(ByteBuffer value) + { + return toInt(value); + } + + /** + * Converts the specified value into a <code>float</code> if allowed. + * + * @param value the value to convert + * @return the converted value + * @throws UnsupportedOperationException if the value cannot be converted without losing precision + */ + protected float toFloat(ByteBuffer value) + { + return toInt(value); + } + + /** + * Converts the specified value into a <code>double</code> if allowed. + * + * @param value the value to convert + * @return the converted value + * @throws UnsupportedOperationException if the value cannot be converted without losing precision + */ + protected double toDouble(ByteBuffer value) + { + return toLong(value); + } + + /** + * Adds the left argument to the right one. + * + * @param leftType the type associated to the left argument + * @param left the left argument + * @param rightType the type associated to the right argument + * @param right the right argument + * @return the addition result + */ + public abstract ByteBuffer add(NumberType<?> leftType, + ByteBuffer left, + NumberType<?> rightType, + ByteBuffer right); + + /** + * Substracts the left argument from the right one. + * + * @param leftType the type associated to the left argument + * @param left the left argument + * @param rightType the type associated to the right argument + * @param right the right argument + * @return the substraction result + */ + public abstract ByteBuffer substract(NumberType<?> leftType, + ByteBuffer left, + NumberType<?> rightType, + ByteBuffer right); + + /** + * Multiplies the left argument with the right one. + * + * @param leftType the type associated to the left argument + * @param left the left argument + * @param rightType the type associated to the right argument + * @param right the right argument + * @return the multiplication result + */ + public abstract ByteBuffer multiply(NumberType<?> leftType, + ByteBuffer left, + NumberType<?> rightType, + ByteBuffer right); + + /** + * Divides the left argument by the right one. + * + * @param leftType the type associated to the left argument + * @param left the left argument + * @param rightType the type associated to the right argument + * @param right the right argument + * @return the division result + */ + public abstract ByteBuffer divide(NumberType<?> leftType, + ByteBuffer left, + NumberType<?> rightType, + ByteBuffer right); + + /** + * Return the remainder. + * + * @param leftType the type associated to the left argument + * @param left the left argument + * @param rightType the type associated to the right argument + * @param right the right argument + * @return the remainder + */ + public abstract ByteBuffer mod(NumberType<?> leftType, + ByteBuffer left, + NumberType<?> rightType, + ByteBuffer right); + + /** + * Negates the argument. + * + * @param input the argument to negate + * @return the negated argument + */ + public abstract ByteBuffer negate(ByteBuffer input); +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/ReversedType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/ReversedType.java b/src/java/org/apache/cassandra/db/marshal/ReversedType.java index 0eb0046..250dfdc 100644 --- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java +++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java @@ -132,7 +132,7 @@ public class ReversedType<T> extends AbstractType<T> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return baseType.valueLengthIfFixed(); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/ShortType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/ShortType.java b/src/java/org/apache/cassandra/db/marshal/ShortType.java index 7645ec6..b37a0b7 100644 --- a/src/java/org/apache/cassandra/db/marshal/ShortType.java +++ b/src/java/org/apache/cassandra/db/marshal/ShortType.java @@ -28,7 +28,7 @@ import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; -public class ShortType extends AbstractType<Short> +public class ShortType extends NumberType<Short> { public static final ShortType instance = new ShortType(); @@ -91,4 +91,53 @@ public class ShortType extends AbstractType<Short> { return ShortSerializer.instance; } + + @Override + public int valueLengthIfFixed() + { + return 2; + } + + @Override + public short toShort(ByteBuffer value) + { + return ByteBufferUtil.toShort(value); + } + + @Override + public int toInt(ByteBuffer value) + { + return toShort(value); + } + + @Override + public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes((short) (leftType.toShort(left) + rightType.toShort(right))); + } + + public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes((short) (leftType.toShort(left) - rightType.toShort(right))); + } + + public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes((short) (leftType.toShort(left) * rightType.toShort(right))); + } + + public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes((short) (leftType.toShort(left) / rightType.toShort(right))); + } + + public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right) + { + return ByteBufferUtil.bytes((short) (leftType.toShort(left) % rightType.toShort(right))); + } + + public ByteBuffer negate(ByteBuffer input) + { + return ByteBufferUtil.bytes((short) -toShort(input)); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java b/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java index 36305a3..f8e58db 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java @@ -130,7 +130,7 @@ public class TimeUUIDType extends AbstractType<UUID> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 16; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/TimestampType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TimestampType.java b/src/java/org/apache/cassandra/db/marshal/TimestampType.java index 953ae1b..ae74e2f 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimestampType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimestampType.java @@ -128,7 +128,7 @@ public class TimestampType extends AbstractType<Date> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 8; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/TupleType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TupleType.java b/src/java/org/apache/cassandra/db/marshal/TupleType.java index 60a63aa..71e946c 100644 --- a/src/java/org/apache/cassandra/db/marshal/TupleType.java +++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java @@ -101,6 +101,11 @@ public class TupleType extends AbstractType<ByteBuffer> return types; } + public boolean isTuple() + { + return true; + } + public int compareCustom(ByteBuffer o1, ByteBuffer o2) { if (!o1.hasRemaining() || !o2.hasRemaining()) http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/UUIDType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/UUIDType.java b/src/java/org/apache/cassandra/db/marshal/UUIDType.java index 9722a52..27e3360 100644 --- a/src/java/org/apache/cassandra/db/marshal/UUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/UUIDType.java @@ -171,7 +171,7 @@ public class UUIDType extends AbstractType<UUID> } @Override - protected int valueLengthIfFixed() + public int valueLengthIfFixed() { return 16; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/db/marshal/UserType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/UserType.java b/src/java/org/apache/cassandra/db/marshal/UserType.java index 176ab84..e47b7ac 100644 --- a/src/java/org/apache/cassandra/db/marshal/UserType.java +++ b/src/java/org/apache/cassandra/db/marshal/UserType.java @@ -86,6 +86,11 @@ public class UserType extends TupleType return true; } + public boolean isTuple() + { + return false; + } + @Override public boolean isMultiCell() { http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/exceptions/OperationExecutionException.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/exceptions/OperationExecutionException.java b/src/java/org/apache/cassandra/exceptions/OperationExecutionException.java new file mode 100644 index 0000000..4f9ffa4 --- /dev/null +++ b/src/java/org/apache/cassandra/exceptions/OperationExecutionException.java @@ -0,0 +1,57 @@ +/* + * 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.cassandra.exceptions; + +import java.util.List; + +import org.apache.cassandra.db.marshal.AbstractType; + +/** + * Thrown when an operation problem has occured (e.g. division by zero with integer). + */ +public final class OperationExecutionException extends RequestExecutionException +{ + + /** + * Creates a new <code>OperationExecutionException</code> for the specified operation. + * + * @param operator the operator + * @param argTypes the argument types + * @param e the original Exception + * @return a new <code>OperationExecutionException</code> for the specified operation + */ + public static OperationExecutionException create(char operator, List<AbstractType<?>> argTypes, Exception e) + { + List<String> cqlTypes = AbstractType.asCQLTypeStringList(argTypes); + return new OperationExecutionException(String.format("the operation '%s %s %s' failed: %s", + cqlTypes.get(0), + operator, + cqlTypes.get(1), + e.getMessage())); + } + + /** + * Creates an <code>OperationExecutionException</code> with the specified message. + * @param msg the error message + */ + public OperationExecutionException(String msg) + { + super(ExceptionCode.FUNCTION_FAILURE, msg); + } + +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/serializers/ByteSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/serializers/ByteSerializer.java b/src/java/org/apache/cassandra/serializers/ByteSerializer.java index 9d34fbc..8428e62 100644 --- a/src/java/org/apache/cassandra/serializers/ByteSerializer.java +++ b/src/java/org/apache/cassandra/serializers/ByteSerializer.java @@ -28,12 +28,12 @@ public class ByteSerializer implements TypeSerializer<Byte> public Byte deserialize(ByteBuffer bytes) { - return bytes == null || bytes.remaining() == 0 ? null : bytes.get(bytes.position()); + return bytes == null || bytes.remaining() == 0 ? null : ByteBufferUtil.toByte(bytes); } public ByteBuffer serialize(Byte value) { - return value == null ? ByteBufferUtil.EMPTY_BYTE_BUFFER : ByteBuffer.allocate(1).put(0, value); + return value == null ? ByteBufferUtil.EMPTY_BYTE_BUFFER : ByteBufferUtil.bytes(value); } public void validate(ByteBuffer bytes) throws MarshalException http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/src/java/org/apache/cassandra/utils/ByteBufferUtil.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/utils/ByteBufferUtil.java b/src/java/org/apache/cassandra/utils/ByteBufferUtil.java index fb1a9ec..b6f93bc 100644 --- a/src/java/org/apache/cassandra/utils/ByteBufferUtil.java +++ b/src/java/org/apache/cassandra/utils/ByteBufferUtil.java @@ -435,6 +435,18 @@ public class ByteBufferUtil return bytes.getShort(bytes.position()); } + /** + * Convert a byte buffer to a short. + * Does not change the byte buffer position. + * + * @param bytes byte buffer to convert to byte + * @return byte representation of the byte buffer + */ + public static byte toByte(ByteBuffer bytes) + { + return bytes.get(bytes.position()); + } + public static long toLong(ByteBuffer bytes) { return bytes.getLong(bytes.position()); @@ -450,6 +462,11 @@ public class ByteBufferUtil return bytes.getDouble(bytes.position()); } + public static ByteBuffer bytes(byte b) + { + return ByteBuffer.allocate(1).put(0, b); + } + public static ByteBuffer bytes(short s) { return ByteBuffer.allocate(2).putShort(0, s); http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/test/unit/org/apache/cassandra/cql3/CQLTester.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java index 2a5afc2..598addd 100644 --- a/test/unit/org/apache/cassandra/cql3/CQLTester.java +++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java @@ -1432,7 +1432,7 @@ public abstract class CQLTester return s; } - private static ByteBuffer makeByteBuffer(Object value, AbstractType type) + protected static ByteBuffer makeByteBuffer(Object value, AbstractType type) { if (value == null) return null; @@ -1463,7 +1463,7 @@ public abstract class CQLTester return type.getString(bb); } - protected Object tuple(Object...values) + protected TupleValue tuple(Object...values) { return new TupleValue(values); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/8b3de2f4/test/unit/org/apache/cassandra/cql3/functions/OperationFctsTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/functions/OperationFctsTest.java b/test/unit/org/apache/cassandra/cql3/functions/OperationFctsTest.java new file mode 100644 index 0000000..6de5fdb --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/functions/OperationFctsTest.java @@ -0,0 +1,744 @@ +/* + * 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.cassandra.cql3.functions; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.junit.Test; + +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.exceptions.OperationExecutionException; + +public class OperationFctsTest extends CQLTester +{ + @Test + public void testSingleOperations() throws Throwable + { + createTable("CREATE TABLE %s (a tinyint, b smallint, c int, d bigint, e float, f double, g varint, h decimal, PRIMARY KEY(a, b, c))"); + execute("INSERT INTO %S (a, b, c, d, e, f, g, h) VALUES (1, 2, 3, 4, 5.5, 6.5, 7, 8.5)"); + + // Test additions + assertColumnNames(execute("SELECT a + a, b + a, c + a, d + a, e + a, f + a, g + a, h + a FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + "a + a", "b + a", "c + a", "d + a", "e + a", "f + a", "g + a", "h + a"); + + assertRows(execute("SELECT a + a, b + a, c + a, d + a, e + a, f + a, g + a, h + a FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + row((byte) 2, (short) 3, 4, 5L, 6.5F, 7.5, BigInteger.valueOf(8), BigDecimal.valueOf(9.5))); + + assertRows(execute("SELECT a + b, b + b, c + b, d + b, e + b, f + b, g + b, h + b FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + row((short) 3, (short) 4, 5, 6L, 7.5F, 8.5, BigInteger.valueOf(9), BigDecimal.valueOf(10.5))); + + assertRows(execute("SELECT a + c, b + c, c + c, d + c, e + c, f + c, g + c, h + c FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + row(4, 5, 6, 7L, 8.5F, 9.5, BigInteger.valueOf(10), BigDecimal.valueOf(11.5))); + + assertRows(execute("SELECT a + d, b + d, c + d, d + d, e + d, f + d, g + d, h + d FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + row(5L, 6L, 7L, 8L, 9.5, 10.5, BigInteger.valueOf(11), BigDecimal.valueOf(12.5))); + + assertRows(execute("SELECT a + e, b + e, c + e, d + e, e + e, f + e, g + e, h + e FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + row(6.5F, 7.5F, 8.5F, 9.5, 11.0F, 12.0, BigDecimal.valueOf(12.5), BigDecimal.valueOf(14.0))); + + assertRows(execute("SELECT a + f, b + f, c + f, d + f, e + f, f + f, g + f, h + f FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + row(7.5, 8.5, 9.5, 10.5, 12.0, 13.0, BigDecimal.valueOf(13.5), BigDecimal.valueOf(15.0))); + + assertRows(execute("SELECT a + g, b + g, c + g, d + g, e + g, f + g, g + g, h + g FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + row(BigInteger.valueOf(8), + BigInteger.valueOf(9), + BigInteger.valueOf(10), + BigInteger.valueOf(11), + BigDecimal.valueOf(12.5), + BigDecimal.valueOf(13.5), + BigInteger.valueOf(14), + BigDecimal.valueOf(15.5))); + + assertRows(execute("SELECT a + h, b + h, c + h, d + h, e + h, f + h, g + h, h + h FROM %s WHERE a = 1 AND b = 2 AND c = 1 + 2"), + row(BigDecimal.valueOf(9.5), + BigDecimal.valueOf(10.5), + BigDecimal.valueOf(11.5), + BigDecimal.valueOf(12.5), + BigDecimal.valueOf(14.0), + BigDecimal.valueOf(15.0), + BigDecimal.valueOf(15.5), + BigDecimal.valueOf(17.0))); + + // Test substractions + + assertColumnNames(execute("SELECT a - a, b - a, c - a, d - a, e - a, f - a, g - a, h - a FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + "a - a", "b - a", "c - a", "d - a", "e - a", "f - a", "g - a", "h - a"); + + assertRows(execute("SELECT a - a, b - a, c - a, d - a, e - a, f - a, g - a, h - a FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + row((byte) 0, (short) 1, 2, 3L, 4.5F, 5.5, BigInteger.valueOf(6), BigDecimal.valueOf(7.5))); + + assertRows(execute("SELECT a - b, b - b, c - b, d - b, e - b, f - b, g - b, h - b FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + row((short) -1, (short) 0, 1, 2L, 3.5F, 4.5, BigInteger.valueOf(5), BigDecimal.valueOf(6.5))); + + assertRows(execute("SELECT a - c, b - c, c - c, d - c, e - c, f - c, g - c, h - c FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + row(-2, -1, 0, 1L, 2.5F, 3.5, BigInteger.valueOf(4), BigDecimal.valueOf(5.5))); + + assertRows(execute("SELECT a - d, b - d, c - d, d - d, e - d, f - d, g - d, h - d FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + row(-3L, -2L, -1L, 0L, 1.5, 2.5, BigInteger.valueOf(3), BigDecimal.valueOf(4.5))); + + assertRows(execute("SELECT a - e, b - e, c - e, d - e, e - e, f - e, g - e, h - e FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + row(-4.5F, -3.5F, -2.5F, -1.5, 0.0F, 1.0, BigDecimal.valueOf(1.5), BigDecimal.valueOf(3.0))); + + assertRows(execute("SELECT a - f, b - f, c - f, d - f, e - f, f - f, g - f, h - f FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + row(-5.5, -4.5, -3.5, -2.5, -1.0, 0.0, BigDecimal.valueOf(0.5), BigDecimal.valueOf(2.0))); + + assertRows(execute("SELECT a - g, b - g, c - g, d - g, e - g, f - g, g - g, h - g FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + row(BigInteger.valueOf(-6), + BigInteger.valueOf(-5), + BigInteger.valueOf(-4), + BigInteger.valueOf(-3), + BigDecimal.valueOf(-1.5), + BigDecimal.valueOf(-0.5), + BigInteger.valueOf(0), + BigDecimal.valueOf(1.5))); + + assertRows(execute("SELECT a - h, b - h, c - h, d - h, e - h, f - h, g - h, h - h FROM %s WHERE a = 1 AND b = 2 AND c = 4 - 1"), + row(BigDecimal.valueOf(-7.5), + BigDecimal.valueOf(-6.5), + BigDecimal.valueOf(-5.5), + BigDecimal.valueOf(-4.5), + BigDecimal.valueOf(-3.0), + BigDecimal.valueOf(-2.0), + BigDecimal.valueOf(-1.5), + BigDecimal.valueOf(0.0))); + + // Test multiplications + + assertColumnNames(execute("SELECT a * a, b * a, c * a, d * a, e * a, f * a, g * a, h * a FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + "a * a", "b * a", "c * a", "d * a", "e * a", "f * a", "g * a", "h * a"); + + assertRows(execute("SELECT a * a, b * a, c * a, d * a, e * a, f * a, g * a, h * a FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + row((byte) 1, (short) 2, 3, 4L, 5.5F, 6.5, BigInteger.valueOf(7), new BigDecimal("8.50"))); + + assertRows(execute("SELECT a * b, b * b, c * b, d * b, e * b, f * b, g * b, h * b FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + row((short) 2, (short) 4, 6, 8L, 11.0F, 13.0, BigInteger.valueOf(14), new BigDecimal("17.00"))); + + assertRows(execute("SELECT a * c, b * c, c * c, d * c, e * c, f * c, g * c, h * c FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + row(3, 6, 9, 12L, 16.5F, 19.5, BigInteger.valueOf(21), new BigDecimal("25.50"))); + + assertRows(execute("SELECT a * d, b * d, c * d, d * d, e * d, f * d, g * d, h * d FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + row(4L, 8L, 12L, 16L, 22.0, 26.0, BigInteger.valueOf(28), new BigDecimal("34.00"))); + + assertRows(execute("SELECT a * e, b * e, c * e, d * e, e * e, f * e, g * e, h * e FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + row(5.5F, 11.0F, 16.5F, 22.0, 30.25F, 35.75, new BigDecimal("38.5"), new BigDecimal("46.75"))); + + assertRows(execute("SELECT a * f, b * f, c * f, d * f, e * f, f * f, g * f, h * f FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + row(6.5, 13.0, 19.5, 26.0, 35.75, 42.25, new BigDecimal("45.5"), BigDecimal.valueOf(55.25))); + + assertRows(execute("SELECT a * g, b * g, c * g, d * g, e * g, f * g, g * g, h * g FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + row(BigInteger.valueOf(7), + BigInteger.valueOf(14), + BigInteger.valueOf(21), + BigInteger.valueOf(28), + new BigDecimal("38.5"), + new BigDecimal("45.5"), + BigInteger.valueOf(49), + new BigDecimal("59.5"))); + + assertRows(execute("SELECT a * h, b * h, c * h, d * h, e * h, f * h, g * h, h * h FROM %s WHERE a = 1 AND b = 2 AND c = 3 * 1"), + row(new BigDecimal("8.50"), + new BigDecimal("17.00"), + new BigDecimal("25.50"), + new BigDecimal("34.00"), + new BigDecimal("46.75"), + new BigDecimal("55.25"), + new BigDecimal("59.5"), + new BigDecimal("72.25"))); + + // Test divisions + + assertColumnNames(execute("SELECT a / a, b / a, c / a, d / a, e / a, f / a, g / a, h / a FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + "a / a", "b / a", "c / a", "d / a", "e / a", "f / a", "g / a", "h / a"); + + assertRows(execute("SELECT a / a, b / a, c / a, d / a, e / a, f / a, g / a, h / a FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + row((byte) 1, (short) 2, 3, 4L, 5.5F, 6.5, BigInteger.valueOf(7), new BigDecimal("8.5"))); + + assertRows(execute("SELECT a / b, b / b, c / b, d / b, e / b, f / b, g / b, h / b FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + row((short) 0, (short) 1, 1, 2L, 2.75F, 3.25, BigInteger.valueOf(3), new BigDecimal("4.25"))); + + assertRows(execute("SELECT a / c, b / c, c / c, d / c, e / c, f / c, g / c, h / c FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + row(0, 0, 1, 1L, 1.8333334F, 2.1666666666666665, BigInteger.valueOf(2), new BigDecimal("2.833333333333333333333333333333333"))); + + assertRows(execute("SELECT a / d, b / d, c / d, d / d, e / d, f / d, g / d, h / d FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + row(0L, 0L, 0L, 1L, 1.375, 1.625, BigInteger.valueOf(1), new BigDecimal("2.125"))); + + assertRows(execute("SELECT a / e, b / e, c / e, d / e, e / e, f / e, g / e, h / e FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + row(0.18181819F, 0.36363637F, 0.54545456F, 0.7272727272727273, 1.0F, 1.1818181818181819, new BigDecimal("1.272727272727272727272727272727273"), new BigDecimal("1.545454545454545454545454545454545"))); + + assertRows(execute("SELECT a / f, b / f, c / f, d / f, e / f, f / f, g / f, h / f FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + row(0.15384615384615385, 0.3076923076923077, 0.46153846153846156, 0.6153846153846154, 0.8461538461538461, 1.0, new BigDecimal("1.076923076923076923076923076923077"), new BigDecimal("1.307692307692307692307692307692308"))); + + assertRows(execute("SELECT a / g, b / g, c / g, d / g, e / g, f / g, g / g, h / g FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + row(BigInteger.valueOf(0), + BigInteger.valueOf(0), + BigInteger.valueOf(0), + BigInteger.valueOf(0), + new BigDecimal("0.7857142857142857142857142857142857"), + new BigDecimal("0.9285714285714285714285714285714286"), + BigInteger.valueOf(1), + new BigDecimal("1.214285714285714285714285714285714"))); + + assertRows(execute("SELECT a / h, b / h, c / h, d / h, e / h, f / h, g / h, h / h FROM %s WHERE a = 1 AND b = 2 AND c = 3 / 1"), + row(new BigDecimal("0.1176470588235294117647058823529412"), + new BigDecimal("0.2352941176470588235294117647058824"), + new BigDecimal("0.3529411764705882352941176470588235"), + new BigDecimal("0.4705882352941176470588235294117647"), + new BigDecimal("0.6470588235294117647058823529411765"), + new BigDecimal("0.7647058823529411764705882352941176"), + new BigDecimal("0.8235294117647058823529411764705882"), + new BigDecimal("1"))); + + // Test modulo operations + + assertColumnNames(execute("SELECT a %% a, b %% a, c %% a, d %% a, e %% a, f %% a, g %% a, h %% a FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + "a % a", "b % a", "c % a", "d % a", "e % a", "f % a", "g % a", "h % a"); + + assertRows(execute("SELECT a %% a, b %% a, c %% a, d %% a, e %% a, f %% a, g %% a, h %% a FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + row((byte) 0, (short) 0, 0, 0L, 0.5F, 0.5, BigInteger.valueOf(0), new BigDecimal("0.5"))); + + assertRows(execute("SELECT a %% b, b %% b, c %% b, d %% b, e %% b, f %% b, g %% b, h %% b FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + row((short) 1, (short) 0, 1, 0L, 1.5F, 0.5, BigInteger.valueOf(1), new BigDecimal("0.5"))); + + assertRows(execute("SELECT a %% c, b %% c, c %% c, d %% c, e %% c, f %% c, g %% c, h %% c FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + row(1, 2, 0, 1L, 2.5F, 0.5, BigInteger.valueOf(1), new BigDecimal("2.5"))); + + assertRows(execute("SELECT a %% d, b %% d, c %% d, d %% d, e %% d, f %% d, g %% d, h %% d FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + row(1L, 2L, 3L, 0L, 1.5, 2.5, BigInteger.valueOf(3), new BigDecimal("0.5"))); + + assertRows(execute("SELECT a %% e, b %% e, c %% e, d %% e, e %% e, f %% e, g %% e, h %% e FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + row(1.0F, 2.0F, 3.0F, 4.0, 0.0F, 1.0, new BigDecimal("1.5"), new BigDecimal("3.0"))); + + assertRows(execute("SELECT a %% f, b %% f, c %% f, d %% f, e %% f, f %% f, g %% f, h %% f FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + row(1.0, 2.0, 3.0, 4.0, 5.5, 0.0, new BigDecimal("0.5"), new BigDecimal("2.0"))); + + assertRows(execute("SELECT a %% g, b %% g, c %% g, d %% g, e %% g, f %% g, g %% g, h %% g FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + row(BigInteger.valueOf(1), + BigInteger.valueOf(2), + BigInteger.valueOf(3), + BigInteger.valueOf(4), + new BigDecimal("5.5"), + new BigDecimal("6.5"), + BigInteger.valueOf(0), + new BigDecimal("1.5"))); + + assertRows(execute("SELECT a %% h, b %% h, c %% h, d %% h, e %% h, f %% h, g %% h, h %% h FROM %s WHERE a = 1 AND b = 2 AND c = 23 %% 5"), + row(new BigDecimal("1.0"), + new BigDecimal("2.0"), + new BigDecimal("3.0"), + new BigDecimal("4.0"), + new BigDecimal("5.5"), + new BigDecimal("6.5"), + new BigDecimal("7"), + new BigDecimal("0.0"))); + + // Test negation + + assertColumnNames(execute("SELECT -a, -b, -c, -d, -e, -f, -g, -h FROM %s WHERE a = 1 AND b = 2"), + "-a", "-b", "-c", "-d", "-e", "-f", "-g", "-h"); + + assertRows(execute("SELECT -a, -b, -c, -d, -e, -f, -g, -h FROM %s WHERE a = 1 AND b = 2"), + row((byte) -1, (short) -2, -3, -4L, -5.5F, -6.5, BigInteger.valueOf(-7), new BigDecimal("-8.5"))); + + // Test with null + execute("UPDATE %s SET d = ? WHERE a = ? AND b = ? AND c = ?", null, (byte) 1, (short) 2, 3); + assertRows(execute("SELECT a + d, b + d, c + d, d + d, e + d, f + d, g + d, h + d FROM %s WHERE a = 1 AND b = 2"), + row(null, null, null, null, null, null, null, null)); + } + + @Test + public void testSingleOperationsWithLiterals() throws Throwable + { + createTable("CREATE TABLE %s (pk int, c1 tinyint, c2 smallint, v text, PRIMARY KEY(pk, c1, c2))"); + execute("INSERT INTO %S (pk, c1, c2, v) VALUES (2, 2, 2, 'test')"); + + // There is only one function outputing tinyint + assertRows(execute("SELECT * FROM %s WHERE pk = 2 AND c1 = 1 + 1"), + row(2, (byte) 2, (short) 2, "test")); + + // As the operation can only be a sum between tinyints the expected type is tinyint + assertInvalidMessage("Expected 1 byte for a tinyint (4)", + "SELECT * FROM %s WHERE pk = 2 AND c1 = 1 + ?", 1); + + assertRows(execute("SELECT * FROM %s WHERE pk = 2 AND c1 = 1 + ?", (byte) 1), + row(2, (byte) 2, (short) 2, "test")); + + assertRows(execute("SELECT * FROM %s WHERE pk = 1 + 1 AND c1 = 2"), + row(2, (byte) 2, (short) 2, "test")); + + assertRows(execute("SELECT * FROM %s WHERE pk = 2 AND c1 = 2 AND c2 = 1 + 1"), + row(2, (byte) 2, (short) 2, "test")); + + assertRows(execute("SELECT * FROM %s WHERE pk = 2 AND c1 = 2 AND c2 = 1 * (1 + 1)"), + row(2, (byte) 2, (short) 2, "test")); + + // tinyint, smallint and int could be used there so we need to disambiguate + assertInvalidMessage("Ambiguous '+' operation: use type casts to disambiguate", + "SELECT * FROM %s WHERE pk = ? + 1 AND c1 = 2", 1); + + assertInvalidMessage("Ambiguous '+' operation: use type casts to disambiguate", + "SELECT * FROM %s WHERE pk = 2 AND c1 = 2 AND c2 = 1 * (? + 1)", 1); + + assertRows(execute("SELECT 1 + 1, v FROM %s WHERE pk = 2 AND c1 = 2"), + row(2, "test")); + + // As the output type is unknown the ? type cannot be determined + assertInvalidMessage("Ambiguous '+' operation: use type casts to disambiguate", + "SELECT 1 + ?, v FROM %s WHERE pk = 2 AND c1 = 2", 1); + + // As the prefered type for the constants is int, the returned type will be int + assertRows(execute("SELECT 100 + 50, v FROM %s WHERE pk = 2 AND c1 = 2"), + row(150, "test")); + + // As the output type is unknown the ? type cannot be determined + assertInvalidMessage("Ambiguous '+' operation: use type casts to disambiguate", + "SELECT ? + 50, v FROM %s WHERE pk = 2 AND c1 = 2", 100); + + createTable("CREATE TABLE %s (a tinyint, b smallint, c int, d bigint, e float, f double, g varint, h decimal, PRIMARY KEY(a, b))" + + " WITH CLUSTERING ORDER BY (b DESC)"); // Make sure we test with ReversedTypes + execute("INSERT INTO %S (a, b, c, d, e, f, g, h) VALUES (1, 2, 3, 4, 5.5, 6.5, 7, 8.5)"); + + // Test additions + assertColumnNames(execute("SELECT a + 1, b + 1, c + 1, d + 1, e + 1, f + 1, g + 1, h + 1 FROM %s WHERE a = 1 AND b = 2"), + "a + 1", "b + 1", "c + 1", "d + 1", "e + 1", "f + 1", "g + 1", "h + 1"); + + assertRows(execute("SELECT a + 1, b + 1, c + 1, d + 1, e + 1, f + 1, g + 1, h + 1 FROM %s WHERE a = 1 AND b = 2"), + row(2, 3, 4, 5L, 6.5F, 7.5, BigInteger.valueOf(8), BigDecimal.valueOf(9.5))); + + assertRows(execute("SELECT 2 + a, 2 + b, 2 + c, 2 + d, 2 + e, 2 + f, 2 + g, 2 + h FROM %s WHERE a = 1 AND b = 2"), + row(3, 4, 5, 6L, 7.5F, 8.5, BigInteger.valueOf(9), BigDecimal.valueOf(10.5))); + + long bigInt = Integer.MAX_VALUE + 10L; + + assertRows(execute("SELECT a + " + bigInt + "," + + " b + " + bigInt + "," + + " c + " + bigInt + "," + + " d + " + bigInt + "," + + " e + " + bigInt + "," + + " f + " + bigInt + "," + + " g + " + bigInt + "," + + " h + " + bigInt + " FROM %s WHERE a = 1 AND b = 2"), + row(1L + bigInt, + 2L + bigInt, + 3L + bigInt, + 4L + bigInt, + 5.5 + bigInt, + 6.5 + bigInt, + BigInteger.valueOf(bigInt + 7), + BigDecimal.valueOf(bigInt + 8.5))); + + assertRows(execute("SELECT a + 5.5, b + 5.5, c + 5.5, d + 5.5, e + 5.5, f + 5.5, g + 5.5, h + 5.5 FROM %s WHERE a = 1 AND b = 2"), + row(6.5, 7.5, 8.5, 9.5, 11.0, 12.0, BigDecimal.valueOf(12.5), BigDecimal.valueOf(14.0))); + + assertRows(execute("SELECT a + 6.5, b + 6.5, c + 6.5, d + 6.5, e + 6.5, f + 6.5, g + 6.5, h + 6.5 FROM %s WHERE a = 1 AND b = 2"), + row(7.5, 8.5, 9.5, 10.5, 12.0, 13.0, BigDecimal.valueOf(13.5), BigDecimal.valueOf(15.0))); + + // Test substractions + + assertColumnNames(execute("SELECT a - 1, b - 1, c - 1, d - 1, e - 1, f - 1, g - 1, h - 1 FROM %s WHERE a = 1 AND b = 2"), + "a - 1", "b - 1", "c - 1", "d - 1", "e - 1", "f - 1", "g - 1", "h - 1"); + + assertRows(execute("SELECT a - 1, b - 1, c - 1, d - 1, e - 1, f - 1, g - 1, h - 1 FROM %s WHERE a = 1 AND b = 2"), + row(0, 1, 2, 3L, 4.5F, 5.5, BigInteger.valueOf(6), BigDecimal.valueOf(7.5))); + + assertRows(execute("SELECT a - 2, b - 2, c - 2, d - 2, e - 2, f - 2, g - 2, h - 2 FROM %s WHERE a = 1 AND b = 2"), + row(-1, 0, 1, 2L, 3.5F, 4.5, BigInteger.valueOf(5), BigDecimal.valueOf(6.5))); + + assertRows(execute("SELECT a - 3, b - 3, 3 - 3, d - 3, e - 3, f - 3, g - 3, h - 3 FROM %s WHERE a = 1 AND b = 2"), + row(-2, -1, 0, 1L, 2.5F, 3.5, BigInteger.valueOf(4), BigDecimal.valueOf(5.5))); + + assertRows(execute("SELECT a - " + bigInt + "," + + " b - " + bigInt + "," + + " c - " + bigInt + "," + + " d - " + bigInt + "," + + " e - " + bigInt + "," + + " f - " + bigInt + "," + + " g - " + bigInt + "," + + " h - " + bigInt + " FROM %s WHERE a = 1 AND b = 2"), + row(1L - bigInt, + 2L - bigInt, + 3L - bigInt, + 4L - bigInt, + 5.5 - bigInt, + 6.5 - bigInt, + BigInteger.valueOf(7 - bigInt), + BigDecimal.valueOf(8.5 - bigInt))); + + assertRows(execute("SELECT a - 5.5, b - 5.5, c - 5.5, d - 5.5, e - 5.5, f - 5.5, g - 5.5, h - 5.5 FROM %s WHERE a = 1 AND b = 2"), + row(-4.5, -3.5, -2.5, -1.5, 0.0, 1.0, BigDecimal.valueOf(1.5), BigDecimal.valueOf(3.0))); + + assertRows(execute("SELECT a - 6.5, b - 6.5, c - 6.5, d - 6.5, e - 6.5, f - 6.5, g - 6.5, h - 6.5 FROM %s WHERE a = 1 AND b = 2"), + row(-5.5, -4.5, -3.5, -2.5, -1.0, 0.0, BigDecimal.valueOf(0.5), BigDecimal.valueOf(2.0))); + + // Test multiplications + + assertColumnNames(execute("SELECT a * 1, b * 1, c * 1, d * 1, e * 1, f * 1, g * 1, h * 1 FROM %s WHERE a = 1 AND b = 2"), + "a * 1", "b * 1", "c * 1", "d * 1", "e * 1", "f * 1", "g * 1", "h * 1"); + + assertRows(execute("SELECT a * 1, b * 1, c * 1, d * 1, e * 1, f * 1, g * 1, h * 1 FROM %s WHERE a = 1 AND b = 2"), + row(1, 2, 3, 4L, 5.5F, 6.5, BigInteger.valueOf(7), new BigDecimal("8.50"))); + + assertRows(execute("SELECT a * 2, b * 2, c * 2, d * 2, e * 2, f * 2, g * 2, h * 2 FROM %s WHERE a = 1 AND b = 2"), + row(2, 4, 6, 8L, 11.0F, 13.0, BigInteger.valueOf(14), new BigDecimal("17.00"))); + + assertRows(execute("SELECT a * 3, b * 3, c * 3, d * 3, e * 3, f * 3, g * 3, h * 3 FROM %s WHERE a = 1 AND b = 2"), + row(3, 6, 9, 12L, 16.5F, 19.5, BigInteger.valueOf(21), new BigDecimal("25.50"))); + + assertRows(execute("SELECT a * " + bigInt + "," + + " b * " + bigInt + "," + + " c * " + bigInt + "," + + " d * " + bigInt + "," + + " e * " + bigInt + "," + + " f * " + bigInt + "," + + " g * " + bigInt + "," + + " h * " + bigInt + " FROM %s WHERE a = 1 AND b = 2"), + row(1L * bigInt, + 2L * bigInt, + 3L * bigInt, + 4L * bigInt, + 5.5 * bigInt, + 6.5 * bigInt, + BigInteger.valueOf(7 * bigInt), + BigDecimal.valueOf(8.5 * bigInt))); + + assertRows(execute("SELECT a * 5.5, b * 5.5, c * 5.5, d * 5.5, e * 5.5, f * 5.5, g * 5.5, h * 5.5 FROM %s WHERE a = 1 AND b = 2"), + row(5.5, 11.0, 16.5, 22.0, 30.25, 35.75, new BigDecimal("38.5"), new BigDecimal("46.75"))); + + assertRows(execute("SELECT a * 6.5, b * 6.5, c * 6.5, d * 6.5, e * 6.5, 6.5 * f, g * 6.5, h * 6.5 FROM %s WHERE a = 1 AND b = 2"), + row(6.5, 13.0, 19.5, 26.0, 35.75, 42.25, new BigDecimal("45.5"), BigDecimal.valueOf(55.25))); + + // Test divisions + + assertColumnNames(execute("SELECT a / 1, b / 1, c / 1, d / 1, e / 1, f / 1, g / 1, h / 1 FROM %s WHERE a = 1 AND b = 2"), + "a / 1", "b / 1", "c / 1", "d / 1", "e / 1", "f / 1", "g / 1", "h / 1"); + + assertRows(execute("SELECT a / 1, b / 1, c / 1, d / 1, e / 1, f / 1, g / 1, h / 1 FROM %s WHERE a = 1 AND b = 2"), + row(1, 2, 3, 4L, 5.5F, 6.5, BigInteger.valueOf(7), new BigDecimal("8.5"))); + + assertRows(execute("SELECT a / 2, b / 2, c / 2, d / 2, e / 2, f / 2, g / 2, h / 2 FROM %s WHERE a = 1 AND b = 2"), + row(0, 1, 1, 2L, 2.75F, 3.25, BigInteger.valueOf(3), new BigDecimal("4.25"))); + + assertRows(execute("SELECT a / 3, b / 3, c / 3, d / 3, e / 3, f / 3, g / 3, h / 3 FROM %s WHERE a = 1 AND b = 2"), + row(0, 0, 1, 1L, 1.8333334F, 2.1666666666666665, BigInteger.valueOf(2), new BigDecimal("2.833333333333333333333333333333333"))); + + assertRows(execute("SELECT a / " + bigInt + "," + + " b / " + bigInt + "," + + " c / " + bigInt + "," + + " d / " + bigInt + "," + + " e / " + bigInt + "," + + " f / " + bigInt + "," + + " g / " + bigInt + " FROM %s WHERE a = 1 AND b = 2"), + row(1L / bigInt, + 2L / bigInt, + 3L / bigInt, + 4L / bigInt, + 5.5 / bigInt, + 6.5 / bigInt, + BigInteger.valueOf(7).divide(BigInteger.valueOf(bigInt)))); + + assertRows(execute("SELECT a / 5.5, b / 5.5, c / 5.5, d / 5.5, e / 5.5, f / 5.5, g / 5.5, h / 5.5 FROM %s WHERE a = 1 AND b = 2"), + row(0.18181818181818182, 0.36363636363636365, 0.5454545454545454, 0.7272727272727273, 1.0, 1.1818181818181819, new BigDecimal("1.272727272727272727272727272727273"), new BigDecimal("1.545454545454545454545454545454545"))); + + assertRows(execute("SELECT a / 6.5, b / 6.5, c / 6.5, d / 6.5, e / 6.5, f / 6.5, g / 6.5, h / 6.5 FROM %s WHERE a = 1 AND b = 2"), + row(0.15384615384615385, 0.3076923076923077, 0.46153846153846156, 0.6153846153846154, 0.8461538461538461, 1.0, new BigDecimal("1.076923076923076923076923076923077"), new BigDecimal("1.307692307692307692307692307692308"))); + + // Test modulo operations + + assertColumnNames(execute("SELECT a %% 1, b %% 1, c %% 1, d %% 1, e %% 1, f %% 1, g %% 1, h %% 1 FROM %s WHERE a = 1 AND b = 2"), + "a % 1", "b % 1", "c % 1", "d % 1", "e % 1", "f % 1", "g % 1", "h % 1"); + + assertRows(execute("SELECT a %% 1, b %% 1, c %% 1, d %% 1, e %% 1, f %% 1, g %% 1, h %% 1 FROM %s WHERE a = 1 AND b = 2"), + row(0, 0, 0, 0L, 0.5F, 0.5, BigInteger.valueOf(0), new BigDecimal("0.5"))); + + assertRows(execute("SELECT a %% 2, b %% 2, c %% 2, d %% 2, e %% 2, f %% 2, g %% 2, h %% 2 FROM %s WHERE a = 1 AND b = 2"), + row(1, 0, 1, 0L, 1.5F, 0.5, BigInteger.valueOf(1), new BigDecimal("0.5"))); + + assertRows(execute("SELECT a %% 3, b %% 3, c %% 3, d %% 3, e %% 3, f %% 3, g %% 3, h %% 3 FROM %s WHERE a = 1 AND b = 2"), + row(1, 2, 0, 1L, 2.5F, 0.5, BigInteger.valueOf(1), new BigDecimal("2.5"))); + + assertRows(execute("SELECT a %% " + bigInt + "," + + " b %% " + bigInt + "," + + " c %% " + bigInt + "," + + " d %% " + bigInt + "," + + " e %% " + bigInt + "," + + " f %% " + bigInt + "," + + " g %% " + bigInt + "," + + " h %% " + bigInt + " FROM %s WHERE a = 1 AND b = 2"), + row(1L % bigInt, + 2L % bigInt, + 3L % bigInt, + 4L % bigInt, + 5.5 % bigInt, + 6.5 % bigInt, + BigInteger.valueOf(7 % bigInt), + BigDecimal.valueOf(8.5 % bigInt))); + + assertRows(execute("SELECT a %% 5.5, b %% 5.5, c %% 5.5, d %% 5.5, e %% 5.5, f %% 5.5, g %% 5.5, h %% 5.5 FROM %s WHERE a = 1 AND b = 2"), + row(1.0, 2.0, 3.0, 4.0, 0.0, 1.0, new BigDecimal("1.5"), new BigDecimal("3.0"))); + + assertRows(execute("SELECT a %% 6.5, b %% 6.5, c %% 6.5, d %% 6.5, e %% 6.5, f %% 6.5, g %% 6.5, h %% 6.5 FROM %s WHERE a = 1 AND b = 2"), + row(1.0, 2.0, 3.0, 4.0, 5.5, 0.0, new BigDecimal("0.5"), new BigDecimal("2.0"))); + + assertRows(execute("SELECT a, b, 1 + 1, 2 - 1, 2 * 2, 2 / 1 , 2 %% 1, (int) -1 FROM %s WHERE a = 1 AND b = 2"), + row((byte) 1, (short) 2, 2, 1, 4, 2, 0, -1)); + } + + @Test + public void testWithCounters() throws Throwable + { + createTable("CREATE TABLE %s (a int PRIMARY KEY, b counter)"); + execute("UPDATE %s SET b = b + 1 WHERE a = 1"); + execute("UPDATE %s SET b = b + 1 WHERE a = 1"); + assertRows(execute("SELECT b FROM %s WHERE a = 1"), row(2L)); + + assertRows(execute("SELECT b + (tinyint) 1," + + " b + (smallint) 1," + + " b + 1," + + " b + (bigint) 1," + + " b + (float) 1.5," + + " b + 1.5," + + " b + (varint) 1," + + " b + (decimal) 1.5," + + " b + b FROM %s WHERE a = 1"), + row(3L, 3L, 3L, 3L, 3.5, 3.5, BigInteger.valueOf(3), new BigDecimal("3.5"), 4L)); + + assertRows(execute("SELECT b - (tinyint) 1," + + " b - (smallint) 1," + + " b - 1," + + " b - (bigint) 1," + + " b - (float) 1.5," + + " b - 1.5," + + " b - (varint) 1," + + " b - (decimal) 1.5," + + " b - b FROM %s WHERE a = 1"), + row(1L, 1L, 1L, 1L, 0.5, 0.5, BigInteger.valueOf(1), new BigDecimal("0.5"), 0L)); + + assertRows(execute("SELECT b * (tinyint) 1," + + " b * (smallint) 1," + + " b * 1," + + " b * (bigint) 1," + + " b * (float) 1.5," + + " b * 1.5," + + " b * (varint) 1," + + " b * (decimal) 1.5," + + " b * b FROM %s WHERE a = 1"), + row(2L, 2L, 2L, 2L, 3.0, 3.0, BigInteger.valueOf(2), new BigDecimal("3.00"), 4L)); + + assertRows(execute("SELECT b / (tinyint) 1," + + " b / (smallint) 1," + + " b / 1," + + " b / (bigint) 1," + + " b / (float) 0.5," + + " b / 0.5," + + " b / (varint) 1," + + " b / (decimal) 0.5," + + " b / b FROM %s WHERE a = 1"), + row(2L, 2L, 2L, 2L, 4.0, 4.0, BigInteger.valueOf(2), new BigDecimal("4"), 1L)); + + assertRows(execute("SELECT b %% (tinyint) 1," + + " b %% (smallint) 1," + + " b %% 1," + + " b %% (bigint) 1," + + " b %% (float) 0.5," + + " b %% 0.5," + + " b %% (varint) 1," + + " b %% (decimal) 0.5," + + " b %% b FROM %s WHERE a = 1"), + row(0L, 0L, 0L, 0L, 0.0, 0.0, BigInteger.valueOf(0), new BigDecimal("0.0"), 0L)); + + assertRows(execute("SELECT -b FROM %s WHERE a = 1"), row(-2L)); + } + + @Test + public void testPrecedenceAndParentheses() throws Throwable + { + createTable("CREATE TABLE %s (a int, b int, c int, d int, PRIMARY KEY(a, b))"); + execute("INSERT INTO %S (a, b, c, d) VALUES (2, 5, 25, 4)"); + + UntypedResultSet rs = execute("SELECT a - c / b + d FROM %s"); + assertColumnNames(rs, "a - c / b + d"); + assertRows(rs, row(1)); + + rs = execute("SELECT (c - b) / a + d FROM %s"); + assertColumnNames(rs, "(c - b) / a + d"); + assertRows(rs, row(14)); + + rs = execute("SELECT c / a / b FROM %s"); + assertColumnNames(rs, "c / a / b"); + assertRows(rs, row(2)); + + rs = execute("SELECT c / b / d FROM %s"); + assertColumnNames(rs, "c / b / d"); + assertRows(rs, row(1)); + + rs = execute("SELECT (c - a) %% d / a FROM %s"); + assertColumnNames(rs, "(c - a) % d / a"); + assertRows(rs, row(1)); + + rs = execute("SELECT (c - a) %% d / a + d FROM %s"); + assertColumnNames(rs, "(c - a) % d / a + d"); + assertRows(rs, row(5)); + + rs = execute("SELECT -(c - a) %% d / a + d FROM %s"); + assertColumnNames(rs, "-(c - a) % d / a + d"); + assertRows(rs, row(3)); + + rs = execute("SELECT (-c - a) %% d / a + d FROM %s"); + assertColumnNames(rs, "(-c - a) % d / a + d"); + assertRows(rs, row(3)); + + rs = execute("SELECT c - a %% d / a + d FROM %s"); + assertColumnNames(rs, "c - a % d / a + d"); + assertRows(rs, row(28)); + + rs = execute("SELECT (int)((c - a) %% d / (a + d)) FROM %s"); + assertColumnNames(rs, "(int)((c - a) % d / (a + d))"); + assertRows(rs, row(0)); + + // test with aliases + rs = execute("SELECT (int)((c - a) %% d / (a + d)) as result FROM %s"); + assertColumnNames(rs, "result"); + assertRows(rs, row(0)); + + rs = execute("SELECT c / a / b as divisions FROM %s"); + assertColumnNames(rs, "divisions"); + assertRows(rs, row(2)); + + assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = (int) ? / 2 - 5", 2, 20), + row(2, 5, 25, 4)); + + assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = (int) ? / (2 + 2)", 2, 20), + row(2, 5, 25, 4)); + } + + @Test + public void testWithDivisionByZero() throws Throwable + { + createTable("CREATE TABLE %s (a tinyint, b smallint, c int, d bigint, e float, f double, g varint, h decimal, PRIMARY KEY(a, b))"); + execute("INSERT INTO %S (a, b, c, d, e, f, g, h) VALUES (0, 2, 3, 4, 5.5, 6.5, 7, 8.5)"); + + assertInvalidThrowMessage("the operation 'tinyint / tinyint' failed: / by zero", + OperationExecutionException.class, + "SELECT a / a FROM %s WHERE a = 0 AND b = 2"); + + assertInvalidThrowMessage("the operation 'smallint / tinyint' failed: / by zero", + OperationExecutionException.class, + "SELECT b / a FROM %s WHERE a = 0 AND b = 2"); + + assertInvalidThrowMessage("the operation 'int / tinyint' failed: / by zero", + OperationExecutionException.class, + "SELECT c / a FROM %s WHERE a = 0 AND b = 2"); + + assertInvalidThrowMessage("the operation 'bigint / tinyint' failed: / by zero", + OperationExecutionException.class, + "SELECT d / a FROM %s WHERE a = 0 AND b = 2"); + + assertInvalidThrowMessage("the operation 'smallint / smallint' failed: / by zero", + OperationExecutionException.class, + "SELECT a FROM %s WHERE a = 0 AND b = 10/0"); + + assertRows(execute("SELECT e / a FROM %s WHERE a = 0 AND b = 2"), row(Float.POSITIVE_INFINITY)); + assertRows(execute("SELECT f / a FROM %s WHERE a = 0 AND b = 2"), row(Double.POSITIVE_INFINITY)); + + assertInvalidThrowMessage("the operation 'varint / tinyint' failed: BigInteger divide by zero", + OperationExecutionException.class, + "SELECT g / a FROM %s WHERE a = 0 AND b = 2"); + + assertInvalidThrowMessage("the operation 'decimal / tinyint' failed: Division by zero", + OperationExecutionException.class, + "SELECT h / a FROM %s WHERE a = 0 AND b = 2"); + } + + @Test + public void testWithNanAndInfinity() throws Throwable + { + createTable("CREATE TABLE %s (a int PRIMARY KEY, b double, c decimal)"); + assertInvalidMessage("Ambiguous '+' operation: use type casts to disambiguate", + "INSERT INTO %S (a, b, c) VALUES (? + 1, ?, ?)", 0, Double.NaN, BigDecimal.valueOf(1)); + + execute("INSERT INTO %S (a, b, c) VALUES ((int) ? + 1, -?, ?)", 0, Double.NaN, BigDecimal.valueOf(1)); + + assertRows(execute("SELECT * FROM %s"), row(1, Double.NaN, BigDecimal.valueOf(1))); + + assertRows(execute("SELECT a + NAN, b + 1 FROM %s"), row(Double.NaN, Double.NaN)); + assertInvalidThrowMessage("the operation 'decimal + double' failed: A NaN cannot be converted into a decimal", + OperationExecutionException.class, + "SELECT c + NAN FROM %s"); + + assertRows(execute("SELECT a + (float) NAN, b + 1 FROM %s"), row(Float.NaN, Double.NaN)); + assertInvalidThrowMessage("the operation 'decimal + float' failed: A NaN cannot be converted into a decimal", + OperationExecutionException.class, + "SELECT c + (float) NAN FROM %s"); + + execute("INSERT INTO %S (a, b, c) VALUES (?, ?, ?)", 1, Double.POSITIVE_INFINITY, BigDecimal.valueOf(1)); + assertRows(execute("SELECT * FROM %s"), row(1, Double.POSITIVE_INFINITY, BigDecimal.valueOf(1))); + + assertRows(execute("SELECT a + INFINITY, b + 1 FROM %s"), row(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)); + assertInvalidThrowMessage("the operation 'decimal + double' failed: An infinite number cannot be converted into a decimal", + OperationExecutionException.class, + "SELECT c + INFINITY FROM %s"); + + assertRows(execute("SELECT a + (float) INFINITY, b + 1 FROM %s"), row(Float.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)); + assertInvalidThrowMessage("the operation 'decimal + float' failed: An infinite number cannot be converted into a decimal", + OperationExecutionException.class, + "SELECT c + (float) INFINITY FROM %s"); + + execute("INSERT INTO %S (a, b, c) VALUES (?, ?, ?)", 1, Double.NEGATIVE_INFINITY, BigDecimal.valueOf(1)); + assertRows(execute("SELECT * FROM %s"), row(1, Double.NEGATIVE_INFINITY, BigDecimal.valueOf(1))); + + assertRows(execute("SELECT a + -INFINITY, b + 1 FROM %s"), row(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); + assertInvalidThrowMessage("the operation 'decimal + double' failed: An infinite number cannot be converted into a decimal", + OperationExecutionException.class, + "SELECT c + -INFINITY FROM %s"); + + assertRows(execute("SELECT a + (float) -INFINITY, b + 1 FROM %s"), row(Float.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); + assertInvalidThrowMessage("the operation 'decimal + float' failed: An infinite number cannot be converted into a decimal", + OperationExecutionException.class, + "SELECT c + (float) -INFINITY FROM %s"); + } + + @Test + public void testInvalidTypes() throws Throwable + { + createTable("CREATE TABLE %s (a int PRIMARY KEY, b boolean, c text)"); + execute("INSERT INTO %S (a, b, c) VALUES (?, ?, ?)", 1, true, "test"); + + assertInvalidMessage("the '+' operation is not supported between a and b", "SELECT a + b FROM %s"); + assertInvalidMessage("the '+' operation is not supported between b and c", "SELECT b + c FROM %s"); + assertInvalidMessage("the '+' operation is not supported between b and 1", "SELECT b + 1 FROM %s"); + assertInvalidMessage("the '+' operation is not supported between 1 and b", "SELECT 1 + b FROM %s"); + assertInvalidMessage("the '+' operation is not supported between b and NaN", "SELECT b + NaN FROM %s"); + assertInvalidMessage("the '/' operation is not supported between a and b", "SELECT a / b FROM %s"); + assertInvalidMessage("the '/' operation is not supported between b and c", "SELECT b / c FROM %s"); + assertInvalidMessage("the '/' operation is not supported between b and 1", "SELECT b / 1 FROM %s"); + assertInvalidMessage("the '/' operation is not supported between NaN and b", "SELECT NaN / b FROM %s"); + assertInvalidMessage("the '/' operation is not supported between -Infinity and b", "SELECT -Infinity / b FROM %s"); + } + + @Test + public void testOverflow() throws Throwable + { + createTable("CREATE TABLE %s (a int PRIMARY KEY, b tinyint, c smallint)"); + execute("INSERT INTO %S (a, b, c) VALUES (?, ?, ?)", 1, (byte) 1, (short) 1); + assertRows(execute("SELECT a + (int) ?, b + (tinyint) ?, c + (smallint) ? FROM %s", 1, (byte) 1, (short) 1), + row(2, (byte) 2,(short) 2)); + assertRows(execute("SELECT a + (int) ?, b + (tinyint) ?, c + (smallint) ? FROM %s", Integer.MAX_VALUE, Byte.MAX_VALUE, Short.MAX_VALUE), + row(Integer.MIN_VALUE, Byte.MIN_VALUE, Short.MIN_VALUE)); + } +}