Skip to content

Commit

Permalink
Fixed failing Date/time and math functions tests with Oracle DB. (#1546)
Browse files Browse the repository at this point in the history
* Fixed failing Date/time and math functions tests with Oracle DB.
 - fixes issue #1539
 - fixes issue #1540
 - fixes issue #1541
* Fixed failing Date/time and math functions tests with Oracle DB: Review notes applied
* Fixed failing Date/time tests in MS SQL.
 - fixes issue #1548
 - fixes issue #1549
 - fixes issue #1550
* Issue #1547: LN() failure with SQLServer
* Fixed TestJava8DateTime test failures caused by cast exceptions between date/time/datetime types.
* Skip EXTRACT(WEEK FROM date) test on Derby.

Signed-off-by: Tomas Kraus <tomas.kraus@oracle.com>
  • Loading branch information
Tomas-Kraus committed Jun 27, 2022
1 parent af30f4a commit 72436b3
Show file tree
Hide file tree
Showing 18 changed files with 830 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1317,9 +1317,11 @@ public Object getObject(ResultSet resultSet, DatabaseField field, ResultSetMetaD
}
} else {
value = platform.getObjectFromResultSet(resultSet, columnNumber, type, session);
if (PlatformWrapper.isPlatformWrapper(value)) {
value = platform.convertObject(value, field.getType());
// PERF: only perform blob check on non-optimized types.
// CR2943 - convert early if the type is a BLOB or a CLOB.
if (isBlob(type)) {
} else if (isBlob(type)) {
// EL Bug 294578 - Store previous value of BLOB so that temporary objects can be freed after conversion
Object originalValue = value;
value = platform.convertObject(value, ClassConstants.APBYTE);
Expand Down Expand Up @@ -1352,10 +1354,19 @@ public Object getObject(ResultSet resultSet, DatabaseField field, ResultSetMetaD
}

/**
* Handle the conversion into java optimally through calling the direct type API.
* If the type is not one that can be optimized return null.
*/
protected Object getObjectThroughOptimizedDataConversion(ResultSet resultSet, DatabaseField field, int type, int columnNumber, DatabasePlatform platform, AbstractSession session) throws SQLException {
* Handle the {@code ResultSet} conversion into java optimally through calling the direct type API.
*
* @param resultSet JDBC {@code ResultSet} with query result
* @param field database field mapping
* @param type database type from {@link java.sql.Types} or provider specific type
* @param columnNumber number of column to fetch
* @param platform current database platform
* @param session current database session
* @return new instance of converted type or {@code this} if no optimized conversion wasdone
*/
protected Object getObjectThroughOptimizedDataConversion(
ResultSet resultSet, DatabaseField field, int type, int columnNumber,
DatabasePlatform platform, AbstractSession session) throws SQLException {
Object value = this;// Means no optimization, need to distinguish from null.
Class<?> fieldType = field.type;

Expand All @@ -1381,6 +1392,7 @@ protected Object getObjectThroughOptimizedDataConversion(ResultSet resultSet, Da
} else if (fieldType == null) {
return this;
}

boolean isPrimitive = false;

// Optimize numeric values to avoid conversion into big-dec and back to primitives.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.Vector;

Expand Down Expand Up @@ -2614,6 +2615,8 @@ public void setParameterValueInDatabaseCall(Object parameter,
} else if (parameter instanceof java.time.LocalTime){
java.time.LocalTime lt = (java.time.LocalTime) parameter;
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), lt));
// This may cause cast exceptions, statement.setTime(index, ...) should be here, but some platforms rely on full TIMESTAMP types
// overriden to statement.setTime(index, ...) in: SQLServerPlatform
statement.setTimestamp(index, ts);
} else if (parameter instanceof java.time.OffsetTime) {
java.time.OffsetTime ot = (java.time.OffsetTime) parameter;
Expand Down Expand Up @@ -3749,7 +3752,6 @@ public boolean supportsOuterJoinsWithBrackets() {
public void freeTemporaryObject(Object value) throws SQLException {
}


/**
* INTERNAL:
* Allow initialization from the connection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

import java.io.Serializable;
import java.io.Writer;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.eclipse.persistence.exceptions.ConversionException;
Expand Down Expand Up @@ -309,4 +311,13 @@ public interface Platform extends CorePlatform<ConversionManager>, Serializable,
void removeIdentitySequences(
final Session session, final String defaultIdentityGenerator, final Set<String> tableNames);

/**
* INTERNAL:
* Get platform specific connection properties.
* @return properties to be added to connection properties
*/
default Map<Object, Object> connectionProperties() {
return Collections.emptyMap();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2022 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:
// Oracle - initial API and implementation
package org.eclipse.persistence.internal.databaseaccess;

import java.sql.ResultSet;

import org.eclipse.persistence.internal.sessions.AbstractSession;

/**
* Marks {@link DatabasePlatform} specific wrapper of instance from {@link java.sql.ResultSet}.
* Instances returned by {@link DatabasePlatform#getObjectFromResultSet(ResultSet, int, int, AbstractSession)}
* from {@code ResultSet} and marked as wrappers are considered as unknown to EclipseLink core module
* so they are returned back to {@link DatabasePlatform#convertObject(Object, Class)} method to be converted
* to target {@link org.eclipse.persistence.internal.helper.DatabaseField#type} class.
*/
public interface PlatformWrapper {

/**
* Check whether provided instance is marked as {@link DatabasePlatform} specific wrapper.
* @param instance instance to be checked
* @return value of {@code true} when provided instance is marked as {@link DatabasePlatform}
* specific wrapper or {@code false} otherwise
*/
static boolean isPlatformWrapper(Object instance) {
return PlatformWrapper.class.isInstance(instance);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ public class LoggingLocalizationResource extends ListResourceBundle {
{ "class_list_created_by", "Class list created by ({0}).({1})() method."},
{ "jar_file_url_exception", "Exception while parsing persistence.xml. Jar file location could not be found: {0}"},
{ "cannot_unwrap_connection", "Cannot unwrap the oracle connection wrapped by the application server because of the following exception. {0}"},
{ "platform_specific_connection_property_exists", "Cannot add platform specific connection property {0}={1}. Property key {0} was already added to connection properties."},
{ "error_loading_xml_file", "Exception while loading ORM xml file: {0}: {1}"},
// B5112171: XML AnyObject and AnyCollection throw NPE on null document root element
{ "exception_loading_entity_class", "An exception while trying to initialize persistence. {1} occurred while trying to load entity class: {0}."},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.databaseaccess.DatasourceCall.ParameterType;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.expressions.ExpressionJavaPrinter;
import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
import org.eclipse.persistence.internal.expressions.FunctionExpression;
import org.eclipse.persistence.internal.expressions.LiteralExpression;
import org.eclipse.persistence.internal.expressions.RelationExpression;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.helper.ClassConstants;
Expand Down Expand Up @@ -578,6 +580,8 @@ protected void initializePlatformOperators() {
addOperator(ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.Today, "SYSDATE"));
addOperator(ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.CurrentDate, "TO_DATE(CURRENT_DATE)"));
addOperator(ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.CurrentTime, "SYSDATE"));
addOperator(ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.LocalTime, "SYSDATE"));
addOperator(ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.LocalDateTime, "SYSDATE"));
addOperator(ExpressionOperator.truncateDate());
addOperator(ExpressionOperator.newTime());
addOperator(ExpressionOperator.ifNull());
Expand All @@ -587,6 +591,7 @@ protected void initializePlatformOperators() {
addOperator(operatorLocate2());
addOperator(regexpOperator());
addOperator(exceptOperator());
addOperator(oracleExtractOperator());
}

/**
Expand Down Expand Up @@ -712,6 +717,89 @@ protected static ExpressionOperator oracleDateName() {
return exOperator;
}

/**
* Oracle does not support EXTRACT QUARTER. Can be emulated using TO_NUMBER(TO_CHAR(", ", 'Q')).
*/
private ExpressionOperator oracleExtractOperator() {

ExpressionOperator exOperator = new ExpressionOperator() {

// QUARTER emulation: ((MONTH(:first)+2)/3)
private final String[] QUARTER_STRINGS = new String[] {"TO_NUMBER(TO_CHAR(", ", 'Q'))"};

private void printQuarterSQL(final Expression first, final ExpressionSQLPrinter printer) {
printer.printString(QUARTER_STRINGS[0]);
first.printSQL(printer);
printer.printString(QUARTER_STRINGS[1]);
}

private void printQuarterJava(final Expression first, final ExpressionJavaPrinter printer) {
printer.printString(QUARTER_STRINGS[0]);
first.printJava(printer);
printer.printString(QUARTER_STRINGS[1]);
}

@Override
public void printDuo(Expression first, Expression second, ExpressionSQLPrinter printer) {
if (second instanceof LiteralExpression && "QUARTER".equals(((LiteralExpression)second).getValue().toUpperCase())) {
printQuarterSQL(first, printer);
} else {
super.printDuo(first, second, printer);
}
}

@Override
public void printCollection(List<Expression> items, ExpressionSQLPrinter printer) {
if (items.size() == 2) {
Expression first = items.get(0);
Expression second = items.get(1);
if (second instanceof LiteralExpression && "QUARTER".equals(((LiteralExpression)second).getValue().toUpperCase())) {
printQuarterSQL(first, printer);
return;
}
}
super.printCollection(items, printer);
}

@Override
public void printJavaDuo(Expression first, Expression second, ExpressionJavaPrinter printer) {
if (second instanceof LiteralExpression && "QUARTER".equals(((LiteralExpression)second).getValue().toUpperCase())) {
printQuarterJava(first, printer);
} else {
super.printJavaDuo(first, second, printer);
}
}

@Override
public void printJavaCollection(List<Expression> items, ExpressionJavaPrinter printer) {
if (items.size() == 2) {
Expression first = items.get(0);
Expression second = items.get(1);
if (second instanceof LiteralExpression && "QUARTER".equals(((LiteralExpression)second).getValue().toUpperCase())) {
printQuarterJava(first, printer);
return;
}
}
super.printJavaCollection(items, printer);
}
};
List<String> v = new ArrayList<>(5);
exOperator.setType(ExpressionOperator.FunctionOperator);
exOperator.setSelector(ExpressionOperator.Extract);
exOperator.setName("EXTRACT");
v.add("EXTRACT(");
v.add(" FROM ");
v.add(")");
exOperator.printsAs(v);
int[] indices = new int[2];
indices[0] = 1;
indices[1] = 0;
exOperator.setArgumentIndices(indices);
exOperator.bePrefix();
exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
return exOperator;
}

/**
* INTERNAL:
* Used by derived platforms (Oracle8Platform and higher)
Expand Down

0 comments on commit 72436b3

Please sign in to comment.