提交 51386e8e authored 作者: noelgrandin's avatar noelgrandin

Support TRUNC(timestamp) to make Oracle users happy.

上级 db6a0c91
...@@ -2799,6 +2799,7 @@ CALL HASH('SHA256', STRINGTOUTF8('Password'), 1000) ...@@ -2799,6 +2799,7 @@ CALL HASH('SHA256', STRINGTOUTF8('Password'), 1000)
"," ","
Truncates to a number of digits (to the next value closer to 0). Truncates to a number of digits (to the next value closer to 0).
This method returns a double. This method returns a double.
There is also a TRUNCATE function that operates on numbers.
"," ","
TRUNCATE(VALUE, 2) TRUNCATE(VALUE, 2)
" "
...@@ -3366,6 +3367,16 @@ Returns the second (0-59) from a timestamp. ...@@ -3366,6 +3367,16 @@ Returns the second (0-59) from a timestamp.
SECOND(CREATED) SECOND(CREATED)
" "
"Functions (Time and Date)","TRUNCATE","
{ TRUNC | TRUNCATE } (timestamp)
","
Truncates a timestamp to a date (day) value.
This method returns a date.
There is also a TRUNCATE function that operates on numbers.
","
TRUNCATE(CREATED)
"
"Functions (Time and Date)","WEEK"," "Functions (Time and Date)","WEEK","
WEEK(timestamp) WEEK(timestamp)
"," ","
......
...@@ -18,7 +18,7 @@ Change Log ...@@ -18,7 +18,7 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>- <ul><li>Support TRUNC(timestamp) to make Oracle users happy.
</li></ul> </li></ul>
<h2>Version 1.3.171 (2013-03-17)</h2> <h2>Version 1.3.171 (2013-03-17)</h2>
......
...@@ -204,9 +204,9 @@ public class Function extends Expression implements FunctionCall { ...@@ -204,9 +204,9 @@ public class Function extends Expression implements FunctionCall {
addFunction("SQRT", SQRT, 1, Value.DOUBLE); addFunction("SQRT", SQRT, 1, Value.DOUBLE);
addFunction("TAN", TAN, 1, Value.DOUBLE); addFunction("TAN", TAN, 1, Value.DOUBLE);
addFunction("TANH", TANH, 1, Value.DOUBLE); addFunction("TANH", TANH, 1, Value.DOUBLE);
addFunction("TRUNCATE", TRUNCATE, 2, Value.DOUBLE); addFunction("TRUNCATE", TRUNCATE, VAR_ARGS, Value.NULL);
// same as TRUNCATE // same as TRUNCATE
addFunction("TRUNC", TRUNCATE, 2, Value.DOUBLE); addFunction("TRUNC", TRUNCATE, VAR_ARGS, Value.NULL);
addFunction("HASH", HASH, 3, Value.BYTES); addFunction("HASH", HASH, 3, Value.BYTES);
addFunction("ENCRYPT", ENCRYPT, 3, Value.BYTES); addFunction("ENCRYPT", ENCRYPT, 3, Value.BYTES);
addFunction("DECRYPT", DECRYPT, 3, Value.BYTES); addFunction("DECRYPT", DECRYPT, 3, Value.BYTES);
...@@ -1016,11 +1016,22 @@ public class Function extends Expression implements FunctionCall { ...@@ -1016,11 +1016,22 @@ public class Function extends Expression implements FunctionCall {
break; break;
} }
case TRUNCATE: { case TRUNCATE: {
if (v0.getType() == Value.TIMESTAMP) {
java.sql.Timestamp d = v0.getTimestamp();
Calendar c = Calendar.getInstance();
c.setTime(d);
c.set(Calendar.HOUR, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
result = ValueTimestamp.get(new java.sql.Timestamp(c.getTimeInMillis()));
} else {
double d = v0.getDouble(); double d = v0.getDouble();
int p = v1.getInt(); int p = v1.getInt();
double f = Math.pow(10., p); double f = Math.pow(10., p);
double g = d * f; double g = d * f;
result = ValueDouble.get(((d < 0) ? Math.ceil(g) : Math.floor(g)) / f); result = ValueDouble.get(((d < 0) ? Math.ceil(g) : Math.floor(g)) / f);
}
break; break;
} }
case HASH: case HASH:
...@@ -1699,6 +1710,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1699,6 +1710,7 @@ public class Function extends Expression implements FunctionCall {
case FILE_READ: case FILE_READ:
case ROUND: case ROUND:
case XMLTEXT: case XMLTEXT:
case TRUNCATE:
min = 1; min = 1;
max = 2; max = 2;
break; break;
...@@ -1858,11 +1870,27 @@ public class Function extends Expression implements FunctionCall { ...@@ -1858,11 +1870,27 @@ public class Function extends Expression implements FunctionCall {
s = scale; s = scale;
d = displaySize; d = displaySize;
break; break;
case TRUNCATE:
t = p0.getType();
s = p0.getScale();
p = p0.getPrecision();
d = p0.getDisplaySize();
if (t == Value.NULL) {
t = Value.INT;
p = ValueInt.PRECISION;
d = ValueInt.DISPLAY_SIZE;
s = 0;
} else if (t == Value.TIMESTAMP) {
t = Value.DATE;
p = ValueDate.PRECISION;
s = 0;
d = ValueDate.DISPLAY_SIZE;
}
break;
case ABS: case ABS:
case FLOOR: case FLOOR:
case RADIANS: case RADIANS:
case ROUND: case ROUND:
case TRUNCATE:
case POWER: case POWER:
t = p0.getType(); t = p0.getType();
s = p0.getScale(); s = p0.getScale();
...@@ -2004,6 +2032,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -2004,6 +2032,7 @@ public class Function extends Expression implements FunctionCall {
case TRIM: case TRIM:
case STRINGDECODE: case STRINGDECODE:
case UTF8TOSTRING: case UTF8TOSTRING:
case TRUNCATE:
precision = args[0].getPrecision(); precision = args[0].getPrecision();
displaySize = args[0].getDisplaySize(); displaySize = args[0].getDisplaySize();
break; break;
......
...@@ -1223,6 +1223,10 @@ Returns the quarter (1-4) from a timestamp." ...@@ -1223,6 +1223,10 @@ Returns the quarter (1-4) from a timestamp."
SECOND(timestamp) SECOND(timestamp)
"," ","
Returns the second (0-59) from a timestamp." Returns the second (0-59) from a timestamp."
"Functions (Time and Date)","TRUNCATE","
TRUNCATE(timestamp)
","
Truncates a timestamp to a date (day) value."
"Functions (Time and Date)","WEEK"," "Functions (Time and Date)","WEEK","
WEEK(timestamp) WEEK(timestamp)
"," ","
......
...@@ -29,7 +29,7 @@ public class ValueDate extends Value { ...@@ -29,7 +29,7 @@ public class ValueDate extends Value {
* The display size of the textual representation of a date. * The display size of the textual representation of a date.
* Example: 2000-01-02 * Example: 2000-01-02
*/ */
static final int DISPLAY_SIZE = 10; public static final int DISPLAY_SIZE = 10;
private final long dateValue; private final long dateValue;
......
...@@ -25,9 +25,11 @@ import java.sql.ResultSetMetaData; ...@@ -25,9 +25,11 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.SimpleTimeZone;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
...@@ -589,6 +591,22 @@ public abstract class TestBase { ...@@ -589,6 +591,22 @@ public abstract class TestBase {
} }
} }
/**
* Check if two values are equal, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws AssertionError if the values are not equal
*/
public void assertEquals(java.util.Date expected, java.util.Date actual) {
if (expected != actual && !expected.equals(actual)) {
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
final SimpleTimeZone GMT_TIMEZONE = new SimpleTimeZone(0, "Z");
df.setTimeZone(GMT_TIMEZONE);
fail("Expected: " + df.format(expected) + " actual: " + df.format(actual));
}
}
/** /**
* Check if two values are equal, and if not throw an exception. * Check if two values are equal, and if not throw an exception.
* *
......
...@@ -17,6 +17,7 @@ import java.sql.CallableStatement; ...@@ -17,6 +17,7 @@ import java.sql.CallableStatement;
import java.sql.Clob; import java.sql.Clob;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
...@@ -25,6 +26,7 @@ import java.sql.SQLException; ...@@ -25,6 +26,7 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Properties; import java.util.Properties;
import java.util.UUID; import java.util.UUID;
import org.h2.api.AggregateFunction; import org.h2.api.AggregateFunction;
...@@ -75,6 +77,7 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -75,6 +77,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
testValue(); testValue();
testNvl2(); testNvl2();
testConcatWs(); testConcatWs();
testTruncate();
deleteDb("functions"); deleteDb("functions");
FileUtils.deleteRecursive(TEMP_DIR, true); FileUtils.deleteRecursive(TEMP_DIR, true);
} }
...@@ -860,6 +863,46 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -860,6 +863,46 @@ public class TestFunctions extends TestBase implements AggregateFunction {
conn.close(); conn.close();
} }
private void testTruncate() throws SQLException {
deleteDb("functions");
Connection conn = getConnection("functions");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT TRUNCATE(1.234, 2) FROM dual");
rs.next();
assertEquals(1.23d, rs.getDouble(1));
rs = stat.executeQuery("SELECT CURRENT_TIMESTAMP(), TRUNCATE(CURRENT_TIMESTAMP()) FROM dual");
rs.next();
Calendar c = Calendar.getInstance();
c.setTime(rs.getTimestamp(1));
c.set(Calendar.HOUR, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
java.util.Date nowDate = c.getTime();
assertEquals(nowDate, rs.getTimestamp(2));
try {
rs = stat.executeQuery("SELECT TRUNCATE('bad', 1) FROM dual");
fail("expected exception");
} catch (SQLException ex) {}
// check for passing wrong data type
try {
rs = stat.executeQuery("SELECT TRUNCATE('bad') FROM dual");
fail("expected exception");
} catch (SQLException ex) {}
// check for too many parameters
try {
rs = stat.executeQuery("SELECT TRUNCATE(1,2,3) FROM dual");
fail("expected exception");
} catch (SQLException ex) {}
conn.close();
}
private void assertCallResult(String expected, Statement stat, String sql) throws SQLException { private void assertCallResult(String expected, Statement stat, String sql) throws SQLException {
ResultSet rs = stat.executeQuery("CALL " + sql); ResultSet rs = stat.executeQuery("CALL " + sql);
rs.next(); rs.next();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论