Unverified 提交 685dd6b6 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1134 from katzyn/misc

Detect possible overflow in integer division and optimize some code
...@@ -3259,7 +3259,7 @@ public class Parser { ...@@ -3259,7 +3259,7 @@ public class Parser {
r = ValueExpression.get(ValueInt.get(Integer.MIN_VALUE)); r = ValueExpression.get(ValueInt.get(Integer.MIN_VALUE));
} else if (r.getType() == Value.DECIMAL && } else if (r.getType() == Value.DECIMAL &&
r.getValue(session).getBigDecimal() r.getValue(session).getBigDecimal()
.compareTo(ValueLong.MIN_BD) == 0) { .compareTo(Value.MIN_LONG_DECIMAL) == 0) {
// convert Long.MIN_VALUE to type 'long' // convert Long.MIN_VALUE to type 'long'
// (Long.MAX_VALUE+1 is of type 'decimal') // (Long.MAX_VALUE+1 is of type 'decimal')
r = ValueExpression.get(ValueLong.MIN); r = ValueExpression.get(ValueLong.MIN);
......
...@@ -51,7 +51,7 @@ public class DropSchema extends DefineCommand { ...@@ -51,7 +51,7 @@ public class DropSchema extends DefineCommand {
} }
if (dropAction == ConstraintActionType.RESTRICT && !schema.isEmpty()) { if (dropAction == ConstraintActionType.RESTRICT && !schema.isEmpty()) {
StatementBuilder buff = new StatementBuilder(); StatementBuilder buff = new StatementBuilder();
for (SchemaObject object : schema.getAll()) { for (SchemaObject object : schema.getAll(null)) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(object.getName()); buff.append(object.getName());
} }
......
...@@ -1564,7 +1564,7 @@ public class Database implements DataHandler { ...@@ -1564,7 +1564,7 @@ public class Database implements DataHandler {
initMetaTables(); initMetaTables();
ArrayList<SchemaObject> list = new ArrayList<>(); ArrayList<SchemaObject> list = new ArrayList<>();
for (Schema schema : schemas.values()) { for (Schema schema : schemas.values()) {
list.addAll(schema.getAll()); schema.getAll(list);
} }
return list; return list;
} }
...@@ -1581,7 +1581,7 @@ public class Database implements DataHandler { ...@@ -1581,7 +1581,7 @@ public class Database implements DataHandler {
} }
ArrayList<SchemaObject> list = new ArrayList<>(); ArrayList<SchemaObject> list = new ArrayList<>();
for (Schema schema : schemas.values()) { for (Schema schema : schemas.values()) {
list.addAll(schema.getAll(type)); schema.getAll(type, list);
} }
return list; return list;
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
package org.h2.schema; package org.h2.schema;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
...@@ -591,30 +592,46 @@ public class Schema extends DbObjectBase { ...@@ -591,30 +592,46 @@ public class Schema extends DbObjectBase {
/** /**
* Get all objects. * Get all objects.
* *
* @return a (possible empty) list of all objects * @param addTo
* list to add objects to, or {@code null} to allocate a new
* list
* @return the specified list with added objects, or a new (possibly empty) list
* with all objects
*/ */
public ArrayList<SchemaObject> getAll() { public ArrayList<SchemaObject> getAll(ArrayList<SchemaObject> addTo) {
ArrayList<SchemaObject> all = Utils.newSmallArrayList(); if (addTo == null) {
all.addAll(getMap(DbObject.TABLE_OR_VIEW).values()); addTo = Utils.newSmallArrayList();
all.addAll(getMap(DbObject.SYNONYM).values()); }
all.addAll(getMap(DbObject.SEQUENCE).values()); addTo.addAll(tablesAndViews.values());
all.addAll(getMap(DbObject.INDEX).values()); addTo.addAll(synonyms.values());
all.addAll(getMap(DbObject.TRIGGER).values()); addTo.addAll(sequences.values());
all.addAll(getMap(DbObject.CONSTRAINT).values()); addTo.addAll(indexes.values());
all.addAll(getMap(DbObject.CONSTANT).values()); addTo.addAll(triggers.values());
all.addAll(getMap(DbObject.FUNCTION_ALIAS).values()); addTo.addAll(constraints.values());
return all; addTo.addAll(constants.values());
addTo.addAll(functions.values());
return addTo;
} }
/** /**
* Get all objects of the given type. * Get all objects of the given type.
* *
* @param type the object type * @param type
* @return a (possible empty) list of all objects * the object type
*/ * @param addTo
public ArrayList<SchemaObject> getAll(int type) { * list to add objects to, or {@code null} to allocate a new
Map<String, SchemaObject> map = getMap(type); * list
return new ArrayList<>(map.values()); * @return the specified list with added objects, or a new (possibly empty) list
* with objects of the given type
*/
public ArrayList<SchemaObject> getAll(int type, ArrayList<SchemaObject> addTo) {
Collection<SchemaObject> values = getMap(type).values();
if (addTo != null) {
addTo.addAll(values);
} else {
addTo = new ArrayList<>(values);
}
return addTo;
} }
/** /**
......
...@@ -179,11 +179,14 @@ public abstract class Value { ...@@ -179,11 +179,14 @@ public abstract class Value {
*/ */
public static final int TYPE_COUNT = ENUM; public static final int TYPE_COUNT = ENUM;
private static SoftReference<Value[]> softCache = private static SoftReference<Value[]> softCache;
new SoftReference<>(null);
private static final BigDecimal MAX_LONG_DECIMAL = private static final BigDecimal MAX_LONG_DECIMAL =
BigDecimal.valueOf(Long.MAX_VALUE); BigDecimal.valueOf(Long.MAX_VALUE);
private static final BigDecimal MIN_LONG_DECIMAL =
/**
* The smallest Long value, as a BigDecimal.
*/
public static final BigDecimal MIN_LONG_DECIMAL =
BigDecimal.valueOf(Long.MIN_VALUE); BigDecimal.valueOf(Long.MIN_VALUE);
/** /**
...@@ -396,11 +399,8 @@ public abstract class Value { ...@@ -396,11 +399,8 @@ public abstract class Value {
static Value cache(Value v) { static Value cache(Value v) {
if (SysProperties.OBJECT_CACHE) { if (SysProperties.OBJECT_CACHE) {
int hash = v.hashCode(); int hash = v.hashCode();
if (softCache == null) { Value[] cache;
softCache = new SoftReference<>(null); if (softCache == null || (cache = softCache.get()) == null) {
}
Value[] cache = softCache.get();
if (cache == null) {
cache = new Value[SysProperties.OBJECT_CACHE_SIZE]; cache = new Value[SysProperties.OBJECT_CACHE_SIZE];
softCache = new SoftReference<>(cache); softCache = new SoftReference<>(cache);
} }
......
...@@ -75,7 +75,7 @@ public class ValueByte extends Value { ...@@ -75,7 +75,7 @@ public class ValueByte extends Value {
if (other.value == 0) { if (other.value == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL()); throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
} }
return ValueByte.get((byte) (value / other.value)); return checkRange(value / other.value);
} }
@Override @Override
......
...@@ -100,11 +100,15 @@ public class ValueInt extends Value { ...@@ -100,11 +100,15 @@ public class ValueInt extends Value {
@Override @Override
public Value divide(Value v) { public Value divide(Value v) {
ValueInt other = (ValueInt) v; int y = ((ValueInt) v).value;
if (other.value == 0) { if (y == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL()); throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
} }
return ValueInt.get(value / other.value); int x = value;
if (x == Integer.MIN_VALUE && y == -1) {
throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1, "2147483648");
}
return ValueInt.get(x / y);
} }
@Override @Override
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
*/ */
package org.h2.value; package org.h2.value;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -33,11 +32,6 @@ public class ValueLong extends Value { ...@@ -33,11 +32,6 @@ public class ValueLong extends Value {
*/ */
public static final BigInteger MAX_BI = BigInteger.valueOf(Long.MAX_VALUE); public static final BigInteger MAX_BI = BigInteger.valueOf(Long.MAX_VALUE);
/**
* The smallest Long value, as a BigDecimal.
*/
public static final BigDecimal MIN_BD = BigDecimal.valueOf(Long.MIN_VALUE);
/** /**
* The precision in digits. * The precision in digits.
*/ */
...@@ -49,7 +43,6 @@ public class ValueLong extends Value { ...@@ -49,7 +43,6 @@ public class ValueLong extends Value {
*/ */
public static final int DISPLAY_SIZE = 20; public static final int DISPLAY_SIZE = 20;
private static final BigInteger MIN_BI = BigInteger.valueOf(Long.MIN_VALUE);
private static final int STATIC_SIZE = 100; private static final int STATIC_SIZE = 100;
private static final ValueLong[] STATIC_CACHE; private static final ValueLong[] STATIC_CACHE;
...@@ -116,41 +109,33 @@ public class ValueLong extends Value { ...@@ -116,41 +109,33 @@ public class ValueLong extends Value {
return add(other.negate()); return add(other.negate());
} }
private static boolean isInteger(long a) {
return a >= Integer.MIN_VALUE && a <= Integer.MAX_VALUE;
}
@Override @Override
public Value multiply(Value v) { public Value multiply(Value v) {
ValueLong other = (ValueLong) v; long x = value;
long result = value * other.value; long y = ((ValueLong) v).value;
if (value == 0 || value == 1 || other.value == 0 || other.value == 1) { long result = x * y;
return ValueLong.get(result); // Check whether numbers are large enough to overflow and second value != 0
} if ((Math.abs(x) | Math.abs(y)) >>> 31 != 0 && y != 0
if (isInteger(value) && isInteger(other.value)) { // Check with division
return ValueLong.get(result); && (result / y != x
} // Also check the special condition that is not handled above
// just checking one case is not enough: Long.MIN_VALUE * -1 || x == Long.MIN_VALUE && y == -1)) {
// probably this is correct but I'm not sure
// if (result / value == other.value && result / other.value == value) {
// return ValueLong.get(result);
//}
BigInteger bv = BigInteger.valueOf(value);
BigInteger bo = BigInteger.valueOf(other.value);
BigInteger br = bv.multiply(bo);
if (br.compareTo(MIN_BI) < 0 || br.compareTo(MAX_BI) > 0) {
throw getOverflow(); throw getOverflow();
} }
return ValueLong.get(br.longValue()); return ValueLong.get(result);
} }
@Override @Override
public Value divide(Value v) { public Value divide(Value v) {
ValueLong other = (ValueLong) v; long y = ((ValueLong) v).value;
if (other.value == 0) { if (y == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL()); throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
} }
return ValueLong.get(value / other.value); long x = value;
if (x == Long.MIN_VALUE && y == -1) {
throw getOverflow();
}
return ValueLong.get(x / y);
} }
@Override @Override
......
...@@ -75,7 +75,7 @@ public class ValueShort extends Value { ...@@ -75,7 +75,7 @@ public class ValueShort extends Value {
if (other.value == 0) { if (other.value == 0) {
throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL()); throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
} }
return ValueShort.get((short) (value / other.value)); return checkRange(value / other.value);
} }
@Override @Override
......
...@@ -2,3 +2,52 @@ ...@@ -2,3 +2,52 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html). -- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group -- Initial Developer: H2 Group
-- --
-- Multiplication
SELECT CAST(-4294967296 AS BIGINT) * CAST (2147483648 AS BIGINT);
>> -9223372036854775808
SELECT CAST(4294967296 AS BIGINT) * CAST (-2147483648 AS BIGINT);
>> -9223372036854775808
SELECT CAST(-2147483648 AS BIGINT) * CAST (4294967296 AS BIGINT);
>> -9223372036854775808
SELECT CAST(2147483648 AS BIGINT) * CAST (-4294967296 AS BIGINT);
>> -9223372036854775808
SELECT CAST(4294967296 AS BIGINT) * CAST (2147483648 AS BIGINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
SELECT CAST(-4294967296 AS BIGINT) * CAST (-2147483648 AS BIGINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
SELECT CAST(2147483648 AS BIGINT) * CAST (4294967296 AS BIGINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
SELECT CAST(-2147483648 AS BIGINT) * CAST (-4294967296 AS BIGINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
SELECT CAST(-9223372036854775808 AS BIGINT) * CAST(1 AS BIGINT);
>> -9223372036854775808
SELECT CAST(-9223372036854775808 AS BIGINT) * CAST(-1 AS BIGINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
SELECT CAST(1 AS BIGINT) * CAST(-9223372036854775808 AS BIGINT);
>> -9223372036854775808
SELECT CAST(-1 AS BIGINT) * CAST(-9223372036854775808 AS BIGINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
-- Division
SELECT CAST(1 AS BIGINT) / CAST(0 AS BIGINT);
> exception DIVISION_BY_ZERO_1
SELECT CAST(-9223372036854775808 AS BIGINT) / CAST(1 AS BIGINT);
>> -9223372036854775808
SELECT CAST(-9223372036854775808 AS BIGINT) / CAST(-1 AS BIGINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
...@@ -2,3 +2,14 @@ ...@@ -2,3 +2,14 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html). -- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group -- Initial Developer: H2 Group
-- --
-- Division
SELECT CAST(1 AS INT) / CAST(0 AS INT);
> exception DIVISION_BY_ZERO_1
SELECT CAST(-2147483648 AS INT) / CAST(1 AS INT);
>> -2147483648
SELECT CAST(-2147483648 AS INT) / CAST(-1 AS INT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
...@@ -2,3 +2,14 @@ ...@@ -2,3 +2,14 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html). -- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group -- Initial Developer: H2 Group
-- --
-- Division
SELECT CAST(1 AS SMALLINT) / CAST(0 AS SMALLINT);
> exception DIVISION_BY_ZERO_1
SELECT CAST(-32768 AS SMALLINT) / CAST(1 AS SMALLINT);
>> -32768
SELECT CAST(-32768 AS SMALLINT) / CAST(-1 AS SMALLINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
...@@ -2,3 +2,14 @@ ...@@ -2,3 +2,14 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html). -- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group -- Initial Developer: H2 Group
-- --
-- Division
SELECT CAST(1 AS TINYINT) / CAST(0 AS TINYINT);
> exception DIVISION_BY_ZERO_1
SELECT CAST(-128 AS TINYINT) / CAST(1 AS TINYINT);
>> -128
SELECT CAST(-128 AS TINYINT) / CAST(-1 AS TINYINT);
> exception NUMERIC_VALUE_OUT_OF_RANGE_1
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论