blob: 65a7573177835150d66e8dc920f7ecb727f6922b [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// dminsky - initial API and implementation
package org.eclipse.persistence.testing.tests.types;
import java.text.*;
import java.util.*;
import oracle.sql.*;
import org.eclipse.persistence.sessions.*;
import org.eclipse.persistence.descriptors.*;
import org.eclipse.persistence.expressions.*;
import org.eclipse.persistence.queries.*;
import org.eclipse.persistence.tools.schemaframework.*;
import org.eclipse.persistence.platform.database.oracle.*;
import org.eclipse.persistence.testing.framework.*;
/**
* EL Bug 249500 - Daylight savings time is not printed in native SQL run against Oracle 9i and above
* Regression test for validating that when a Calendar is inserted with native SQL enabled and binding
* disabled, TZR TZD is printed when the Calendar reports that it is in DST (daylight savings time).
* For other scenarios (DST not present, DST not used, DST not used anymore), only TZR is printed.
*
* Fix involves:
* - Adding a method: TIMESTAMPHelper.shouldAppendDaylightTime() to determine if TZD data should be
* printed in addition to TZR data.
* - Changed TIMESTAMPHelper.printCalendar() to check TIMESTAMPHelper.shouldAppendDaylightTime() and
* print the relevant data for the TZR TZD / TZR element(s).
* - Changed Oracle9Platform.appendCalendar() to print TZR TZD or TZR depending on whether the
* Calendar instance passed is in DST or not.
*/
public class CalendarDaylightSavingsTest extends TestCase {
// persistent fields
private int testId;
private Calendar calendar;
// transient fields
private QuerySQLTracker sqlTracker;
private boolean oldBindingValue;
private boolean oldNativeSqlValue;
private CalendarDaylightSavingsTest result;
// static test data
private static List<Calendar> testCalendars;
public CalendarDaylightSavingsTest() {
super();
}
public CalendarDaylightSavingsTest(int testId, Calendar calendar) {
super();
setTestId(testId);
setCalendar(calendar);
setDescription(super.getDescription() + " -> " + formatCalendarAsString(calendar));
}
public Calendar getCalendar() {
return calendar;
}
public void setCalendar(Calendar calendar) {
this.calendar = calendar;
}
public int getTestId() {
return testId;
}
public void setTestId(int testId) {
this.testId = testId;
}
/**
* Fancy mechanism for creating test Calendars for each TimeZone:
* - 10 years in the past
* - 10 years in the future
* - this current date
*/
public static List<Calendar> getTestCalendars() {
if (testCalendars == null) {
final String[] timeZones = new String[] {
"US/Eastern",
"US/Pacific",
"EST",
"PST",
"GMT",
"UTC",
"America/New_York",
"America/Los_Angeles",
"Asia/Tokyo",
"Europe/London",
"Australia/Sydney", // uses DST
"Australia/Brisbane", // used to use DST
"Asia/Calcutta", // used to use DST
"Asia/Bangkok", // never used DST
"Asia/Riyadh", // never used DST
"Asia/Jayapura", // never used DST
};
testCalendars = new ArrayList<Calendar>(timeZones.length * 4);
for (int i = 0; i < timeZones.length; i++) {
int[] months = new int[] { 0, -6 }; // - 6 months and current month
for (int j = 0; j < months.length; j++) {
int[] years = new int[] { -10, 0, 10 }; // -10 years, current year and +10 years
for (int k = 0; k < years.length; k++) {
TimeZone timeZone = TimeZone.getTimeZone(timeZones[i]);
Calendar cal = Calendar.getInstance(timeZone);
cal.set(Calendar.DATE, 1);
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + months[j]);
cal.set(Calendar.YEAR, cal.get(Calendar.YEAR) + years[k]);
cal.set(Calendar.HOUR, 1);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.AM_PM, Calendar.PM);
testCalendars.add(cal);
}
}
}
}
return testCalendars;
}
public String toString() {
return "Test #: " + getTestId() + " -> " + formatCalendarAsString(this.calendar);
}
/**
* Return a test instance for every item of test data
*/
public static Vector testInstancesWithNoBindingAndNativeSql() {
Vector tests = new Vector(getTestCalendars().size());
for (int i = 0; i < getTestCalendars().size(); i++) {
tests.add(new CalendarDaylightSavingsTest(i + 1, getTestCalendars().get(i)));
}
return tests;
}
@Override
public void setup() {
if (!(getSession().getPlatform() instanceof Oracle9Platform)) {
throw new TestWarningException("Test is only supported on Oracle9 platform and above, as TIMESTAMPTZ is used");
}
Oracle9Platform platform = (Oracle9Platform) getSession().getPlatform();
this.oldBindingValue = platform.shouldBindAllParameters();
this.oldNativeSqlValue = platform.usesNativeSQL();
// parameter binding must be off and native SQL must be on
platform.setShouldBindAllParameters(false);
platform.setUsesNativeSQL(true);
// delete myself if I exist because we need to perform an insert not an update
try {
UnitOfWork uow = getSession().acquireUnitOfWork();
Object objectToDelete = uow.readObject(
getClass(),
new ExpressionBuilder().get("testId").equal(getTestId()));
if (objectToDelete != null) {
uow.deleteObject(objectToDelete);
uow.commit();
}
} catch (Exception e) {
throw new TestWarningException("Error whilst removing test object: " + getTestId(), e);
}
// create tracker object for query SQL
sqlTracker = new QuerySQLTracker(getSession());
}
@Override
public void test() {
// write myself out to the database
UnitOfWork uow = getSession().acquireUnitOfWork();
uow.registerObject(this);
uow.commit();
getSession().getIdentityMapAccessor().initializeIdentityMap(getClass());
// read myself back from the database (bypassing the cache) with my id
// this is to check that the object was correctly written to the database and that it exists
ReadObjectQuery query = new ReadObjectQuery(CalendarDaylightSavingsTest.class);
ExpressionBuilder builder = query.getExpressionBuilder();
Expression expression = builder.get("testId").equal(getTestId());
query.setSelectionCriteria(expression);
query.setCacheUsage(query.DoNotCheckCache);
// cache the result for later verification
result = (CalendarDaylightSavingsTest)getSession().executeQuery(query);
}
@Override
public void verify() {
final String TZR = "TZR";
final String TZR_TZD = "TZR TZD";
// add in check to determine if the SQL executed has the correct TZD and TZR fields in it
List<String> statements = sqlTracker.getSqlStatements();
if (statements.isEmpty()) {
throw new TestErrorException("Test: " + getTestId() + " - No statements executed!");
}
if (this.result == null) {
throw new TestErrorException("Result from database was null for test id: " + getTestId());
}
// get the SQL statement that was executed
String statement = statements.get(0).toUpperCase();
// if the TimeZone is in daylight time, we expect that the statement contains 'TZR TZD'
// if the TimeZone is not in daylight time, we expect that the statement contains only 'TZR'
TimeZone timeZone = calendar.getTimeZone();
Date date = calendar.getTime();
if (timeZone.inDaylightTime(date) && !statement.contains(TZR_TZD)) {
throw new TestErrorException("Test: " + getTestId() + " - Expected sql statement string with: " + TZR_TZD + " -> " + statement);
} else if (!timeZone.inDaylightTime(date) && !statement.contains(TZR)) {
throw new TestErrorException("Test: " + getTestId() + " - Expected sql statement string with: " + TZR + " -> " + statement);
}
}
@Override
public void reset() {
// Compatibility for Oracle 9 and above is checked in the setup() method
Oracle9Platform platform = (Oracle9Platform) getSession().getPlatform();
platform.setUsesNativeSQL(this.oldNativeSqlValue);
platform.setShouldBindAllParameters(this.oldBindingValue);
// reset the SQL tracker
if (sqlTracker != null) {
sqlTracker.remove();
sqlTracker = null;
}
}
// Calendar pretty printing, not used for DB operations
protected static String formatCalendarAsString(final Calendar cal) {
final StringBuilder buffer = new StringBuilder();
final TimeZone zone = cal.getTimeZone();
final SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
sdf.setCalendar(cal);
sdf.setTimeZone(zone);
buffer.append(sdf.format(cal.getTime()));
buffer.append(" ");
buffer.append(zone.getID());
// SimpleDateFormat is not good enough to print exactly what I need to see from the TimeZone
if (zone.inDaylightTime(cal.getTime())) {
buffer.append(" ");
buffer.append(zone.getDisplayName(true, TimeZone.SHORT));
}
return buffer.toString();
}
/*
public static void main(String args[]) {
List<Calendar> calendarz = getTestCalendars();
Iterator<Calendar> i = calendarz.iterator();
while (i.hasNext()) {
Calendar cal = i.next();
System.out.println(formatCalendarAsString(cal));
}
}*/
// Descriptor and Table information
private static RelationalDescriptor commonDescriptor() {
RelationalDescriptor descriptor = new RelationalDescriptor();
descriptor.setJavaClass(CalendarDaylightSavingsTest.class);
descriptor.setTableName(tableDefinition().getName());
descriptor.setPrimaryKeyFieldName("TEST_ID");
descriptor.addDirectMapping("testId", "getTestId", "setTestId", "TEST_ID");
return descriptor;
}
public static RelationalDescriptor descriptorWithAccessors() {
RelationalDescriptor descriptor = commonDescriptor();
descriptor.addDirectMapping("calendar", "getCalendar", "setCalendar", "TSTZ_DATA");
return descriptor;
}
public static RelationalDescriptor descriptor() {
RelationalDescriptor descriptor = commonDescriptor();
descriptor.addDirectMapping("calendar", "TSTZ_DATA");
return descriptor;
}
public static TableDefinition tableDefinition() {
TableDefinition definition = new TableDefinition();
definition.setName("CALENDARTOTSTZ_NATIVE");
definition.addField("TEST_ID", Integer.class);
definition.addField("TSTZ_DATA", TIMESTAMPTZ.class);
return definition;
}
}