提交 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' ...@@ -1748,7 +1748,7 @@ ID=1 OR NAME='Hi'
" "
"Other Grammar","Factor"," "Other Grammar","Factor","
term [ { { * | / } term } [...] ] term [ { { * | / | % } term } [...] ]
"," ","
A value or a numeric factor. A value or a numeric factor.
"," ","
......
...@@ -18,7 +18,9 @@ Change Log ...@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <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". 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 </li><li>Some right outer joins with invalid column referenced (typos) threw a NullPointerException instead of
"column not found" exception. "column not found" exception.
......
...@@ -181,7 +181,6 @@ See also <a href="build.html#providing_patches">Providing Patches</a>. ...@@ -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>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>Allow execution time prepare for SELECT * FROM CSVREAD(?, 'columnNameString')
</li><li>Support data type INTERVAL </li><li>Support data type INTERVAL
</li><li>Support % operator (modulo).
</li><li>Support nested transactions (possibly using savepoints internally). </li><li>Support nested transactions (possibly using savepoints internally).
</li><li>Add a benchmark for bigger databases, and one for many users. </li><li>Add a benchmark for bigger databases, and one for many users.
</li><li>Compression in the result set over TCP/IP. </li><li>Compression in the result set over TCP/IP.
......
...@@ -123,8 +123,8 @@ import org.h2.table.IndexColumn; ...@@ -123,8 +123,8 @@ import org.h2.table.IndexColumn;
import org.h2.table.RangeTable; import org.h2.table.RangeTable;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.table.TableView; import org.h2.table.TableView;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
...@@ -1996,6 +1996,8 @@ public class Parser { ...@@ -1996,6 +1996,8 @@ public class Parser {
r = new Operation(Operation.MULTIPLY, r, readTerm()); r = new Operation(Operation.MULTIPLY, r, readTerm());
} else if (readIf("/")) { } else if (readIf("/")) {
r = new Operation(Operation.DIVIDE, r, readTerm()); r = new Operation(Operation.DIVIDE, r, readTerm());
} else if (readIf("%")) {
r = new Operation(Operation.MODULUS, r, readTerm());
} else { } else {
return r; return r;
} }
...@@ -3237,6 +3239,7 @@ public class Parser { ...@@ -3237,6 +3239,7 @@ public class Parser {
case '}': case '}':
case '*': case '*':
case '/': case '/':
case '%':
case ';': case ';':
case ',': case ',':
case ':': case ':':
......
...@@ -53,6 +53,11 @@ public class Operation extends Expression { ...@@ -53,6 +53,11 @@ public class Operation extends Expression {
*/ */
public static final int NEGATE = 5; 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 int opType;
private Expression left, right; private Expression left, right;
private int dataType; private int dataType;
...@@ -92,6 +97,8 @@ public class Operation extends Expression { ...@@ -92,6 +97,8 @@ public class Operation extends Expression {
return "*"; return "*";
case DIVIDE: case DIVIDE:
return "/"; return "/";
case MODULUS:
return "%";
default: default:
throw DbException.throwInternalError("opType=" + opType); throw DbException.throwInternalError("opType=" + opType);
} }
...@@ -149,6 +156,11 @@ public class Operation extends Expression { ...@@ -149,6 +156,11 @@ public class Operation extends Expression {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} }
return l.divide(r); return l.divide(r);
case MODULUS:
if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
return ValueNull.INSTANCE;
}
return l.modulus(r);
default: default:
throw DbException.throwInternalError("type=" + opType); throw DbException.throwInternalError("type=" + opType);
} }
...@@ -181,6 +193,7 @@ public class Operation extends Expression { ...@@ -181,6 +193,7 @@ public class Operation extends Expression {
case MINUS: case MINUS:
case MULTIPLY: case MULTIPLY:
case DIVIDE: case DIVIDE:
case MODULUS:
right = right.optimize(session); right = right.optimize(session);
int l = left.getType(); int l = left.getType();
int r = right.getType(); int r = right.getType();
......
...@@ -582,7 +582,7 @@ andCondition [ { OR andCondition } [...] ] ...@@ -582,7 +582,7 @@ andCondition [ { OR andCondition } [...] ]
"," ","
Value or condition." Value or condition."
"Other Grammar","Factor"," "Other Grammar","Factor","
term [ { { * | / } term } [...] ] term [ { { * | / | % } term } [...] ]
"," ","
A value or a numeric factor." A value or a numeric factor."
"Other Grammar","Hex"," "Other Grammar","Hex","
......
...@@ -485,6 +485,16 @@ public abstract class Value { ...@@ -485,6 +485,16 @@ public abstract class Value {
throw throwUnsupportedExceptionForType("*"); 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. * Compare a value to the specified type.
* *
......
...@@ -72,6 +72,14 @@ public class ValueByte extends Value { ...@@ -72,6 +72,14 @@ public class ValueByte extends Value {
return ValueByte.get((byte) (value / other.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() { public String getSQL() {
return getString(); return getString();
} }
......
...@@ -104,6 +104,15 @@ public class ValueDecimal extends Value { ...@@ -104,6 +104,15 @@ public class ValueDecimal extends Value {
return ValueDecimal.get(bd); 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() { public String getSQL() {
return getString(); return getString();
} }
......
...@@ -69,6 +69,14 @@ public class ValueDouble extends Value { ...@@ -69,6 +69,14 @@ public class ValueDouble extends Value {
return ValueDouble.get(value / v2.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() { public String getSQL() {
if (value == Double.POSITIVE_INFINITY) { if (value == Double.POSITIVE_INFINITY) {
return "POWER(0, -1)"; return "POWER(0, -1)";
......
...@@ -68,6 +68,14 @@ public class ValueFloat extends Value { ...@@ -68,6 +68,14 @@ public class ValueFloat extends Value {
return ValueFloat.get(value / v2.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() { public String getSQL() {
if (value == Float.POSITIVE_INFINITY) { if (value == Float.POSITIVE_INFINITY) {
return "POWER(0, -1)"; return "POWER(0, -1)";
......
...@@ -102,6 +102,14 @@ public class ValueInt extends Value { ...@@ -102,6 +102,14 @@ public class ValueInt extends Value {
return ValueInt.get(value / other.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() { public String getSQL() {
return getString(); return getString();
} }
......
...@@ -137,6 +137,14 @@ public class ValueLong extends Value { ...@@ -137,6 +137,14 @@ public class ValueLong extends Value {
return ValueLong.get(value / other.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() { public String getSQL() {
return getString(); return getString();
} }
......
...@@ -72,6 +72,14 @@ public class ValueShort extends Value { ...@@ -72,6 +72,14 @@ public class ValueShort extends Value {
return ValueShort.get((short) (value / other.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() { public String getSQL() {
return getString(); return getString();
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package org.h2.test.unit; package org.h2.test.unit;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date; import java.sql.Date;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -14,10 +15,13 @@ import java.sql.Time; ...@@ -14,10 +15,13 @@ import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.UUID; import java.util.UUID;
import org.h2.constant.ErrorCode;
import org.h2.message.DbException;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble; import org.h2.value.ValueDouble;
import org.h2.value.ValueFloat; import org.h2.value.ValueFloat;
import org.h2.value.ValueResultSet; import org.h2.value.ValueResultSet;
...@@ -43,6 +47,9 @@ public class TestValue extends TestBase { ...@@ -43,6 +47,9 @@ public class TestValue extends TestBase {
testUUID(); testUUID();
testDouble(false); testDouble(false);
testDouble(true); testDouble(true);
testModulusDouble();
testModulusDecimal();
testModulusOperator();
} }
private void testValueResultSet() throws SQLException { private void testValueResultSet() throws SQLException {
...@@ -159,4 +166,42 @@ public class TestValue extends TestBase { ...@@ -159,4 +166,42 @@ public class TestValue extends TestBase {
assertEquals("00000000-0000-4000-8000-000000000000", min.getString()); 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 ...@@ -678,5 +678,5 @@ clip upgrades demonstrate inspectors
exceed identities differentiate inherited tracks strip suggestions exceed identities differentiate inherited tracks strip suggestions
registration sanity improperly annotate inheritance composite inspected registration sanity improperly annotate inheritance composite inspected
hurt imposes marshal policy upgrader configurations dark varray xlint executor 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 sine cosine tangent cotangent trigonometric
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论