org.eclipse.persistence.platform.database.Informix11Platform Maven / Gradle / Ivy
Show all versions of org.eclipse.persistence.core Show documentation
/*
* Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2018 Jenzabar, Inc. 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:
// Jenzabar - Initial implementation
package org.eclipse.persistence.platform.database;
import java.io.IOException;
import java.io.Serializable; // for javadoc only
import java.io.Writer;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import org.eclipse.persistence.expressions.ExpressionOperator;
import org.eclipse.persistence.internal.databaseaccess.DatasourcePlatform; // for javadoc only
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.queries.UpdateAllQuery; // for javadoc only
// for javadoc only
/**
* An {@link InformixPlatform} that fixes many EclipseLink bugs
* related to Informix support.
*
* @see EclipseLink
* bug 401393
*
* @see EclipseLink
* bug 401718
*
* @see EclipseLink
* bug 401746
*
* @see EclipseLink
* bug 402037
*
* @see EclipseLink
* bug 402180
*
* @see EclipseLink
* bug 402600
*
* @see EclipseLink
* bug 402953
*
* @see EclipseLink
* bug 405645
*/
public class Informix11Platform extends InformixPlatform {
/**
* The version of this class for serialization purposes.
*
* @see Serializable
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new {@link Informix11Platform}. Calls {@link
* #setShouldBindLiterals(boolean)} with {@code false} as a
* parameter value.
*
* @see EclipseLink
* bug 401718
*/
public Informix11Platform() {
super();
this.setShouldBindLiterals(false);
}
/**
* Fixes EclipseLink
* bug 402600 by making sure that the {@link
* ExpressionOperator#distinct() Distinct} {@link
* ExpressionOperator} is set to {@linkplain
* ExpressionOperator#printsAs(String) print as} {@code
* DISTINCT } (no parentheses, one trailing space), and fixes
* EclipseLink
* bug 402953 by {@linkplain
* DatasourcePlatform#addOperator(ExpressionOperator) adding
* platform operators} for {@linkplain
* ExpressionOperator#CurrentDate current date} and {@linkplain
* ExpressionOperator#CurrentTime current time}.
*
* @see #currentDateOperator()
*
* @see #currentTimeOperator()
*
* @see EclipseLink
* bug 402600
*
* @see EclipseLink
* bug 402953
*/
@Override
protected void initializePlatformOperators() {
final ExpressionOperator distinctOverride = ExpressionOperator.getOperator(Integer.valueOf(ExpressionOperator.Distinct));
assert distinctOverride != null;
distinctOverride.printsAs("DISTINCT "); // no parens, one space
super.initializePlatformOperators();
this.addOperator(this.currentDateOperator());
this.addOperator(this.currentTimeOperator());
}
/**
* Fixes EclipseLink
* bug 402953 by returning the result of running the following
* code: {@link ExpressionOperator#simpleFunctionNoParentheses(int,
* String)
* ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.CurrentDate,
* "CURRENT YEAR TO DAY");}
*
* @return a non-{@code null} {@link ExpressionOperator}
*
* @see EclipseLink
* bug 402953
*/
protected ExpressionOperator currentDateOperator() {
return ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.CurrentDate, "CURRENT YEAR TO DAY");
}
/**
* Fixes EclipseLink
* bug 402953 by returning the result of running the following
* code: {@link ExpressionOperator#simpleFunctionNoParentheses(int,
* String)
* ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.CurrentDate,
* "CURRENT YEAR TO FRACTION(3)");}
*
* @return a non-{@code null} {@link ExpressionOperator}
*
* EclipseLink
* bug 402953
*/
protected ExpressionOperator currentTimeOperator() {
return ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.CurrentDate, "CURRENT YEAR TO FRACTION(3)");
}
/**
* Overrides the default behavior to return {@code false},
* indicating that ANSI {@code OUTER JOIN} syntax should be used,
* since all modern versions of Informix support it.
*
* @return {@code false} when invoked
*
* @see EclipseLink
* bug 401393
*/
@Override
public boolean isInformixOuterJoin() {
return false;
}
/**
* Fixes EclipseLink
* bug 401746 by writing out either {@code 't'} or {@code
* 'f'}—with single quotes— instead of {@code 1} or
* {@code 0}.
*
* @param booleanValue a non-{@code null} {@link Boolean} to append
*
* @param writer a non-{@code null} {@link Writer} to append the
* literal value to
*
* @exception IllegalArgumentException if either {@code
* booleanValue} or {@code writer} is {@code null}
*
* @exception IOException if the supplied {@link Writer} throws an
* {@link IOException}
*
* @see EclipseLink
* bug 401393
*/
@Override
protected void appendBoolean(final Boolean booleanValue, final Writer writer) throws IOException {
if (booleanValue == null) {
throw new IllegalArgumentException("booleanValue", new NullPointerException("booleanValue"));
}
if (writer == null) {
throw new IllegalArgumentException("writer", new NullPointerException("writer"));
}
if (Boolean.TRUE.equals(booleanValue)) {
writer.write("'t'");
} else {
writer.write("'f'");
}
}
/**
* Returns {@code true} when invoked to indicate that parameter
* binding must not be used when working with temporary
* tables as part of an {@code UPDATE} query.
*
* Parsing the English is a little difficult in this method name.
* It means: "Do not bind parameters in queries that are part of an
* overall 'update all' operation where {@linkplain
* #shouldAlwaysUseTempStorageForModifyAll() temporary tables are
* being used}."
*
* @return {@code true} when invoked
*
* @see EclipseLink
* bug 402037
*/
@Override
public boolean dontBindUpdateAllQueryUsingTempTables() {
return true;
}
/**
* Returns {@code true} when invoked to force Informix to use
* temporary storage when constructing bulk {@code UPDATE}
* statements.
*
* @return {@code true} in all cases
*
* @see EclipseLink
* bug 402037
*/
@Override
public boolean shouldAlwaysUseTempStorageForModifyAll() {
return true;
}
/**
* Returns {@code true} when invoked to indicate that Informix does
* indeed support local temporary tables.
*
* local is defined by EclipseLink in {@link
* org.eclipse.persistence.internal.databaseaccess.DatabasePlatform}
* to mean:
*
* "Local" means that several threads may create
* temporary tables with the same name.
* Local temporary table is created in the beginning of {@link UpdateAllQuery}
* execution and dropped in the end of it.
*
* @return {@code true} when invoked
*
* @see EclipseLink
* bug 402037
*/
@Override
public boolean supportsLocalTempTables() {
return true;
}
/**
* Returns {@code CREATE TEMP TABLE} when invoked, per
* the Informix
* 11.70 Information Center documentation.
*
* While Informix 11.70 supports an additional {@code IF NOT
* EXISTS} clause, Informix 11.50 does not, so this method does not
* add the {@code IF NOT EXISTS} clause. In practice this should be
* fine as temporary tables are scoped to the Informix session, and
* EclipseLink takes care of cleaning up any temporary tables that
* it creates.
*
* @return {@code CREATE TEMP TABLE} when invoked
*
* @see EclipseLink
* bug 402037
*/
@Override
protected String getCreateTempTableSqlPrefix() {
return "CREATE TEMP TABLE ";
}
/**
* Overrides the superclass implementation to return a new {@link
* DatabaseTable} with no {@linkplain
* DatabaseTable#getTableQualifier()}. Informix 11.50 and above do
* not support table qualifiers of any kind on temporary tables.
*
* @param table the {@link DatabaseTable} for which a temporary
* {@link DatabaseTable} should be returned; must not be {@code
* null}
*
* @return a new {@link DatabaseTable} with no {@linkplain
* DatabaseTable#getTableQualifier() table qualifier}
*
* @exception IllegalArgumentException if {@code table} is {@code
* null}
*
* @see EclipseLink
* bug 402037
*
* @see EclipseLink
* bug 402180
*/
@Override
public DatabaseTable getTempTableForTable(final DatabaseTable table) {
if (table == null) {
throw new IllegalArgumentException("table", new NullPointerException("table"));
}
return new DatabaseTable("TL_" + table.getName(), "" /* no table qualifier */, table.shouldUseDelimiters(), this.getStartDelimiter(), this.getEndDelimiter());
}
/**
* Returns {@code WITH NO LOG} when invoked, per the Informix
* 11.70 Information Center documentation, since transactions
* are not needed on temp tables for the purposes for which
* EclipseLink uses them.
*
* @return {@code WITH NO LOG} when invoked
*
* @see EclipseLink
* bug 402037
*/
@Override
protected String getCreateTempTableSqlSuffix() {
return " WITH NO LOG";
}
/**
* Overrides {@link
* org.eclipse.persistence.internal.databaseaccess.DatabasePlatform#writeUpdateOriginalFromTempTableSql(Writer,
* DatabaseTable, Collection, Collection)} to ensure that a {@code
* SET} clause that is supposed to set a value for only one column
* does not wrap that column name in parentheses.
*
* The code for this method is identical to that of the stock
* {@link H2Platform}, since the H2 database has the same
* requirement.
*
* @param writer the {@link Writer} that will be writing SQL; must
* not be {@code null}
*
* @param table the {@link DatabaseTable} to be updated; must not be
* {@code null}
*
* @param pkFields a {@link Collection} of {@link DatabaseField}s
* each of which is a primary key of the table to be updated; must
* not be {@code null}
*
* @param assignedFields a {@link Collection} of {@link
* DatabaseField}s that are receiving updated values; must not be
* {@code null}
*
* @exception IOException if an input/output error is thrown by the
* supplied {@link Writer}
*
* @exception NullPointerException if any of the supplied parameters
* is {@code null}
*
* @see EclipseLink
* bug 402037
*/
@Override
public void writeUpdateOriginalFromTempTableSql(final Writer writer, final DatabaseTable table, final Collection pkFields, final Collection assignedFields) throws IOException {
writer.write("UPDATE ");
final String tableName = table.getQualifiedNameDelimited(this);
writer.write(tableName);
writer.write(" SET ");
final int size = assignedFields.size();
if (size > 1) {
writer.write("(");
}
writeFieldsList(writer, assignedFields, this);
if (size > 1) {
writer.write(")");
}
writer.write(" = (SELECT ");
writeFieldsList(writer, assignedFields, this);
writer.write(" FROM ");
final String tempTableName = this.getTempTableForTable(table).getQualifiedNameDelimited(this);
writer.write(tempTableName);
writeAutoJoinWhereClause(writer, null, tableName, pkFields, this);
writer.write(") WHERE EXISTS(SELECT ");
writer.write(((DatabaseField)pkFields.iterator().next()).getNameDelimited(this));
writer.write(" FROM ");
writer.write(tempTableName);
writeAutoJoinWhereClause(writer, null, tableName, pkFields, this);
writer.write(")");
}
/**
* Overrides {@link
* org.eclipse.persistence.internal.databaseaccess.DatabasePlatform#getObjectFromResultSet(ResultSet,
* int, int, AbstractSession)} so that if the supplied {@code
* jdbcType} is equal to {@link Types#LONGVARCHAR}, then the {@link
* ResultSet#getString(int)} method (instead of the {@link
* ResultSet#getObject(int)} method) is used to fetch the "raw"
* value of the column indicated by the supplied {@code
* columnNumber} from the supplied {@link ResultSet}.
*
* This works around an issue with the Informix JDBC driver,
* version 3.70.JC7 and earlier, where a {@code TEXT} column is
* reported to be mappable to the JDBC type of {@link
* Types#LONGVARCHAR LONGVARCHAR}, but the actual object returned by
* {@link ResultSet#getObject(int)} is a {@code byte[]} (instead of
* either a {@code char[]} or a {@link String}). Invoking {@link
* ResultSet#getString(int)} instead returns the desired result.
*
* @param resultSet the {@link ResultSet} to {@linkplain
* ResultSet#getObject(int) get an Object
} from; may be
* {@code null}
*
* @param columnNumber the {@code 1}-based column number indicating
* the column to use
*
* @param jdbcType an {@code int} indicating the {@linkplain Types
* JDBC type} of which the column in the {@link ResultSet} indicated
* by the supplied {@code columnNumber} is expected to be
*
* @param session an {@link AbstractSession} that, when used at all,
* is passed to the superclass implementation of this method; may be
* {@code null}
*
* @return the {@link Object} resulting from a call to {@link
* ResultSet#getObject(int)}; may be {@code null}
*
* @exception SQLException if any error occurs
*
* @see EclipseLink
* bug 405645
*/
@Override
public Object getObjectFromResultSet(final ResultSet resultSet, final int columnNumber, final int jdbcType, final AbstractSession session) throws SQLException {
Object returnValue = null;
switch (jdbcType) {
case Types.LONGVARCHAR:
if (resultSet != null) {
returnValue = resultSet.getString(columnNumber);
}
break;
default:
returnValue = super.getObjectFromResultSet(resultSet, columnNumber, jdbcType, session);
}
return returnValue;
}
}