[ 
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)

Reply via email to