提交 eea52ad7 authored 作者: Thomas Mueller's avatar Thomas Mueller

New system function TRUNCATE_VALUE to truncate a value to the required precision.

上级 f3f6c2a9
......@@ -3644,6 +3644,18 @@ The value is unique across database restarts (values are not re-used).
CALL TRANSACTION_ID()
"
"Functions (System)","TRUNCATE_VALUE","
TRUNCATE_VALUE(value, precisionInt, forceBoolean)
","
Truncate a value to the required precision.
The precision of the returned value may be a bit larger than requested,
because fixed precision values are not truncated (unlike the numeric TRUNCATE method).
Unlike CAST, the truncating a decimal value may lose precision if the force flag is set to true.
The method returns a value with the same data type as the first parameter.
","
CALL TRUNCATE_VALUE(X, 10, TRUE);
"
"Functions (System)","USER","
{ USER | CURRENT_USER } ()
","
......
......@@ -95,7 +95,7 @@ public class Function extends Expression implements FunctionCall {
CASE = 206, NEXTVAL = 207, CURRVAL = 208, ARRAY_GET = 209, CSVREAD = 210, CSVWRITE = 211,
MEMORY_FREE = 212, MEMORY_USED = 213, LOCK_MODE = 214, SCHEMA = 215, SESSION_ID = 216, ARRAY_LENGTH = 217,
LINK_SCHEMA = 218, GREATEST = 219, LEAST = 220, CANCEL_SESSION = 221, SET = 222, TABLE = 223, TABLE_DISTINCT = 224,
FILE_READ = 225, TRANSACTION_ID = 226;
FILE_READ = 225, TRANSACTION_ID = 226, TRUNCATE_VALUE = 227;
private static final int VAR_ARGS = -1;
private static final long PRECISION_UNKNOWN = -1;
......@@ -297,6 +297,7 @@ public class Function extends Expression implements FunctionCall {
addFunctionWithNull("CASEWHEN", CASEWHEN, 3, Value.NULL);
addFunctionWithNull("CONVERT", CONVERT, 1, Value.NULL);
addFunctionWithNull("CAST", CAST, 1, Value.NULL);
addFunctionWithNull("TRUNCATE_VALUE", TRUNCATE_VALUE, 3, Value.NULL);
addFunctionWithNull("COALESCE", COALESCE, VAR_ARGS, Value.NULL);
addFunctionWithNull("NVL", COALESCE, VAR_ARGS, Value.NULL);
addFunctionWithNull("NULLIF", NULLIF, 2, Value.NULL);
......@@ -715,7 +716,7 @@ public class Function extends Expression implements FunctionCall {
v0 = v0.convertTo(dataType);
Mode mode = database.getMode();
v0 = v0.convertScale(mode.convertOnlyToSmallerScale, scale);
v0 = v0.convertPrecision(getPrecision());
v0 = v0.convertPrecision(getPrecision(), false);
result = v0;
break;
}
......@@ -1150,6 +1151,10 @@ public class Function extends Expression implements FunctionCall {
}
break;
}
case TRUNCATE_VALUE: {
result = v0.convertPrecision(v1.getLong(), v2.getBoolean());
break;
}
default:
throw DbException.throwInternalError("type=" + info.type);
}
......@@ -1678,6 +1683,7 @@ public class Function extends Expression implements FunctionCall {
break;
case CAST:
case CONVERT:
case TRUNCATE_VALUE:
// data type, precision and scale is already set
t = dataType;
p = precision;
......
......@@ -110,7 +110,7 @@ public class TableFunction extends Function {
Column c = columnList[j];
v = l[row];
v = c.convert(v);
v = v.convertPrecision(c.getPrecision());
v = v.convertPrecision(c.getPrecision(), false);
v = v.convertScale(true, c.getScale());
}
r[j] = v;
......
......@@ -1351,6 +1351,10 @@ Returns the result set."
TRANSACTION_ID()
","
Returns the current transaction id for this session."
"Functions (System)","TRUNCATE_VALUE","
TRUNCATE_VALUE(value, precisionInt, forceBoolean)
","
Truncate a value to the required precision."
"Functions (System)","USER","
{ USER | CURRENT_USER } ()
","
......
......@@ -910,27 +910,18 @@ public abstract class Value {
}
/**
* Convert the precision to the requested value.
* Convert the precision to the requested value. The precision of the
* returned value may be somewhat larger than requested, because values with
* a fixed precision are not truncated.
*
* @param precision the new precision
* @param force true if losing numeric precision is allowed
* @return the new value
*/
public Value convertPrecision(long precision) {
public Value convertPrecision(long precision, boolean force) {
return this;
}
/**
* Trim the value to the requested precision.
* Unlike convertPrecision, this will always work
* (possibly losing precision).
*
* @param precision the maximum precision
* @return the new value
*/
public Value trim(long precision) {
return convertPrecision(precision);
}
private static byte convertToByte(long x) {
if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) {
throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE);
......
......@@ -7,8 +7,10 @@
package org.h2.value;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import org.h2.engine.Constants;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StatementBuilder;
/**
......@@ -55,7 +57,11 @@ public class ValueArray extends Value {
}
public long getPrecision() {
return 0;
long p = 0;
for (Value v : values) {
p += v.getPrecision();
}
return p;
}
public String getString() {
......@@ -156,4 +162,24 @@ public class ValueArray extends Value {
return memory;
}
public Value convertPrecision(long precision, boolean force) {
if (!force) {
return this;
}
ArrayList<Value> list = New.arrayList();
for (Value v : values) {
v = v.convertPrecision(precision, true);
// empty byte arrays or strings have precision 0
// they count as precision 1 here
precision -= Math.max(1, v.getPrecision());
if (precision < 0) {
break;
}
list.add(v);
}
Value[] array = new Value[list.size()];
list.toArray(array);
return get(array);
}
}
......@@ -117,4 +117,14 @@ public class ValueBytes extends Value {
return other instanceof ValueBytes && Utils.compareNotNull(value, ((ValueBytes) other).value) == 0;
}
public Value convertPrecision(long precision, boolean force) {
if (value.length <= precision) {
return this;
}
int len = MathUtils.convertLongToInt(precision);
byte[] buff = new byte[len];
System.arraycopy(value, 0, buff, 0, len);
return get(buff);
}
}
......@@ -184,11 +184,14 @@ public class ValueDecimal extends Value {
return ValueDecimal.get(bd);
}
public Value convertPrecision(long newPrecision) {
if (getPrecision() <= newPrecision) {
public Value convertPrecision(long precision, boolean force) {
if (getPrecision() <= precision) {
return this;
}
throw DbException.get(ErrorCode.VALUE_TOO_LARGE_FOR_PRECISION_1, "" + newPrecision);
if (force) {
return get(BigDecimal.valueOf(value.doubleValue()));
}
throw DbException.get(ErrorCode.VALUE_TOO_LARGE_FOR_PRECISION_1, "" + precision);
}
/**
......
......@@ -799,4 +799,17 @@ public class ValueLob extends Value {
return lob;
}
public Value convertPrecision(long precision, boolean force) {
if (this.precision <= precision) {
return this;
}
ValueLob lob;
if (type == CLOB) {
lob = ValueLob.createClob(getReader(), precision, handler);
} else {
lob = ValueLob.createBlob(getInputStream(), precision, handler);
}
return lob;
}
}
......@@ -541,4 +541,17 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
return (int) m;
}
public Value convertPrecision(long precision, boolean force) {
if (this.precision <= precision) {
return this;
}
ValueLob lob;
if (type == CLOB) {
lob = ValueLob.createClob(getReader(), precision, handler);
} else {
lob = ValueLob.createBlob(getInputStream(), precision, handler);
}
return lob;
}
}
......@@ -10,7 +10,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.h2.message.DbException;
import org.h2.tools.SimpleResultSet;
import org.h2.util.StatementBuilder;
......@@ -79,7 +78,7 @@ public class ValueResultSet extends Value {
}
public long getPrecision() {
return 0;
return Integer.MAX_VALUE;
}
public int getDisplaySize() {
......@@ -142,4 +141,11 @@ public class ValueResultSet extends Value {
return "";
}
public Value convertPrecision(long precision, boolean force) {
if (!force) {
return this;
}
return ValueResultSet.get(new SimpleResultSet());
}
}
......@@ -67,7 +67,7 @@ public class ValueString extends Value {
return value.length() * 2 + 48;
}
public Value convertPrecision(long precision) {
public Value convertPrecision(long precision, boolean force) {
if (precision == 0 || value.length() <= precision) {
return this;
}
......@@ -136,7 +136,7 @@ public class ValueString extends Value {
* @param s the string
* @return the value
*/
protected Value getNew(String s) {
protected ValueString getNew(String s) {
return ValueString.get(s);
}
......
--- special grammar and test cases ---------------------------------------------------------------------------------------------
call cast_precision('Test 123', 4, false);
> 'Test'
> ------
> Test
> rows: 1
call cast_precision(1234567890.123456789, 4, false);
> exception
call cast_precision(1234567890.123456789, 4, true);
> 1234567890.1234567
> ------------------
> 1234567890.1234567
> rows: 1
select * from dual where cast('xx' as varchar_ignorecase(1)) = 'X' and cast('x x ' as char(2)) = 'x';
> X
> -
......
......@@ -21,10 +21,14 @@ import org.h2.test.TestBase;
import org.h2.tools.SimpleResultSet;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueBytes;
import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble;
import org.h2.value.ValueFloat;
import org.h2.value.ValueLobDb;
import org.h2.value.ValueResultSet;
import org.h2.value.ValueString;
import org.h2.value.ValueUuid;
/**
......@@ -42,6 +46,7 @@ public class TestValue extends TestBase {
}
public void test() throws SQLException {
testCastTrim();
testValueResultSet();
testDataType();
testUUID();
......@@ -52,6 +57,63 @@ public class TestValue extends TestBase {
testModulusOperator();
}
private void testCastTrim() {
Value v;
String spaces = new String(new char[100]).replace((char) 0, ' ');
v = ValueArray.get(new Value[]{ValueString.get("hello"), ValueString.get("world")});
assertEquals(10, v.getPrecision());
assertEquals(5, v.convertPrecision(5, true).getPrecision());
v = ValueArray.get(new Value[]{ValueString.get(""), ValueString.get("")});
assertEquals(0, v.getPrecision());
assertEquals("('')", v.convertPrecision(1, true).toString());
v = ValueBytes.get(spaces.getBytes());
assertEquals(100, v.getPrecision());
assertEquals(10, v.convertPrecision(10, false).getPrecision());
assertEquals(10, v.convertPrecision(10, false).getBytes().length);
assertEquals(32, v.convertPrecision(10, false).getBytes()[9]);
assertEquals(10, v.convertPrecision(10, true).getPrecision());
v = ValueDecimal.get(new BigDecimal("1234567890.123456789"));
assertEquals(19, v.getPrecision());
assertEquals("1234567890.1234567", v.convertPrecision(10, true).getString());
try {
v.convertPrecision(10, false);
} catch (DbException e) {
assertEquals(ErrorCode.VALUE_TOO_LARGE_FOR_PRECISION_1, e.getErrorCode());
}
v = ValueLobDb.createSmallLob(Value.CLOB, spaces.getBytes(), 100);
assertEquals(100, v.getPrecision());
assertEquals(10, v.convertPrecision(10, false).getPrecision());
assertEquals(10, v.convertPrecision(10, false).getString().length());
assertEquals(" ", v.convertPrecision(10, false).getString());
assertEquals(10, v.convertPrecision(10, true).getPrecision());
v = ValueLobDb.createSmallLob(Value.BLOB, spaces.getBytes(), 100);
assertEquals(100, v.getPrecision());
assertEquals(10, v.convertPrecision(10, false).getPrecision());
assertEquals(10, v.convertPrecision(10, false).getBytes().length);
assertEquals(32, v.convertPrecision(10, false).getBytes()[9]);
assertEquals(10, v.convertPrecision(10, true).getPrecision());
ResultSet rs = new SimpleResultSet();
v = ValueResultSet.get(rs);
assertEquals(Integer.MAX_VALUE, v.getPrecision());
assertEquals(Integer.MAX_VALUE, v.convertPrecision(10, false).getPrecision());
assertTrue(rs == v.convertPrecision(10, false).getObject());
assertFalse(rs == v.convertPrecision(10, true).getObject());
assertEquals(Integer.MAX_VALUE, v.convertPrecision(10, true).getPrecision());
v = ValueString.get(spaces);
assertEquals(100, v.getPrecision());
assertEquals(10, v.convertPrecision(10, false).getPrecision());
assertEquals(" ", v.convertPrecision(10, false).getString());
assertEquals(" ", v.convertPrecision(10, true).getString());
}
private void testValueResultSet() throws SQLException {
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("ID", Types.INTEGER, 0, 0);
......@@ -67,7 +129,7 @@ public class TestValue extends TestBase {
v = ValueResultSet.getCopy(rs, 2);
assertEquals(0, v.hashCode());
assertEquals(Integer.MAX_VALUE, v.getDisplaySize());
assertEquals(0, v.getPrecision());
assertEquals(Integer.MAX_VALUE, v.getPrecision());
assertEquals(0, v.getScale());
assertEquals("", v.getSQL());
assertEquals(Value.RESULT_SET, v.getType());
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论