You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2016/11/21 17:50:17 UTC

[2/4] cassandra git commit: Add support for arithmetic operators

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));
+    }
+}