提交 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). ...@@ -3644,6 +3644,18 @@ The value is unique across database restarts (values are not re-used).
CALL TRANSACTION_ID() 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"," "Functions (System)","USER","
{ USER | CURRENT_USER } () { USER | CURRENT_USER } ()
"," ","
......
...@@ -95,7 +95,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -95,7 +95,7 @@ public class Function extends Expression implements FunctionCall {
CASE = 206, NEXTVAL = 207, CURRVAL = 208, ARRAY_GET = 209, CSVREAD = 210, CSVWRITE = 211, 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, 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, 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 int VAR_ARGS = -1;
private static final long PRECISION_UNKNOWN = -1; private static final long PRECISION_UNKNOWN = -1;
...@@ -297,6 +297,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -297,6 +297,7 @@ public class Function extends Expression implements FunctionCall {
addFunctionWithNull("CASEWHEN", CASEWHEN, 3, Value.NULL); addFunctionWithNull("CASEWHEN", CASEWHEN, 3, Value.NULL);
addFunctionWithNull("CONVERT", CONVERT, 1, Value.NULL); addFunctionWithNull("CONVERT", CONVERT, 1, Value.NULL);
addFunctionWithNull("CAST", CAST, 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("COALESCE", COALESCE, VAR_ARGS, Value.NULL);
addFunctionWithNull("NVL", COALESCE, VAR_ARGS, Value.NULL); addFunctionWithNull("NVL", COALESCE, VAR_ARGS, Value.NULL);
addFunctionWithNull("NULLIF", NULLIF, 2, Value.NULL); addFunctionWithNull("NULLIF", NULLIF, 2, Value.NULL);
...@@ -715,7 +716,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -715,7 +716,7 @@ public class Function extends Expression implements FunctionCall {
v0 = v0.convertTo(dataType); v0 = v0.convertTo(dataType);
Mode mode = database.getMode(); Mode mode = database.getMode();
v0 = v0.convertScale(mode.convertOnlyToSmallerScale, scale); v0 = v0.convertScale(mode.convertOnlyToSmallerScale, scale);
v0 = v0.convertPrecision(getPrecision()); v0 = v0.convertPrecision(getPrecision(), false);
result = v0; result = v0;
break; break;
} }
...@@ -1150,6 +1151,10 @@ public class Function extends Expression implements FunctionCall { ...@@ -1150,6 +1151,10 @@ public class Function extends Expression implements FunctionCall {
} }
break; break;
} }
case TRUNCATE_VALUE: {
result = v0.convertPrecision(v1.getLong(), v2.getBoolean());
break;
}
default: default:
throw DbException.throwInternalError("type=" + info.type); throw DbException.throwInternalError("type=" + info.type);
} }
...@@ -1678,6 +1683,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1678,6 +1683,7 @@ public class Function extends Expression implements FunctionCall {
break; break;
case CAST: case CAST:
case CONVERT: case CONVERT:
case TRUNCATE_VALUE:
// data type, precision and scale is already set // data type, precision and scale is already set
t = dataType; t = dataType;
p = precision; p = precision;
......
...@@ -110,7 +110,7 @@ public class TableFunction extends Function { ...@@ -110,7 +110,7 @@ public class TableFunction extends Function {
Column c = columnList[j]; Column c = columnList[j];
v = l[row]; v = l[row];
v = c.convert(v); v = c.convert(v);
v = v.convertPrecision(c.getPrecision()); v = v.convertPrecision(c.getPrecision(), false);
v = v.convertScale(true, c.getScale()); v = v.convertScale(true, c.getScale());
} }
r[j] = v; r[j] = v;
......
...@@ -1351,6 +1351,10 @@ Returns the result set." ...@@ -1351,6 +1351,10 @@ Returns the result set."
TRANSACTION_ID() TRANSACTION_ID()
"," ","
Returns the current transaction id for this session." 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"," "Functions (System)","USER","
{ USER | CURRENT_USER } () { USER | CURRENT_USER } ()
"," ","
......
...@@ -910,27 +910,18 @@ public abstract class Value { ...@@ -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 precision the new precision
* @param force true if losing numeric precision is allowed
* @return the new value * @return the new value
*/ */
public Value convertPrecision(long precision) { public Value convertPrecision(long precision, boolean force) {
return this; 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) { private static byte convertToByte(long x) {
if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) { if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) {
throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE); throw DbException.get(ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE);
......
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
package org.h2.value; package org.h2.value;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.ArrayList;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
/** /**
...@@ -55,7 +57,11 @@ public class ValueArray extends Value { ...@@ -55,7 +57,11 @@ public class ValueArray extends Value {
} }
public long getPrecision() { public long getPrecision() {
return 0; long p = 0;
for (Value v : values) {
p += v.getPrecision();
}
return p;
} }
public String getString() { public String getString() {
...@@ -156,4 +162,24 @@ public class ValueArray extends Value { ...@@ -156,4 +162,24 @@ public class ValueArray extends Value {
return memory; 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 { ...@@ -117,4 +117,14 @@ public class ValueBytes extends Value {
return other instanceof ValueBytes && Utils.compareNotNull(value, ((ValueBytes) other).value) == 0; 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 { ...@@ -184,11 +184,14 @@ public class ValueDecimal extends Value {
return ValueDecimal.get(bd); return ValueDecimal.get(bd);
} }
public Value convertPrecision(long newPrecision) { public Value convertPrecision(long precision, boolean force) {
if (getPrecision() <= newPrecision) { if (getPrecision() <= precision) {
return this; 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 { ...@@ -799,4 +799,17 @@ public class ValueLob extends Value {
return lob; 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 ...@@ -541,4 +541,17 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
return (int) m; 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; ...@@ -10,7 +10,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
...@@ -79,7 +78,7 @@ public class ValueResultSet extends Value { ...@@ -79,7 +78,7 @@ public class ValueResultSet extends Value {
} }
public long getPrecision() { public long getPrecision() {
return 0; return Integer.MAX_VALUE;
} }
public int getDisplaySize() { public int getDisplaySize() {
...@@ -142,4 +141,11 @@ public class ValueResultSet extends Value { ...@@ -142,4 +141,11 @@ public class ValueResultSet extends Value {
return ""; 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 { ...@@ -67,7 +67,7 @@ public class ValueString extends Value {
return value.length() * 2 + 48; return value.length() * 2 + 48;
} }
public Value convertPrecision(long precision) { public Value convertPrecision(long precision, boolean force) {
if (precision == 0 || value.length() <= precision) { if (precision == 0 || value.length() <= precision) {
return this; return this;
} }
...@@ -136,7 +136,7 @@ public class ValueString extends Value { ...@@ -136,7 +136,7 @@ public class ValueString extends Value {
* @param s the string * @param s the string
* @return the value * @return the value
*/ */
protected Value getNew(String s) { protected ValueString getNew(String s) {
return ValueString.get(s); return ValueString.get(s);
} }
......
--- special grammar and test cases --------------------------------------------------------------------------------------------- --- 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'; select * from dual where cast('xx' as varchar_ignorecase(1)) = 'X' and cast('x x ' as char(2)) = 'x';
> X > X
> - > -
......
...@@ -21,10 +21,14 @@ import org.h2.test.TestBase; ...@@ -21,10 +21,14 @@ 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.ValueArray;
import org.h2.value.ValueBytes;
import org.h2.value.ValueDecimal; 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.ValueLobDb;
import org.h2.value.ValueResultSet; import org.h2.value.ValueResultSet;
import org.h2.value.ValueString;
import org.h2.value.ValueUuid; import org.h2.value.ValueUuid;
/** /**
...@@ -42,6 +46,7 @@ public class TestValue extends TestBase { ...@@ -42,6 +46,7 @@ public class TestValue extends TestBase {
} }
public void test() throws SQLException { public void test() throws SQLException {
testCastTrim();
testValueResultSet(); testValueResultSet();
testDataType(); testDataType();
testUUID(); testUUID();
...@@ -52,6 +57,63 @@ public class TestValue extends TestBase { ...@@ -52,6 +57,63 @@ public class TestValue extends TestBase {
testModulusOperator(); 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 { private void testValueResultSet() throws SQLException {
SimpleResultSet rs = new SimpleResultSet(); SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("ID", Types.INTEGER, 0, 0); rs.addColumn("ID", Types.INTEGER, 0, 0);
...@@ -67,7 +129,7 @@ public class TestValue extends TestBase { ...@@ -67,7 +129,7 @@ public class TestValue extends TestBase {
v = ValueResultSet.getCopy(rs, 2); v = ValueResultSet.getCopy(rs, 2);
assertEquals(0, v.hashCode()); assertEquals(0, v.hashCode());
assertEquals(Integer.MAX_VALUE, v.getDisplaySize()); assertEquals(Integer.MAX_VALUE, v.getDisplaySize());
assertEquals(0, v.getPrecision()); assertEquals(Integer.MAX_VALUE, v.getPrecision());
assertEquals(0, v.getScale()); assertEquals(0, v.getScale());
assertEquals("", v.getSQL()); assertEquals("", v.getSQL());
assertEquals(Value.RESULT_SET, v.getType()); assertEquals(Value.RESULT_SET, v.getType());
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论