提交 e9571b3c authored 作者: noelgrandin@gmail.com's avatar noelgrandin@gmail.com

Issue 292: Support for modulus operator

Added support for modulus operator in expressions
上级 6140fcaa
......@@ -1748,7 +1748,7 @@ ID=1 OR NAME='Hi'
"
"Other Grammar","Factor","
term [ { { * | / } term } [...] ]
term [ { { * | / | % } term } [...] ]
","
A value or a numeric factor.
","
......
......@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>Issue 288: Some right outer join queries failed to produce the correct result or
<ul>
<li>Support for the % operator (modulo) thanks to Noel Grandin.
</li><li>Issue 288: Some right outer join queries failed to produce the correct result or
threw exceptions such as "column x must be in the group by list".
</li><li>Some right outer joins with invalid column referenced (typos) threw a NullPointerException instead of
"column not found" exception.
......
......@@ -181,7 +181,6 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Compatibility with Derby and PostgreSQL: VALUES(1), (2); SELECT * FROM (VALUES (1), (2)) AS myTable(c1). Issue 221.
</li><li>Allow execution time prepare for SELECT * FROM CSVREAD(?, 'columnNameString')
</li><li>Support data type INTERVAL
</li><li>Support % operator (modulo).
</li><li>Support nested transactions (possibly using savepoints internally).
</li><li>Add a benchmark for bigger databases, and one for many users.
</li><li>Compression in the result set over TCP/IP.
......
......@@ -123,8 +123,8 @@ import org.h2.table.IndexColumn;
import org.h2.table.RangeTable;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.table.TableView;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StatementBuilder;
......@@ -1996,6 +1996,8 @@ public class Parser {
r = new Operation(Operation.MULTIPLY, r, readTerm());
} else if (readIf("/")) {
r = new Operation(Operation.DIVIDE, r, readTerm());
} else if (readIf("%")) {
r = new Operation(Operation.MODULUS, r, readTerm());
} else {
return r;
}
......@@ -3237,6 +3239,7 @@ public class Parser {
case '}':
case '*':
case '/':
case '%':
case ';':
case ',':
case ':':
......
......@@ -53,6 +53,11 @@ public class Operation extends Expression {
*/
public static final int NEGATE = 5;
/**
* This operation represents a modulus as in 5 % 2.
*/
public static final int MODULUS = 6;
private int opType;
private Expression left, right;
private int dataType;
......@@ -92,6 +97,8 @@ public class Operation extends Expression {
return "*";
case DIVIDE:
return "/";
case MODULUS:
return "%";
default:
throw DbException.throwInternalError("opType=" + opType);
}
......@@ -149,6 +156,11 @@ public class Operation extends Expression {
return ValueNull.INSTANCE;
}
return l.divide(r);
case MODULUS:
if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
return ValueNull.INSTANCE;
}
return l.modulus(r);
default:
throw DbException.throwInternalError("type=" + opType);
}
......@@ -181,6 +193,7 @@ public class Operation extends Expression {
case MINUS:
case MULTIPLY:
case DIVIDE:
case MODULUS:
right = right.optimize(session);
int l = left.getType();
int r = right.getType();
......
......@@ -582,7 +582,7 @@ andCondition [ { OR andCondition } [...] ]
","
Value or condition."
"Other Grammar","Factor","
term [ { { * | / } term } [...] ]
term [ { { * | / | % } term } [...] ]
","
A value or a numeric factor."
"Other Grammar","Hex","
......
......@@ -485,6 +485,16 @@ public abstract class Value {
throw throwUnsupportedExceptionForType("*");
}
/**
* Take the modulus with a value and return the result.
*
* @param v the value to take the modulus with
* @return the result
*/
public Value modulus(Value v) {
throw throwUnsupportedExceptionForType("%");
}
/**
* Compare a value to the specified type.
*
......
......@@ -72,6 +72,14 @@ public class ValueByte extends Value {
return ValueByte.get((byte) (value / other.value));
}
public Value modulus(Value v) {
ValueByte other = (ValueByte) v;
if (other.value == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
return ValueByte.get((byte) (value % other.value));
}
public String getSQL() {
return getString();
}
......
......@@ -104,6 +104,15 @@ public class ValueDecimal extends Value {
return ValueDecimal.get(bd);
}
public ValueDecimal modulus(Value v) {
ValueDecimal dec = (ValueDecimal) v;
if (dec.value.signum() == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
BigDecimal bd = value.remainder(dec.value);
return ValueDecimal.get(bd);
}
public String getSQL() {
return getString();
}
......
......@@ -69,6 +69,14 @@ public class ValueDouble extends Value {
return ValueDouble.get(value / v2.value);
}
public ValueDouble modulus(Value v) {
ValueDouble other = (ValueDouble) v;
if (other.value == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
return ValueDouble.get(value % other.value);
}
public String getSQL() {
if (value == Double.POSITIVE_INFINITY) {
return "POWER(0, -1)";
......
......@@ -68,6 +68,14 @@ public class ValueFloat extends Value {
return ValueFloat.get(value / v2.value);
}
public Value modulus(Value v) {
ValueFloat other = (ValueFloat) v;
if (other.value == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
return ValueFloat.get(value % other.value);
}
public String getSQL() {
if (value == Float.POSITIVE_INFINITY) {
return "POWER(0, -1)";
......
......@@ -102,6 +102,14 @@ public class ValueInt extends Value {
return ValueInt.get(value / other.value);
}
public Value modulus(Value v) {
ValueInt other = (ValueInt) v;
if (other.value == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
return ValueInt.get(value % other.value);
}
public String getSQL() {
return getString();
}
......
......@@ -137,6 +137,14 @@ public class ValueLong extends Value {
return ValueLong.get(value / other.value);
}
public Value modulus(Value v) {
ValueLong other = (ValueLong) v;
if (other.value == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
return ValueLong.get(this.value % other.value);
}
public String getSQL() {
return getString();
}
......
......@@ -72,6 +72,14 @@ public class ValueShort extends Value {
return ValueShort.get((short) (value / other.value));
}
public Value modulus(Value v) {
ValueShort other = (ValueShort) v;
if (other.value == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
return ValueShort.get((short) (value % other.value));
}
public String getSQL() {
return getString();
}
......
......@@ -7,6 +7,7 @@
package org.h2.test.unit;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
......@@ -14,10 +15,13 @@ import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.UUID;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.test.TestBase;
import org.h2.tools.SimpleResultSet;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble;
import org.h2.value.ValueFloat;
import org.h2.value.ValueResultSet;
......@@ -43,6 +47,9 @@ public class TestValue extends TestBase {
testUUID();
testDouble(false);
testDouble(true);
testModulusDouble();
testModulusDecimal();
testModulusOperator();
}
private void testValueResultSet() throws SQLException {
......@@ -159,4 +166,42 @@ public class TestValue extends TestBase {
assertEquals("00000000-0000-4000-8000-000000000000", min.getString());
}
private void testModulusDouble() {
ValueDouble vd1 = ValueDouble.get(12);
ValueDouble vd2 = ValueDouble.get(10);
ValueDouble vd3 = vd1.modulus(vd2);
assertEquals(2, vd3.getDouble());
try {
vd1.modulus(ValueDouble.get(0));
fail();
} catch (DbException e) {
assertEquals(ErrorCode.DIVISION_BY_ZERO_1, e.getErrorCode());
}
}
private void testModulusDecimal() {
ValueDecimal vd1 = ValueDecimal.get(new BigDecimal(12));
ValueDecimal vd2 = ValueDecimal.get(new BigDecimal(10));
ValueDecimal vd3 = vd1.modulus(vd2);
assertEquals(2, vd3.getDouble());
try {
vd1.modulus(ValueDecimal.get(new BigDecimal(0)));
fail();
} catch (DbException e) {
assertEquals(ErrorCode.DIVISION_BY_ZERO_1, e.getErrorCode());
}
}
private void testModulusOperator() throws SQLException {
Connection conn = getConnection("modulus");
try {
ResultSet rs = conn.createStatement().executeQuery("CALL 12 % 10");
rs.next();
assertEquals(2, rs.getInt(1));
} finally {
conn.close();
deleteDb("modulus");
}
}
}
......@@ -678,5 +678,5 @@ clip upgrades demonstrate inspectors
exceed identities differentiate inherited tracks strip suggestions
registration sanity improperly annotate inheritance composite inspected
hurt imposes marshal policy upgrader configurations dark varray xlint executor
completion inactivity exports maintains backside schwietzke rene rectangular
completion inactivity exports maintains backside schwietzke rene rectangular grandin noel
sine cosine tangent cotangent trigonometric
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论