Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion openjpa-integration/tck/run-tck32.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ declare -A DOCKER_NAMES=(
[mysql]=mysql
[postgresql]=postgresql
[mssql]=server
[oracle]=oracle-xe
[oracle]=oracle-free
)

declare -A VERSIONS=(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
*/
package org.apache.openjpa.jdbc.kernel.exps;

import java.sql.Time;

import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
import org.apache.openjpa.jdbc.sql.SQLBuffer;
import org.apache.openjpa.jdbc.sql.Select;
import org.apache.openjpa.kernel.exps.ExpressionVisitor;
Expand All @@ -38,6 +41,20 @@ abstract class AbstractVal
protected static final String FALSE = "1 <> 1";
private String _alias = null;

/**
* Return the type code to load the value of this expression with: {@link JavaSQLTypes#SQL_DATE} for a
* {@link java.sql.Date} and {@link JavaSQLTypes#TIME} for a {@link Time}, as a driver can return another type
* for the column, such as the Timestamp the Oracle driver returns for a DATE column; else
* {@link JavaSQLTypes#JDBC_DEFAULT}.
*/
protected int getLoadTypeCode() {
Class<?> type = getType();
if (type == java.sql.Date.class || type == Time.class) {
return JavaSQLTypes.getDateTypeCode(type);
}
return JavaSQLTypes.JDBC_DEFAULT;
}

@Override
public boolean isVariable() {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.sql.SQLException;

import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
import org.apache.openjpa.jdbc.sql.Joins;
import org.apache.openjpa.jdbc.sql.Raw;
import org.apache.openjpa.jdbc.sql.Result;
Expand Down Expand Up @@ -162,8 +161,7 @@ private SQLBuffer newSQLBuffer(Select sel, ExpContext ctx, ExpState state) {
@Override
public Object load(ExpContext ctx, ExpState state, Result res)
throws SQLException {
return Filters.convert(res.getObject(this,
JavaSQLTypes.JDBC_DEFAULT, null), getType());
return Filters.convert(res.getObject(this, getLoadTypeCode(), null), getType());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.sql.SQLException;
import java.sql.Types;

import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.Joins;
import org.apache.openjpa.jdbc.sql.Raw;
Expand Down Expand Up @@ -203,8 +202,7 @@ private SQLBuffer newSQLBuffer(Select sel, ExpContext ctx, ExpState state) {
@Override
public Object load(ExpContext ctx, ExpState state, Result res)
throws SQLException {
return Filters.convert(res.getObject(this,
JavaSQLTypes.JDBC_DEFAULT, null), getType());
return Filters.convert(res.getObject(this, getLoadTypeCode(), null), getType());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.sql.SQLException;

import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
import org.apache.openjpa.jdbc.sql.Raw;
import org.apache.openjpa.jdbc.sql.Result;
import org.apache.openjpa.jdbc.sql.SQLBuffer;
Expand Down Expand Up @@ -145,8 +144,7 @@ private SQLBuffer newSQLBuffer(Select sel, ExpContext ctx, ExpState state) {
@Override
public Object load(ExpContext ctx, ExpState state, Result res)
throws SQLException {
return Filters.convert(res.getObject(this,
JavaSQLTypes.JDBC_DEFAULT, null), getType());
return Filters.convert(res.getObject(this, getLoadTypeCode(), null), getType());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.sql.SQLException;

import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.Joins;
import org.apache.openjpa.jdbc.sql.Raw;
Expand Down Expand Up @@ -216,8 +215,7 @@ private SQLBuffer newSQLBuffer(Select sel, ExpContext ctx, ExpState state) {
@Override
public Object load(ExpContext ctx, ExpState state, Result res)
throws SQLException {
return Filters.convert(res.getObject(this,
JavaSQLTypes.JDBC_DEFAULT, null), getType());
return Filters.convert(res.getObject(this, getLoadTypeCode(), null), getType());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.schema.ForeignKey;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;

/**
Expand Down Expand Up @@ -151,6 +152,10 @@ public void testCycleOfNonNullableLogicalForeignKeys() {
* violate the constraint.
*/
public void testCycleOfNullableLogicalForeignKeys() {
// the statements are counted, so they are not batched, as on Oracle and DB2 by default
DBDictionary dict = getDbDictionary(emf);
int batchLimit = dict.getBatchLimit();
dict.setBatchLimit(0);
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
Expand Down Expand Up @@ -183,6 +188,7 @@ public void testCycleOfNullableLogicalForeignKeys() {
assertEquals(sql.toString(), 2, deletes);
em.getTransaction().commit();
} finally {
dict.setBatchLimit(batchLimit);
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ public class BulkDeleteSharedA {
@PersistentCollection
@ContainerTable(name = "BULK_SHARED_COLL",
joinColumns = {
@XJoinColumn(name = "OWNER_ID", referencedColumnName = "ID",
columnDefinition = "BIGINT"),
Comment thread
solomax marked this conversation as resolved.
Resolved
@XJoinColumn(name = "OWNER_ID", referencedColumnName = "ID"),
@XJoinColumn(name = "DISC", referencedColumnName = "'A'",
columnDefinition = "VARCHAR(1)") })
@ElementColumn(name = "VAL")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ public class BulkDeleteSharedB {
@PersistentCollection
@ContainerTable(name = "BULK_SHARED_COLL",
joinColumns = {
@XJoinColumn(name = "OWNER_ID", referencedColumnName = "ID",
columnDefinition = "BIGINT"),
@XJoinColumn(name = "OWNER_ID", referencedColumnName = "ID"),
@XJoinColumn(name = "DISC", referencedColumnName = "'B'",
columnDefinition = "VARCHAR(1)") })
@ElementColumn(name = "VAL")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import jakarta.persistence.EntityManager;

import org.apache.openjpa.jdbc.sql.OracleDictionary;
import org.apache.openjpa.kernel.QueryImpl;
import org.apache.openjpa.persistence.simple.AllFieldTypes;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
Expand Down Expand Up @@ -193,7 +194,8 @@ public void testCaseAndNullIfInMemory() {
};
for (String s : select) {
String jpql = "SELECT " + s + " FROM AllFieldTypes e";
List<String> db = sorted(timestamps(em.createQuery(jpql).getResultList()));
List<?> values = em.createQuery(jpql).getResultList();
List<String> db = sorted(s.startsWith("NULLIF") && s.contains("{d ") ? dates(values) : timestamps(values));
assertEquals(jpql, 2, db.size());
assertFalse(jpql, db.get(0).equals(db.get(1)));
assertEquals(jpql, db, sorted(inMemory(em, all, jpql)));
Expand All @@ -212,6 +214,28 @@ public void testCaseAndNullIfInMemory() {
}
}

/**
* Oracle has no type for a date without a time of day: a date field is
* mapped to a DATE column, for which the Oracle driver returns a
* {@link Timestamp}. The type of NULLIF over a date field and an escape is
* not known to OpenJPA, so that Timestamp is passed on. A Timestamp at the
* start of a day is compared as the {@link Date} it stands for.
*/
private List<?> dates(List<?> values) {
if (!(getDBDictionary() instanceof OracleDictionary)) {
return values;
}
List<Object> result = new ArrayList<>();
for (Object value : values) {
if (value instanceof Timestamp ts && ts.toLocalDateTime().toLocalTime().equals(LocalTime.MIDNIGHT)) {
result.add(Date.valueOf(ts.toLocalDateTime().toLocalDate()));
} else {
result.add(value);
}
}
return result;
}

/**
* The type of NULLIF over a timestamp field and an escape is not known
* to OpenJPA, so the value the driver returns is passed on. MySQL 8.4
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.jpql.expressions;

import java.sql.Date;
import java.sql.Time;
import java.util.List;

import jakarta.persistence.EntityManager;

import org.apache.openjpa.persistence.simple.AllFieldTypes;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;

/**
* COALESCE and CASE over a java.sql.Date or java.sql.Time field return a value of the type of the field, also
* when the driver returns another type for the column, such as the Timestamp the Oracle driver returns for the
* DATE column of such a field.
*/
public class TestTemporalConditionalExpressions extends SingleEMFTestCase {

private static final Date DATE = Date.valueOf("2021-01-03");
private static final Time TIME = Time.valueOf("10:20:30");

@Override
public void setUp() {
setUp(AllFieldTypes.class, CLEAR_TABLES);
try (EntityManager em = emf.createEntityManager()) {
em.getTransaction().begin();
AllFieldTypes e = new AllFieldTypes();
e.setSqlDateField(DATE);
e.setSqlTimeField(TIME);
em.persist(e);
em.getTransaction().commit();
}
}

public void testDate() {
assertResult(DATE, "SELECT COALESCE(e.sqlDateField, e.sqlDateField) FROM AllFieldTypes e");
assertResult(DATE, "SELECT CASE WHEN e.intField = 0 THEN e.sqlDateField ELSE e.sqlDateField END "
+ "FROM AllFieldTypes e");
}

public void testTime() {
assertResult(TIME, "SELECT COALESCE(e.sqlTimeField, e.sqlTimeField) FROM AllFieldTypes e");
assertResult(TIME, "SELECT CASE WHEN e.intField = 0 THEN e.sqlTimeField ELSE e.sqlTimeField END "
+ "FROM AllFieldTypes e");
}

private void assertResult(Object expected, String jpql) {
try (EntityManager em = emf.createEntityManager()) {
List<?> result = em.createQuery(jpql).getResultList();
assertEquals(jpql, 1, result.size());
assertEquals(jpql, expected.getClass(), result.get(0).getClass());
assertEquals(jpql, expected.toString(), result.get(0).toString());
}
}
}
67 changes: 26 additions & 41 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1484,20 +1484,16 @@
<!--
Profile for testing with Oracle DB against a Docker image

Attention: This might requiere a manual download of the Oracle JDBC driver.
This driver (ojdbc6.jar) can be downloaded at the Oracle homepage.

Then determine which version you got
$> unzip -p ojdbc6.jar META-INF/MANIFEST.MF
This will contain something like
"Implementation-Version: 11.2.0.4.0"
Use this version to install the JDBC driver into your local Maven repository
with
$> mvn install:file ...

By default we use a ojdbc6 version hosted at Atlassian.
For this to work we also need to add a repository section.
We only use it for the ojdbc driver though.
Uses the Oracle Database Free image (gvenzl/oracle-free, available for amd64 and arm64)
and the ojdbc11 driver from Maven Central:

$> mvn -N -Ptest-oracle-docker docker:start
$> mvn install -Ptest-oracle-docker
$> mvn -N -Ptest-oracle-docker docker:stop

docker:stop also removes the container and its volumes. The scripts in src/test/sql/oracle-docker
run once when the database is created; they raise the number of processes and sessions,
as the test suite opens more connections than the image allows by default.
-->
<profile>
<id>test-oracle-docker</id>
Expand All @@ -1507,11 +1503,11 @@
</property>
</activation>
<properties>
<oracle.groupid>com.oracle.ojdbc</oracle.groupid>
<oracle.artifactid>ojdbc8</oracle.artifactid>
<oracle.version>19.3.0.0</oracle.version>
<oracle.groupid>com.oracle.database.jdbc</oracle.groupid>
<oracle.artifactid>ojdbc11</oracle.artifactid>
<oracle.version>23.26.4.0.0</oracle.version>

<oracle.server.version>18.4.0</oracle.server.version>
<oracle.server.version>23-slim</oracle.server.version>
<docker.external.oracle.port>1521</docker.external.oracle.port>

<connection.driver.name>oracle.jdbc.driver.OracleDriver</connection.driver.name>
Expand Down Expand Up @@ -1550,7 +1546,7 @@
<removeVolumes>true</removeVolumes>
<images>
<image>
<name>gvenzl/oracle-xe:${oracle.server.version}</name>
<name>gvenzl/oracle-free:${oracle.server.version}</name>
<run>
<env>
<ORACLE_PASSWORD>${openjpa.oracle.password}</ORACLE_PASSWORD>
Expand All @@ -1560,12 +1556,11 @@
</env>
<volumes>
<bind>
<volume>${project.basedir}/src/test/sql/oracle-docker:/docker-entrypoint-initdb.d/</volume>
<volume>${project.basedir}/src/test/sql/oracle-docker:/container-entrypoint-initdb.d/</volume>
</bind>
</volumes>
<ports>
<port>${docker.external.oracle.port}:1521</port>
<port>5500:5500</port>
</ports>
<wait>
<log>DATABASE IS READY TO USE</log>
Expand All @@ -1583,27 +1578,18 @@
<!-- Profile for testing with Oracle DB -->
<profile>
<!--
Example oracle profile. You can use this profile if you:
1) have the Oracle artifacts installed in a local repo and
supply the URL:
-Doracle.maven.repo=http://my.local.repo
2) have a copy of the Oracle driver and run the following
command:
mvn install:install-file -Dfile=${path to ojdbc.jar} \
-DgroupId=com.oracle \
-DartifactId=jdbc-driver \
-Dversion=10g \
-Dpackaging=jar
Profile for testing with an Oracle database of your own. The Oracle
JDBC driver is taken from Maven Central (com.oracle.database.jdbc:ojdbc11).

You must also set the following properties:
You must set the following properties:
-Dopenjpa.oracle.url
-Dopenjpa.oracle.username
-Dopenjpa.oracle.password

Optionally, you can override the default Oracle groupId and
version by also supplying the following properties:
-Doracle.groupid=com.oracle
-Doracle.version=10g
Optionally, you can use another driver by supplying the following properties:
-Doracle.groupid=com.oracle.database.jdbc
-Doracle.artifactid=ojdbc11
-Doracle.version=23.26.4.0.0
-->
<id>test-oracle</id>
<activation>
Expand All @@ -1620,10 +1606,9 @@
</dependency>
</dependencies>
<properties>
<oracle.maven.repo>http://not.a.real.repository</oracle.maven.repo>
<oracle.groupid>com.oracle.ojdbc</oracle.groupid>
<oracle.artifactid>ojdbc8</oracle.artifactid>
<oracle.version>19.3.0.0</oracle.version>
<oracle.groupid>com.oracle.database.jdbc</oracle.groupid>
<oracle.artifactid>ojdbc11</oracle.artifactid>
<oracle.version>23.26.4.0.0</oracle.version>
<connection.driver.name>oracle.jdbc.driver.OracleDriver</connection.driver.name>
<!--<connection.url>jdbc:oracle:thin:@localhost:1521:OPENJPA</connection.url>-->
<connection.url>${openjpa.oracle.url}</connection.url>
Expand Down
Loading
Loading