001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.tests.lang.TimeHandlingTest
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021: package org.apache.derbyTesting.functionTests.tests.lang;
022:
023: import java.io.UnsupportedEncodingException;
024: import java.sql.Date;
025: import java.sql.PreparedStatement;
026: import java.sql.ResultSet;
027: import java.sql.SQLException;
028: import java.sql.Statement;
029: import java.sql.Time;
030: import java.sql.Timestamp;
031: import java.sql.Types;
032: import java.util.Calendar;
033: import java.util.Random;
034:
035: import junit.framework.Test;
036: import junit.framework.TestSuite;
037:
038: import org.apache.derbyTesting.junit.BaseJDBCTestCase;
039: import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
040:
041: public class TimeHandlingTest extends BaseJDBCTestCase {
042:
043: /**
044: * All the functions or expressions that result in
045: * a TIME value with the same value as CURRENT_TIME.
046: */
047: private static final String[] CURRENT_TIME_FUNCTIONS = {
048: "CURRENT TIME", "CURRENT_TIME" };
049:
050: /**
051: * All the functions or expressions that result in
052: * a TIMESTAMP value with the same value as CURRENT_TIMESTAMP.
053: */
054: private static final String[] CURRENT_TIMESTAMP_FUNCTIONS = {
055: "CURRENT TIMESTAMP", "CURRENT_TIMESTAMP" };
056:
057: /**
058: * Time to sleep that will result in different TIME values.
059: */
060: private static final long SLEEP_TIME = 2000;
061:
062: /**
063: * Calendar for testing returned values.
064: */
065: private Calendar cal;
066:
067: public static Test suite() {
068: TestSuite suite = new TestSuite(TimeHandlingTest.class);
069:
070: return new CleanDatabaseTestSetup(suite) {
071: protected void decorateSQL(Statement s) throws SQLException {
072:
073: s.execute("CREATE FUNCTION SLEEP() RETURNS INTEGER"
074: + " LANGUAGE JAVA PARAMETER STYLE JAVA"
075: + " NO SQL "
076: + " EXTERNAL NAME '"
077: + TimeHandlingTest.class.getName().concat(
078: ".sleep'"));
079:
080: s.execute("CREATE TABLE TIME_ALL (ID INT,"
081: + " C_T TIME," + " C_D DATE,"
082: + " C_TS TIMESTAMP)");
083:
084: for (int f = 0; f < CURRENT_TIME_FUNCTIONS.length; f++) {
085: s.execute("ALTER TABLE TIME_ALL ADD COLUMN"
086: + " D_T" + f + " TIME WITH DEFAULT "
087: + CURRENT_TIME_FUNCTIONS[f]);
088: }
089: for (int f = 0; f < CURRENT_TIMESTAMP_FUNCTIONS.length; f++) {
090: s.execute("ALTER TABLE TIME_ALL ADD COLUMN"
091: + " D_TS" + f + " TIMESTAMP WITH DEFAULT "
092: + CURRENT_TIMESTAMP_FUNCTIONS[f]);
093: }
094: }
095: };
096: }
097:
098: /**
099: * Method for SQL SLEEP function. Sleeps for the time
100: * that will result in a change in
101: * System.currentTimeMillis and a Derby TIME value.
102: * @return
103: * @throws InterruptedException
104: */
105: public static int sleep() throws InterruptedException {
106: Thread.sleep(SLEEP_TIME);
107: return 0;
108: }
109:
110: public TimeHandlingTest(String name) {
111: super (name);
112: }
113:
114: /**
115: * Simple set up, just get a Calendar
116: * and ensure the table T_ALL is empty.
117: * @throws SQLException
118: * @throws UnsupportedEncodingException
119: */
120: protected void setUp() throws UnsupportedEncodingException,
121: SQLException {
122: cal = Calendar.getInstance();
123: runSQLCommands("DELETE FROM TIME_ALL;");
124: }
125:
126: /**
127: * Test inserting and selecting of TIME values.
128: * A set of random TIME values are inserted along with an
129: * identifer that encodes the time value. The values are then
130: * fetched and compared to a value calculated from the identifier.
131: * The returned values are fetched using checkTimeValue thus inheriting
132: * all the checks within that method.
133: * @throws SQLException
134: * @throws UnsupportedEncodingException
135: */
136: public void testInertTime() throws SQLException,
137: UnsupportedEncodingException {
138: getConnection().setAutoCommit(false);
139: // Insert a set of time values,
140:
141: Random r = new Random();
142:
143: // Insert 500 TIME values using a PreparedStatement,
144: // but randomly selecting the way the value is inserted
145: // between:
146: // java.sql.Time object
147: // String representation hh:mm:ss from Time.toString()
148: // String representation hh.mm.ss
149:
150: // prime number used to select the way the
151: // selected value is inserted.
152: final int itk = 71;
153:
154: PreparedStatement ps = prepareStatement("INSERT INTO TIME_ALL(ID, C_T) VALUES (?, ?)");
155:
156: for (int i = 0; i < 500; i++) {
157:
158: // Just some big range from zero upwards
159: int id = r.nextInt(1000000);
160: ps.setInt(1, id);
161:
162: Time ct = getCodedTime(id);
163:
164: switch ((id % itk) % 3) {
165: case 0: // Insert using Time object
166: ps.setTime(2, ct);
167: break;
168: case 1: // Insert using String provided by Time.toString() (hh:mm:ss)
169: ps.setString(2, ct.toString());
170: break;
171: case 2: // Insert using String format (hh.mm.ss)
172: ps.setString(2, ct.toString().replace(':', '.'));
173: break;
174: default:
175: fail("not reached");
176:
177: }
178: ps.executeUpdate();
179: }
180: ps.close();
181: commit();
182:
183: Statement s = createStatement();
184:
185: ResultSet rs = s.executeQuery("SELECT ID, C_T FROM TIME_ALL");
186: int rowCount = 0;
187: while (rs.next()) {
188: int id = rs.getInt(1);
189: Time t = checkTimeValue(rs, 2);
190: assertTimeEqual(getCodedTime(id), t);
191: rowCount++;
192: }
193: rs.close();
194: s.close();
195: commit();
196:
197: assertEquals(rowCount, 500);
198: }
199:
200: /**
201: * Return a time simply encoded from an integer identifier
202: * and a set of fixed encoding keys, each a prime number.
203: * This allows a random value to be inserted into a table
204: * as a TIME and an INTEGER and thus checked for consistency
205: * on a SELECT.
206: * @param id
207: * @return
208: */
209: private Time getCodedTime(int id) {
210: final int hk = 17;
211: final int mk = 41;
212: final int sk = 67;
213:
214: int hour = (id % hk) % 24;
215: int min = (id % mk) % 60;
216: int sec = (id % sk) % 60;
217:
218: return getTime19700101(hour, min, sec);
219: }
220:
221: /**
222: * Tests for CURRENT TIME and CURRENT_TIME.
223: * A set of tests that ensure the CURRENT TIME maintains
224: * a single value for the life time of a statement and
225: * that (subject to the resolution) the returned value
226: * is correctly between the start time of the statement
227: * execution and the first fetch or completion.
228: * @throws SQLException
229: * @throws InterruptedException
230: */
231: public void testCurrentTime() throws SQLException,
232: InterruptedException {
233: currentFunctionTests(Types.TIME, CURRENT_TIME_FUNCTIONS);
234: }
235:
236: /**
237: * Tests for CURRENT TIMESTAMP functions.
238: * A set of tests that ensure the CURRENT TIMESTAMP maintains
239: * a single value for the life time of a statement and
240: * that (subject to the resolution) the returned value
241: * is correctly between the start time of the statement
242: * execution and the first fetch or completion.
243: * @throws SQLException
244: * @throws InterruptedException
245: */
246: public void testCurrentTimestamp() throws SQLException,
247: InterruptedException {
248: currentFunctionTests(Types.TIMESTAMP,
249: CURRENT_TIMESTAMP_FUNCTIONS);
250: }
251:
252: /**
253: * Test all the current timedate functions passed in that
254: * return the specified type. Generic function that checks
255: * the functions' are all identical in various situations
256: * and that the have the correct value, and change across
257: * executions.
258: *
259: * @param jdbcType JDBC type, Types.TIME, DATE or TIMESTAMP.
260: * @param functions List of functions or expressions that map to the
261: * current time date value and return the specified type.
262: * @throws SQLException
263: * @throws InterruptedException
264: */
265: private void currentFunctionTests(int jdbcType, String[] functions)
266: throws SQLException, InterruptedException {
267: Statement s = createStatement();
268:
269: // Single value returned by each function.
270: for (int f = 0; f < functions.length; f++) {
271: checkCurrentQuery(jdbcType, s, "VALUES " + functions[f],
272: new int[] { 1 }, 1);
273: }
274:
275: // Create text for a single row in a VALUES clause,
276: // each function represented once.
277: StringBuffer rb = new StringBuffer("(");
278: for (int f = 0; f < functions.length; f++) {
279: if (f != 0)
280: rb.append(", ");
281: rb.append(functions[f]);
282: }
283: rb.append(")");
284: String row = rb.toString();
285:
286: int[] columns = new int[functions.length];
287: for (int f = 0; f < columns.length; f++)
288: columns[f] = f + 1;
289:
290: // All the functions as a single row, all return the same value
291: String sql = "VALUES " + row;
292: checkCurrentQuery(jdbcType, s, sql, columns, functions.length);
293:
294: // Check they produce the same value across multiple rows
295: sql = "VALUES " + row + "," + row + "," + row;
296: checkCurrentQuery(jdbcType, s, sql, columns,
297: 3 * functions.length);
298:
299: // Check they produce the same value across multiple rows
300: // with a forced sleep within row creaton
301: String sleepRow = row.substring(0, row.length() - 1)
302: + ", SLEEP())";
303:
304: sql = "VALUES " + sleepRow + "," + sleepRow + "," + sleepRow;
305: checkCurrentQuery(jdbcType, s, sql, columns,
306: 3 * functions.length);
307:
308: // Check behaviour in non-queries.
309: String ccol = null;
310: String dcol = null;
311: switch (jdbcType) {
312: case Types.TIME:
313: dcol = "D_T";
314: ccol = "C_T";
315: break;
316: case Types.TIMESTAMP:
317: dcol = "D_TS";
318: ccol = "C_TS";
319: break;
320: case Types.DATE:
321: dcol = "D_D";
322: ccol = "C_D";
323: break;
324: default:
325: fail("Unexpected JDBC Type " + jdbcType);
326: }
327:
328: // All the functions as multiple rows, one function per row.
329: StringBuffer rm = new StringBuffer();
330: for (int f = 0; f < functions.length; f++) {
331: if (f != 0)
332: rm.append(", ");
333: rm.append(functions[f]);
334: }
335: String mrow = rm.toString();
336:
337: // Select list with all the columns of this type
338: StringBuffer sb = new StringBuffer();
339: sb.append(ccol); // Column without the defaul
340: for (int f = 0; f < functions.length; f++) {
341: sb.append(", ");
342: sb.append(dcol);
343: sb.append(f);
344: }
345: String typeColumnList = sb.toString();
346: String selectAllType = "SELECT " + typeColumnList
347: + " FROM TIME_ALL";
348:
349: int[] tableColumns = new int[columns.length + 1];
350: for (int i = 0; i < tableColumns.length; i++)
351: tableColumns[i] = i + 1;
352:
353: // Insert multiple rows, one per function
354: // Check all the inserted value and the default
355: // columns have the same value.
356: String insert = "INSERT INTO TIME_ALL(" + ccol + ") VALUES "
357: + mrow;
358: s.executeUpdate("DELETE FROM TIME_ALL");
359: long start = System.currentTimeMillis();
360: s.executeUpdate(insert);
361: long end = System.currentTimeMillis();
362: ResultSet rs = s.executeQuery(selectAllType);
363: rs.next();
364: checkCurrentMultiple(jdbcType, start, end, rs, tableColumns,
365: functions.length * (functions.length + 1));
366: rs.close();
367:
368: // Insert of multiple rows from a query with a delay
369: // All the functions as multiple rows, one function per row
370: // with a SLEEP as the first column.
371: sb = new StringBuffer();
372: for (int f = 0; f < functions.length; f++) {
373: if (f != 0)
374: sb.append(", ");
375: sb.append("(SLEEP(), ");
376: sb.append(functions[f]);
377: sb.append(")");
378: }
379: String mSleepRow = sb.toString();
380:
381: insert = "INSERT INTO TIME_ALL(ID, " + ccol + ") "
382: + " SELECT * FROM TABLE (VALUES " + mSleepRow
383: + ") AS T";
384:
385: s.executeUpdate("DELETE FROM TIME_ALL");
386: start = System.currentTimeMillis();
387: s.executeUpdate(insert);
388: end = System.currentTimeMillis();
389: rs = s.executeQuery(selectAllType);
390: rs.next();
391: checkCurrentMultiple(jdbcType, start, end, rs, tableColumns,
392: functions.length * (functions.length + 1));
393: rs.close();
394:
395: // Ensure a PreparedStatement (psI) resets its current time correctly
396: // and does not get stuck with a single value for all executions.
397: PreparedStatement psQ = prepareStatement(selectAllType
398: + " WHERE ID = ?");
399:
400: Object last = null;
401: for (int f = 0; f < functions.length; f++) {
402: PreparedStatement psI = prepareStatement("INSERT INTO TIME_ALL(ID, "
403: + ccol + ")" + " VALUES (?, " + functions[f] + ")");
404: s.executeUpdate("DELETE FROM TIME_ALL");
405:
406: for (int i = 1; i <= 3; i++) {
407: psI.setInt(1, i);
408: psQ.setInt(1, i);
409: start = System.currentTimeMillis();
410: psI.executeUpdate();
411: end = System.currentTimeMillis();
412:
413: rs = psQ.executeQuery();
414: rs.next();
415: Object next = checkCurrentMultiple(jdbcType, start,
416: end, rs, tableColumns, functions.length + 1);
417: rs.close();
418:
419: if (last != null) {
420: // This check is redundant because the last and next have
421: // been checked they are within limit of the start and end.
422: // But why not check it.
423: assertFalse(
424: "CURRENT value not changed over executions",
425: last.equals(next));
426: }
427: last = next;
428:
429: // Ensure the next execution is meant to get a different value
430: Thread.sleep(SLEEP_TIME);
431: }
432: psI.close();
433:
434: }
435:
436: psQ.close();
437: s.close();
438: }
439:
440: /**
441: * Execute a query that uses CURRENT expressions directly.
442: * The time returned for these values should be between the
443: * start of execution and after the return from the first rs.next().
444: * @param sqlType
445: * @param s
446: * @param sql
447: * @param columns
448: * @param expectedCount
449: * @throws SQLException
450: */
451: private void checkCurrentQuery(int sqlType, Statement s,
452: String sql, int[] columns, int expectedCount)
453: throws SQLException {
454: long start = System.currentTimeMillis();
455: ResultSet rs = s.executeQuery(sql);
456: rs.next();
457: long end = System.currentTimeMillis();
458: checkCurrentMultiple(sqlType, start, end, rs, columns,
459: expectedCount);
460: rs.close();
461: }
462:
463: /**
464: * Check the validity of all CURRENT time values returned and
465: * that they are identical.
466: * @param jdbcType Types.TIME or TIMESTAMP
467: * @param start Start of window for valid value.
468: * @param end End of window for valid value.
469: * @param rs Result set positioned on row.
470: * @param columns Columns holding current values.
471: * @param expectedCount Total number of values exected to see
472: * (row count times column count)
473: * @return
474: * @throws SQLException
475: */
476: private Object checkCurrentMultiple(int jdbcType, long start,
477: long end, ResultSet rs, int[] columns, int expectedCount)
478: throws SQLException {
479: switch (jdbcType) {
480: case Types.TIME:
481: return checkCurrentTimeMultiple(start, end, rs, columns,
482: expectedCount);
483: case Types.TIMESTAMP:
484: return checkCurrentTimestampMultiple(start, end, rs,
485: columns, expectedCount);
486: default:
487: fail("Unexpected type " + jdbcType);
488: return null;
489: }
490: }
491:
492: /**
493: * Check a set of rows and columns with values set to CURRENT TIME
494: * in a single statement are the same.
495: * @param start Start time for the statement that set the values.
496: * @param end End time for the statement that set the values.
497: * @param rs ResultSet positioned on the first row.
498: * @param columns Set of columns holding the TIME values
499: * @param expectedCount Number of values we are execpted to check.
500: * @throws SQLException
501: */
502: private Time checkCurrentTimeMultiple(long start, long end,
503: ResultSet rs, int[] columns, int expectedCount)
504: throws SQLException {
505: // Result set is positioned on starting row
506: // Since all values must be the same since they are based upon
507: // CURRENT TIME from a single statement, pick one as the base
508: // and compare the rest to it.
509: Time base = checkCurrentTimeValue(start, end, rs, columns[0]);
510: assertNotNull(base);
511: int count = 1;
512:
513: // check the remaining columns on this row.
514: for (int i = 1; i < columns.length; i++) {
515: Time t = checkCurrentTimeValue(start, end, rs, columns[i]);
516: assertEquals("CURENT TIME changed during execution", base,
517: t);
518: count++;
519: }
520:
521: // now check all columns on any remaining rows
522: while (rs.next()) {
523: for (int i = 0; i < columns.length; i++) {
524: Time t = checkCurrentTimeValue(start, end, rs,
525: columns[i]);
526: assertEquals("CURENT TIME changed during execution",
527: base, t);
528: count++;
529: }
530: }
531:
532: assertEquals(expectedCount, count);
533:
534: return base;
535: }
536:
537: /**
538: * Check a set of rows and columns with values set to CURRENT TIMESTAMP
539: * in a single statement are the same.
540: * @param start Start time for the statement that set the values.
541: * @param end End time for the statement that set the values.
542: * @param rs ResultSet positioned on the first row.
543: * @param columns Set of columns holding the TIME values
544: * @param expectedCount Number of values we are execpted to check.
545: * @throws SQLException
546: */
547: private Timestamp checkCurrentTimestampMultiple(long start,
548: long end, ResultSet rs, int[] columns, int expectedCount)
549: throws SQLException {
550: // Result set is positioned on starting row
551: // Since all values must be the same since they are based upon
552: // CURRENT TIME from a single statement, pick one as the base
553: // and compare the rest to it.
554: Timestamp base = checkCurrentTimestampValue(start, end, rs,
555: columns[0]);
556: assertNotNull(base);
557: int count = 1;
558:
559: // check the remaining columns on this row.
560: for (int i = 1; i < columns.length; i++) {
561: Timestamp ts = checkCurrentTimestampValue(start, end, rs,
562: columns[i]);
563: assertEquals("CURENT TIMESTAMP changed during execution",
564: base, ts);
565: count++;
566: }
567:
568: // now check all columns on any remaining rows
569: while (rs.next()) {
570: for (int i = 0; i < columns.length; i++) {
571: Timestamp ts = checkCurrentTimestampValue(start, end,
572: rs, columns[i]);
573: assertEquals(
574: "CURENT TIMESTAMP changed during execution",
575: base, ts);
576: count++;
577: }
578: }
579:
580: assertEquals(expectedCount, count);
581:
582: return base;
583: }
584:
585: /**
586: * Check the consistency of a ResultSet column that returns
587: * a TIME value. Can be used for any column of type TIME.
588: *
589: * @param rs ResultSet holding the column, positioned on a row
590: * @param column Column with the TIME value.
591: * @return Returns the Time object obtained from the column.
592: * @throws SQLException
593: */
594: private Time checkTimeValue(ResultSet rs, int column)
595: throws SQLException {
596: assertEquals(java.sql.Types.TIME, rs.getMetaData()
597: .getColumnType(column));
598:
599: try {
600: rs.getDate(column);
601: fail("ResultSet.getDate() succeeded on TIME column");
602: } catch (SQLException e) {
603: assertSQLState("22005", e);
604: }
605:
606: Time tv = rs.getTime(column);
607: assertEquals(tv == null, rs.wasNull());
608:
609: Object ov = rs.getObject(column);
610: assertEquals(ov == null, rs.wasNull());
611:
612: if (tv == null) {
613: assertNull(ov);
614: return null;
615: }
616:
617: assertTrue(ov instanceof java.sql.Time);
618: assertEquals(tv, ov);
619:
620: // Check the date portion is set to 1970/01/01
621: assertTime1970(tv);
622: cal.clear();
623: cal.setTime(tv);
624:
625: // Check the milli-seconds portion is 0
626: // Derby does not support that precision in TIME
627: assertEquals(0, cal.get(Calendar.MILLISECOND));
628:
629: long now = System.currentTimeMillis();
630: Timestamp tsv = rs.getTimestamp(column);
631: long now2 = System.currentTimeMillis();
632: assertNotNull(tsv);
633: assertFalse(rs.wasNull());
634:
635: // Check the TIME portion is set to the same as tv
636: assertTimeEqual(tv, tsv);
637:
638: // DERBY-1811, DERBY-889 being fixed could add tests
639: // Check the returned date portion is the current date
640: // using the value from 'now' and 'now2'. Double check
641: // just in case this test runs at midnight.
642: if (!(isDateEqual(now, tsv) || isDateEqual(now2, tsv))) {
643: fail("TIME to java.sql.Timestamp does not contain current date "
644: + tsv);
645: }
646:
647: String sv = rs.getString(column);
648: assertNotNull(sv);
649: assertFalse(rs.wasNull());
650:
651: // Assert the string converted back into a Time matches the Time returned.
652: assertEquals(
653: "ResultSet String converted to java.sql.Time mismatch",
654: tv, getTime19700101(sv, cal));
655:
656: return tv;
657: }
658:
659: /**
660: * Check the consistency of a ResultSet column that returns
661: * a TIMESTAMP value. Can be used for any column of type TIMESTAMP.
662: *
663: * @param rs ResultSet holding the column, positioned on a row
664: * @param column Column with the TIMESTAMP value.
665: * @return Returns the Time object obtained from the column.
666: * @throws SQLException
667: */
668: private Timestamp checkTimestampValue(ResultSet rs, int column)
669: throws SQLException {
670: assertEquals(java.sql.Types.TIMESTAMP, rs.getMetaData()
671: .getColumnType(column));
672:
673: Timestamp tsv = rs.getTimestamp(column);
674: assertEquals(tsv == null, rs.wasNull());
675:
676: Object ov = rs.getObject(column);
677: assertEquals(ov == null, rs.wasNull());
678:
679: if (tsv == null) {
680: assertNull(ov);
681: return null;
682: }
683:
684: assertTrue(ov instanceof java.sql.Timestamp);
685: assertEquals(tsv, ov);
686:
687: Time tv = rs.getTime(column);
688: assertNotNull(tv);
689: assertFalse(rs.wasNull());
690:
691: // Check the date portion is set to 1970/01/01
692: assertTime1970(tv);
693:
694: // Check the TIME portion is set to the same as tv
695: // DERBY-1816 java.sql.Time values from TIMESTAMP
696: // colummns lose their precision with client.
697: if (!usingDerbyNetClient())
698: assertTimeEqual(tv, tsv);
699:
700: String sv = rs.getString(column);
701: assertNotNull(sv);
702: assertFalse(rs.wasNull());
703:
704: // Assert the string converted back into a Time matches the Time returned.
705: assertEquals(
706: "ResultSet String converted to java.sql.Timestamp mismatch",
707: tsv, Timestamp.valueOf(sv));
708:
709: return tsv;
710: }
711:
712: /**
713: * Check the consistency of a ResultSet column that returns
714: * CURRENT TIME or a value set from CURRENT TIME.
715: *
716: * @param start Time the statement settng the value was executed
717: * @param end Time after first rs.next() or update statement was executed
718: * @param rs ResultSet holding the column, positioned on a row
719: * @param column Column with the timestamp.
720: * @return Returns the Time object obtained from the column.
721: * @throws SQLException
722: */
723: private Time checkCurrentTimeValue(long start, long end,
724: ResultSet rs, int column) throws SQLException {
725: Time tv = checkTimeValue(rs, column);
726:
727: // The time returned should be between the value
728: // of start and end (inclusive of both)
729:
730: Time st = getTime19700101(start, cal);
731: Time et = getTime19700101(end, cal);
732:
733: if (st.after(et)) {
734: // Gone back in time!
735: // Well test was running around midnight and the
736: // time for the start time is equal to or before 23:59:59
737: // and end time is equal to or after 00:00:00
738:
739: assertTrue(
740: "CURRENT TIME outside of range when test crossing midnight",
741: (tv.equals(st) || tv.after(st))
742: || (tv.equals(et) || tv.before(et)));
743: } else {
744: // End time is after or equal to start time, expected case.
745:
746: // The returned time must not be before the
747: // start time or after the end time.
748: assertFalse("CURRENT TIME before start of statement", tv
749: .before(st));
750: assertFalse("CURRENT TIME after end of statement", tv
751: .after(et));
752: }
753:
754: return tv;
755: }
756:
757: /**
758: * Check the consistency of a ResultSet column that returns
759: * CURRENT TIMESTAMP or a value set from CURRENT TIMESTAMP.
760: *
761: * @param start Time the statement settng the value was executed
762: * @param end Time after first rs.next() or update statement was executed
763: * @param rs ResultSet holding the column, positioned on a row
764: * @param column Column with the timestamp.
765: * @return Returns the Timestamp object obtained from the column.
766: * @throws SQLException
767: */
768: private Timestamp checkCurrentTimestampValue(long start, long end,
769: ResultSet rs, int column) throws SQLException {
770: Timestamp tsv = checkTimestampValue(rs, column);
771:
772: // The time returned should be between the value
773: // of start and end (inclusive of both)
774:
775: Timestamp st = new Timestamp(start);
776: Timestamp et = new Timestamp(end);
777:
778: if (st.after(et)) {
779: // Gone back in time!
780: // Well test was running around midnight and the
781: // time for the start time is equal to or before 23:59:59
782: // and end time is equal to or after 00:00:00
783:
784: assertTrue(
785: "CURRENT TIME outside of range when test crossing midnight",
786: (tsv.equals(st) || tsv.after(st))
787: || (tsv.equals(et) || tsv.before(et)));
788: } else {
789: // End time is after or equal to start time, expected case.
790:
791: // The returned time must not be before the
792: // start time or after the end time.
793: assertFalse("CURRENT TIME before start of statement", tsv
794: .before(st));
795: assertFalse("CURRENT TIME after end of statement", tsv
796: .after(et));
797: }
798:
799: return tsv;
800: }
801:
802: /**
803: * Create a Time object that has its date components
804: * set to 1970/01/01 and its time to match the time
805: * represented by h, m and s. This matches Derby by
806: * setting the milli-second component to zero.
807: * <BR>
808: * Note that the Time(long) constructor for java.sql.Time
809: * does *not* set the date component to 1970/01/01.
810: * This is a requirement for JDBC java.sql.Time values though
811: */
812: private Time getTime19700101(int hour, int min, int sec) {
813: cal.clear();
814: cal.set(1970, Calendar.JANUARY, 1);
815: cal.set(Calendar.MILLISECOND, 0);
816:
817: cal.set(Calendar.HOUR_OF_DAY, hour);
818: cal.set(Calendar.MINUTE, min);
819: cal.set(Calendar.SECOND, sec);
820:
821: Time to = new Time(cal.getTime().getTime());
822: assertTime1970(to);
823: return to;
824: }
825:
826: /**
827: * Create a Time object that has its date components
828: * set to 1970/01/01 and its time to match the time
829: * represented by t and cal. This matches Derby by
830: * setting the milli-second component to zero.
831: * <BR>
832: * Note that the Time(long) constructor for java.sql.Time
833: * does *not* set the date component to 1970/01/01.
834: * This is a requirement for JDBC java.sql.Time values though
835: */
836: private Time getTime19700101(long t, Calendar cal) {
837: cal.clear();
838: // JDK 1.3 can't call this!
839: // cal.setTimeInMillis(t);
840: cal.setTime(new Date(t));
841: cal.set(1970, Calendar.JANUARY, 1);
842: cal.set(Calendar.MILLISECOND, 0);
843:
844: Time to = new Time(cal.getTime().getTime());
845: assertTime1970(to);
846: return to;
847: }
848:
849: /**
850: * Create a Time object that has its date components
851: * set to 1970/01/01 and its time to match the time
852: * represented by t and cal. This matches Derby by
853: * setting the milli-second component to zero.
854: * <BR>
855: * Note that the Time(long) constructor for java.sql.Time
856: * does *not* set the date component to 1970/01/01.
857: * This is a requirement for JDBC java.sql.Time values though
858: */
859: private Time getTime19700101(String s, Calendar cal) {
860: cal.clear();
861: // JDK 1.3 can't call this!
862: // cal.setTimeInMillis(t);
863: cal.setTime(Time.valueOf(s));
864: cal.set(1970, Calendar.JANUARY, 1);
865: cal.set(Calendar.MILLISECOND, 0);
866:
867: Time to = new Time(cal.getTime().getTime());
868: assertTime1970(to);
869: return to;
870: }
871:
872: /**
873: * Javadoc for java.sql.Time states the components of
874: * date for a java.sql.Time value must be set to January 1, 1970.
875: * Note that the java.sql.Time class does not enforce this,
876: * it is up to the driver.
877: * @param t
878: */
879: private void assertTime1970(Time t) {
880:
881: /* Cannot do this because all these methods
882: * throw IllegalArgumentException by definition,
883: * see java.sql.Time javadoc.
884:
885: assertEquals(1970, t.getYear());
886: assertEquals(0, t.getMonth());
887: assertEquals(1, t.getDate());
888: */
889: cal.clear();
890: cal.setTime(t);
891:
892: assertEquals(1970, cal.get(Calendar.YEAR));
893: assertEquals(Calendar.JANUARY, cal.get(Calendar.MONTH));
894: assertEquals(1, cal.get(Calendar.DATE));
895: }
896:
897: /**
898: * Assert the SQL time portion of two SQL JDBC type
899: * types are equal.
900: * @param tv
901: * @param tsv
902: */
903: private void assertTimeEqual(java.util.Date tv1, java.util.Date tv2) {
904: cal.clear();
905: cal.setTime(tv1);
906:
907: int hour = cal.get(Calendar.HOUR_OF_DAY);
908: int min = cal.get(Calendar.MINUTE);
909: int sec = cal.get(Calendar.SECOND);
910: int ms = cal.get(Calendar.MILLISECOND);
911:
912: // Check the time portion is set to the same as tv
913: cal.clear();
914: cal.setTime(tv2);
915: assertEquals(hour, cal.get(Calendar.HOUR_OF_DAY));
916: assertEquals(min, cal.get(Calendar.MINUTE));
917: assertEquals(sec, cal.get(Calendar.SECOND));
918: assertEquals(ms, cal.get(Calendar.MILLISECOND));
919: }
920:
921: /**
922: * Check if the date portion of a Timestamp value
923: * is equal to the date portion of a time value
924: * represented in milli-seconds since 1970.
925: */
926: private boolean isDateEqual(long d, Timestamp tsv) {
927: cal.clear();
928: cal.setTime(new java.util.Date(d));
929: int day = cal.get(Calendar.DAY_OF_MONTH);
930: int month = cal.get(Calendar.MONTH);
931: int year = cal.get(Calendar.YEAR);
932:
933: cal.clear();
934: cal.setTime(tsv);
935:
936: return day == cal.get(Calendar.DAY_OF_MONTH)
937: && month == cal.get(Calendar.MONTH)
938: && year == cal.get(Calendar.YEAR);
939: }
940: }
|