[
https://issues.apache.org/jira/browse/DERBY-5866?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Knut Anders Hatlen updated DERBY-5866:
--------------------------------------
Issue & fix info: High Value Fix (was: High Value Fix,Patch Available)
I'm not quite happy with the approach in the patch, so I'm removing the Patch
Available flag until I've explored some more options.
In particular, the code that checks if the current timestamp is higher than the
timestamp of the most recent trigger, may be unreliable. I haven't been able to
construct a case where it breaks, but I think it relies on some unspecified
behaviour in java.util.Calendar and is not guaranteed to give the correct
result.
For example: When the local timezone switches from DST to standard time, there
is a two-hour period where a timestamp without timezone (which is how it is
stored in a Derby TIMESTAMP column) is ambiguous. That is, if the switch
happens at 03:00 DST -> 02:00 std, a TIMESTAMP stored as 02:30 in the database
could represent either 02:30 DST or 02:30 std (an hour later).
If, say, one trigger is created at 02:45 DST, and another one is created half
an hour later, at 02:15 std, here's what the code in the patch does when the
second trigger is created:
1) Create a java.sql.Timestamp representing the current time (02:15 std), let's
call it tsNew.
2) Fetch the creation timestamp (as a java.sql.Timestamp) for the
TriggerDescriptor of the first trigger. This timestamp is 02:45, but since the
timezone is not stored in the database, it is unknown whether it's DST or std.
Let's call it tsOld.
3) Check if tsOld < tsNew
3a) If tsOld is DST, (tsOld < tsNew) is true, and we use tsNew as creation
timestamp. It will be stored as 02:15 in the database.
3b) If tsOld is std, (tsOld < tsNew) is false, and we use tsOld + 1ms as
creation timestamp. It will be stored as 02:45:00.001 in the database.
Now, if 3a happens, we will end up with the first trigger having creation
timestamp 02:45 and the second trigger having creation timestamp 02:15. That
will lead to wrong execution order, so we don't want that to happen.
What actually happens here, at least in all test cases I've managed to
construct, is 3b. That is, the first trigger has timestamp 02:45 and the second
has 02:45:00.001. This gives the correct execution order. (However, the
creation timestamp of the second trigger lies with half an hour to achieve
this.)
But 3b is only guaranteed if we know that getTimestamp() on an ambiguous
timestamp will always return the highest alternative. That is, if we know that
the returned java.sql.Timestamp value for the ambiguous column value 02:15
above will always be 02:15 std and not 02:15 DST, we're fine. I'm not able to
find anything in the API specification that guarantees this, though. I did find
a comment buried in OpenJDK's implementation of java.util.GregorianCalendar
that suggests standard time will be used in case of ambiguities, but nothing in
the actual API reference. So it might be that the current approach works in all
cases, but even so it seems like it depends on an internal implementation
detail in the Java class libraries.
I'm thinking that one possible fix that should make this code more reliable, is
to store the trigger creation timestamp in UTC instead of the local timezone.
Then we'd avoid the DST problems, as well as the problems with the database
being moved from one timezone to another. An additional benefit is that we
wouldn't have to lie when creating the timestamp around the DST switch (such as
adjusting the timestamp with half an hour in the 3b scenario described above).
>
> testFiringConstraintOrder(org.apache.derbyTesting.functionTests.tests.lang.TriggerTest)junit.framework.AssertionFailedError:
> matching triggers need to be fired in order creation:1,NO CASCADE
> BEFORE,DELETE,ROW
> -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: DERBY-5866
> URL: https://issues.apache.org/jira/browse/DERBY-5866
> Project: Derby
> Issue Type: Bug
> Components: SQL
> Affects Versions: 10.10.1.1, 10.10.1.3
> Environment: Windows IBM 1.6 10.10.0.0 alpha - (1361856)
> Reporter: Kathey Marsden
> Assignee: Knut Anders Hatlen
> Labels: derby_triage10_11
> Attachments: d5866-1a-adjust-timestamp.diff, error-stacktrace.out,
> fail1.zip, fail2.zip, time-zone-test.diff
>
>
> I saw this failure in the IBM nightlies on 7/15. The subsequent night did not
> fail, so appears intermittent
> http://cloudsoft.usca.ibm.com/intranet/nightlies/derbywinvm/JarResults.2012-07-15/ibm16_suites.All/suites.All.out
> 1)
> testFiringConstraintOrder(org.apache.derbyTesting.functionTests.tests.lang.TriggerTest)junit.framework.AssertionFailedError:
> matching triggers need to be fired in order creation:1,NO CASCADE
> BEFORE,DELETE,ROW
> at
> org.apache.derbyTesting.functionTests.tests.lang.TriggerTest.assertFiringOrder(TriggerTest.java:560)
> at
> org.apache.derbyTesting.functionTests.tests.lang.TriggerTest.testFiringConstraintOrder(TriggerTest.java:500)
> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
> at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
> at
> org.apache.derbyTesting.junit.BaseTestCase.runBare(BaseTestCase.java:117)
> at
> org.apache.derbyTesting.junit.BaseJDBCTestCase.runBareOverridable(BaseJDBCTestCase.java:424)
> at
> org.apache.derbyTesting.junit.BaseJDBCTestCase.runBare(BaseJDBCTestCase.java:441)
> at junit.extensions.TestDecorator.basicRun(TestDecorator.java:24)
> at junit.extensions.TestSetup$1.protect(TestSetup.java:21)
> at junit.extensions.TestSetup.run(TestSetup.java:25)
> at
> org.apache.derbyTesting.junit.BaseTestSetup.run(BaseTestSetup.java:57)
--
This message was sent by Atlassian JIRA
(v6.1.4#6159)