Unverified 提交 07a263a5 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1537 from katzyn/datetime

Return same datetime values only within a command in compatibility modes
......@@ -4496,7 +4496,8 @@ CALL TRANSLATE('Hello world', 'eo', 'EO')
{ CURRENT_DATE [ () ] | CURDATE() | SYSDATE | TODAY }
","
Returns the current date.
This method always returns the same value within a transaction.
These methods always return the same value within a transaction (default)
or within a command depending on database mode.
","
CURRENT_DATE()
"
......@@ -4509,7 +4510,8 @@ If fractional seconds precision is specified it should be from 0 to 9, 0 is defa
The specified value can be used only to limit precision of a result.
The actual maximum available precision depends on operating system and JVM and can be 3 (milliseconds) or higher.
Higher precision is not available before Java 9.
These methods always return the same value within a transaction.
These methods always return the same value within a transaction (default)
or within a command depending on database mode.
","
CURRENT_TIME()
"
......@@ -4523,7 +4525,8 @@ If fractional seconds precision is specified it should be from 0 to 9, 6 is defa
The specified value can be used only to limit precision of a result.
The actual maximum available precision depends on operating system and JVM and can be 3 (milliseconds) or higher.
Higher precision is not available before Java 9.
This method always returns the same value within a transaction.
This method always returns the same value within a transaction (default)
or within a command depending on database mode.
","
CURRENT_TIMESTAMP()
"
......@@ -4536,7 +4539,8 @@ If fractional seconds precision is specified it should be from 0 to 9, 6 is defa
The specified value can be used only to limit precision of a result.
The actual maximum available precision depends on operating system and JVM and can be 3 (milliseconds) or higher.
Higher precision is not available before Java 9.
These methods always return the same value within a transaction.
These methods always return the same value within a transaction (default)
or within a command depending on database mode.
","
LOCALTIMESTAMP()
"
......
......@@ -21,6 +21,12 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #1536: CURRENT_TIMESTAMP result doesn't change under Transactions
</li>
<li>Issue #239: Consider supporting Lucene 5 indexes
</li>
<li>PR #1520: Fixes bug in PutIfAbsentDecisionMaker
</li>
<li>Issue #1518: ENUM and VIEW with filtering on enum column
</li>
<li>Issue #1516: Array element reference array[index] should be 1-based
......
......@@ -1056,6 +1056,7 @@ or the SQL statement <code>SET MODE DB2</code>.
results in the other value.
</li><li>Support the pseudo-table SYSIBM.SYSDUMMY1.
</li><li>Timestamps with dash between date and time are supported.
</li><li>Datetime value functions return the same value within a command.
</li></ul>
<h3>Derby Compatibility Mode</h3>
......@@ -1071,6 +1072,7 @@ or the SQL statement <code>SET MODE Derby</code>.
</li><li>Concatenating <code>NULL</code> with another value
results in the other value.
</li><li>Support the pseudo-table SYSIBM.SYSDUMMY1.
</li><li>Datetime value functions return the same value within a command.
</li></ul>
<h3>HSQLDB Compatibility Mode</h3>
......@@ -1086,6 +1088,7 @@ or the SQL statement <code>SET MODE HSQLDB</code>.
</li><li>For unique indexes, <code>NULL</code> is distinct.
That means only one row with <code>NULL</code> in one of the columns is allowed.
</li><li>Text can be concatenated using '+'.
</li><li>Datetime value functions return the same value within a command.
</li></ul>
<h3>MS SQL Server Compatibility Mode</h3>
......@@ -1106,6 +1109,7 @@ or the SQL statement <code>SET MODE MSSQLServer</code>.
data type.
</li><li><code>IDENTITY</code> can be used for automatic id generation on column level.
</li><li>Table hints are discarded. Example: <code>SELECT * FROM table WITH (NOLOCK)</code>.
</li><li>Datetime value functions return the same value within a command.
</li></ul>
<h3>MySQL Compatibility Mode</h3>
......@@ -1128,6 +1132,7 @@ or the SQL statement <code>SET MODE MySQL</code>. Use this mode for compatibilit
</li><li>ON DUPLICATE KEY UPDATE is supported in INSERT statements.
</li><li>INSERT IGNORE is partially supported and may be used to skip rows with duplicate keys if ON DUPLICATE KEY UPDATE is not specified.
</li><li>REGEXP_REPLACE() uses \ for back-references for compatibility with MariaDB.
</li><li>Datetime value functions return the same value within a command.
</li></ul>
<p>
Text comparison in MySQL is case insensitive by default, while in H2 it is case sensitive (as in most other databases).
......@@ -1152,6 +1157,7 @@ or the SQL statement <code>SET MODE Oracle</code>.
</li><li>Empty strings are treated like <code>NULL</code> values.
</li><li>REGEXP_REPLACE() uses \ for back-references.
</li><li>DATE data type is treated like TIMESTAMP(0) data type.
</li><li>Datetime value functions return the same value within a command.
</li></ul>
<h3>PostgreSQL Compatibility Mode</h3>
......@@ -1170,6 +1176,7 @@ or the SQL statement <code>SET MODE PostgreSQL</code>.
</li><li>REGEXP_REPLACE() uses \ for back-references.
</li><li>Fixed-width strings are padded with spaces.
</li><li>MONEY data type is treated like NUMERIC(19, 2) data type.
</li><li>Datetime value functions return the same value within a transaction.
</li></ul>
<h3>Ignite Compatibility Mode</h3>
......@@ -1181,6 +1188,7 @@ or the SQL statement <code>SET MODE Ignite</code>.
<code>INDEX(..)</code> or <code>KEY(..)</code>.
Example: <code>create table test(id int primary key, name varchar(255), key idx_name(name));</code>
</li><li>AFFINITY KEY and SHARD KEY keywords may be used in index definition.
</li><li>Datetime value functions return the same value within a transaction.
</li></ul>
<h2 id="auto_reconnect">Auto-Reconnect</h2>
......
......@@ -202,6 +202,13 @@ public class Mode {
*/
public boolean charToBinaryInUtf8;
/**
* If {@code true}, datetime value function return the same value within a
* transaction, if {@code false} datetime value functions return the same
* value within a command.
*/
public boolean dateTimeValueWithinTransaction;
/**
* An optional Set of hidden/disallowed column types.
* Certain DBMSs don't support all column types provided by H2, such as
......@@ -221,6 +228,7 @@ public class Mode {
static {
Mode mode = new Mode(ModeEnum.REGULAR);
mode.nullConcatIsNull = true;
mode.dateTimeValueWithinTransaction = true;
add(mode);
mode = new Mode(ModeEnum.DB2);
......@@ -345,12 +353,14 @@ public class Mode {
dt.sqlType = Types.NUMERIC;
dt.name = "MONEY";
mode.typeByNameMap.put("MONEY", dt);
mode.dateTimeValueWithinTransaction = true;
add(mode);
mode = new Mode(ModeEnum.Ignite);
mode.nullConcatIsNull = true;
mode.allowAffinityKey = true;
mode.indexDefinitionInCreateTable = true;
mode.dateTimeValueWithinTransaction = true;
add(mode);
}
......
......@@ -117,7 +117,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
private boolean closed;
private final long sessionStart = System.currentTimeMillis();
private ValueTimestampTimeZone transactionStart;
private long currentCommandStart;
private ValueTimestampTimeZone currentCommandStart;
private HashMap<String, Value> variables;
private HashSet<ResultInterface> temporaryResults;
private int queryTimeout;
......@@ -1205,8 +1205,8 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
* Wait for some time if this session is throttled (slowed down).
*/
public void throttle() {
if (currentCommandStart == 0) {
currentCommandStart = System.currentTimeMillis();
if (currentCommandStart == null) {
currentCommandStart = CurrentTimestamp.get();
}
if (throttleNs == 0) {
return;
......@@ -1246,10 +1246,14 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
if (command != null && !command.isQuery()) {
getGeneratedKeys().clear(generatedKeysRequest);
}
if (queryTimeout > 0 && command != null) {
currentCommandStart = System.currentTimeMillis();
long now = System.nanoTime();
cancelAtNs = now + TimeUnit.MILLISECONDS.toNanos(queryTimeout);
if (command != null) {
if (queryTimeout > 0) {
currentCommandStart = CurrentTimestamp.get();
long now = System.nanoTime();
cancelAtNs = now + TimeUnit.MILLISECONDS.toNanos(queryTimeout);
} else {
currentCommandStart = null;
}
}
state = command == null ? State.SLEEP : State.RUNNING;
}
......@@ -1285,7 +1289,10 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
return currentCommand;
}
public long getCurrentCommandStart() {
public ValueTimestampTimeZone getCurrentCommandStart() {
if (currentCommandStart == null) {
currentCommandStart = CurrentTimestamp.get();
}
return currentCommandStart;
}
......
......@@ -831,22 +831,28 @@ public class Function extends Expression implements FunctionCall {
}
case CURDATE:
case CURRENT_DATE: {
result = session.getTransactionStart().convertTo(Value.DATE);
result = (database.getMode().dateTimeValueWithinTransaction ? session.getTransactionStart()
: session.getCurrentCommandStart()).convertTo(Value.DATE);
break;
}
case CURTIME:
case CURRENT_TIME: {
ValueTime vt = (ValueTime) session.getTransactionStart().convertTo(Value.TIME);
ValueTime vt = (ValueTime) (database.getMode().dateTimeValueWithinTransaction
? session.getTransactionStart()
: session.getCurrentCommandStart()).convertTo(Value.TIME);
result = vt.convertScale(false, v0 == null ? 0 : v0.getInt());
break;
}
case LOCALTIMESTAMP: {
Value vt = session.getTransactionStart().convertTo(Value.TIMESTAMP);
Value vt = (database.getMode().dateTimeValueWithinTransaction ? session.getTransactionStart()
: session.getCurrentCommandStart()).convertTo(Value.TIMESTAMP);
result = vt.convertScale(false, v0 == null ? 6 : v0.getInt());
break;
}
case CURRENT_TIMESTAMP: {
ValueTimestampTimeZone vt = session.getTransactionStart();
ValueTimestampTimeZone vt = database.getMode().dateTimeValueWithinTransaction
? session.getTransactionStart()
: session.getCurrentCommandStart();
result = vt.convertScale(false, v0 == null ? 6 : v0.getInt());
break;
}
......
......@@ -431,8 +431,9 @@ public class FullTextLucene extends FullText {
int maxResults = (limit == 0 ? 100 : limit) + offset;
TopDocs docs = searcher.search(query, maxResults);
if (limit == 0) {
// TopDocs.totalHits is long now (https://issues.apache.org/jira/browse/LUCENE-7872)
// but in this context it's save to cast
// TopDocs.totalHits is long now
// (https://issues.apache.org/jira/browse/LUCENE-7872)
// but in this context it's safe to cast
limit = (int)docs.totalHits;
}
for (int i = 0, len = docs.scoreDocs.length; i < limit
......
......@@ -250,15 +250,12 @@ public class DatabaseInfo implements DatabaseInfoMBean {
append('\n');
Command command = session.getCurrentCommand();
if (command != null) {
buff.append("statement: ").
append(session.getCurrentCommand()).
append('\n');
long commandStart = session.getCurrentCommandStart();
if (commandStart != 0) {
buff.append("started: ").append(
new Timestamp(commandStart)).
append('\n');
}
buff.append("statement: ")
.append(command)
.append('\n')
.append("started: ")
.append(session.getCurrentCommandStart().getString())
.append('\n');
}
Table[] t = session.getLocks();
if (t.length > 0) {
......
......@@ -362,13 +362,13 @@ public class Column {
if (dt.decimal) {
value = ValueInt.get(0).convertTo(type);
} else if (dt.type == Value.TIMESTAMP) {
value = session.getTransactionStart().convertTo(Value.TIMESTAMP);
value = session.getCurrentCommandStart().convertTo(Value.TIMESTAMP);
} else if (dt.type == Value.TIMESTAMP_TZ) {
value = session.getTransactionStart();
value = session.getCurrentCommandStart();
} else if (dt.type == Value.TIME) {
value = ValueTime.fromNanos(0);
} else if (dt.type == Value.DATE) {
value = session.getTransactionStart().convertTo(Value.DATE);
value = session.getCurrentCommandStart().convertTo(Value.DATE);
} else {
value = ValueString.get("").convertTo(type);
}
......
......@@ -1848,14 +1848,9 @@ public class MetaTable extends Table {
break;
}
case SESSIONS: {
long now = System.currentTimeMillis();
for (Session s : database.getSessions(false)) {
if (admin || s == session) {
Command command = s.getCurrentCommand();
long start = s.getCurrentCommandStart();
if (start == 0) {
start = now;
}
int blockingSessionId = s.getBlockingSessionId();
add(rows,
// ID
......@@ -1867,7 +1862,7 @@ public class MetaTable extends Table {
// STATEMENT
command == null ? null : command.toString(),
// STATEMENT_START
DateTimeUtils.timestampTimeZoneFromMillis(start),
command == null ? null : s.getCurrentCommandStart(),
// CONTAINS_UNCOMMITTED
ValueBoolean.get(s.containsUncommitted()),
// STATE
......
......@@ -11,3 +11,100 @@ SELECT CAST(CURRENT_TIMESTAMP(0) AS TIMESTAMP(9)) = LOCALTIMESTAMP(0);
SELECT CAST(CURRENT_TIMESTAMP(9) AS TIMESTAMP(9)) = LOCALTIMESTAMP(9);
>> TRUE
@reconnect off
SET AUTOCOMMIT OFF;
> ok
CREATE ALIAS SLEEP FOR "java.lang.Thread.sleep(long)";
> ok
CREATE TABLE TEST(I IDENTITY PRIMARY KEY, T TIMESTAMP(9) WITH TIME ZONE);
> ok
INSERT INTO TEST(T) VALUES (CURRENT_TIMESTAMP(9)), (CURRENT_TIMESTAMP(9));
> update count: 2
CALL SLEEP(10);
>> null
INSERT INTO TEST(T) VALUES (CURRENT_TIMESTAMP(9));
> update count: 1
CALL SLEEP(10);
>> null
COMMIT;
> ok
INSERT INTO TEST(T) VALUES (CURRENT_TIMESTAMP(9));
> update count: 1
CALL SLEEP(10);
>> null
COMMIT;
> ok
-- same statement
SELECT (SELECT T FROM TEST WHERE I = 1) = (SELECT T FROM TEST WHERE I = 2);
>> TRUE
-- same transaction
SELECT (SELECT T FROM TEST WHERE I = 2) = (SELECT T FROM TEST WHERE I = 3);
>> TRUE
-- another transaction
SELECT (SELECT T FROM TEST WHERE I = 3) = (SELECT T FROM TEST WHERE I = 4);
>> FALSE
SET MODE MySQL;
> ok
INSERT INTO TEST(T) VALUES (CURRENT_TIMESTAMP(9)), (CURRENT_TIMESTAMP(9));
> update count: 2
CALL SLEEP(10);
>> null
INSERT INTO TEST(T) VALUES (CURRENT_TIMESTAMP(9));
> update count: 1
CALL SLEEP(10);
>> null
COMMIT;
> ok
INSERT INTO TEST(T) VALUES (CURRENT_TIMESTAMP(9));
> update count: 1
COMMIT;
> ok
-- same statement
SELECT (SELECT T FROM TEST WHERE I = 5) = (SELECT T FROM TEST WHERE I = 6);
>> TRUE
-- same transaction
SELECT (SELECT T FROM TEST WHERE I = 6) = (SELECT T FROM TEST WHERE I = 7);
>> FALSE
-- another transaction
SELECT (SELECT T FROM TEST WHERE I = 7) = (SELECT T FROM TEST WHERE I = 8);
>> FALSE
SET MODE Regular;
> ok
DROP TABLE TEST;
> ok
DROP ALIAS SLEEP;
> ok
SET AUTOCOMMIT ON;
> ok
@reconnect on
......@@ -801,3 +801,4 @@ partitioned tri partitions
discard enhancements nolock surefire logarithm
qualification opportunity jumping exploited unacceptable vrs duplicated
queryparser tokenized freeze
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论