/******************************************************************************* | |
* (c) 2011-2013 Jenzabar, Inc. All rights reserved. | |
* This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 | |
* which accompanies this distribution. | |
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | |
* and the Eclipse Distribution License is available at | |
* http://www.eclipse.org/org/documents/edl-v10.php. | |
* | |
* 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.internal.databaseaccess.DatasourcePlatform; // for javadoc only | |
import org.eclipse.persistence.internal.sessions.AbstractSession; | |
import org.eclipse.persistence.expressions.ExpressionOperator; | |
import org.eclipse.persistence.internal.helper.DatabaseField; | |
import org.eclipse.persistence.internal.helper.DatabaseTable; | |
import org.eclipse.persistence.platform.database.H2Platform; // for javadoc only | |
import org.eclipse.persistence.platform.database.InformixPlatform; | |
import org.eclipse.persistence.queries.UpdateAllQuery; // for javadoc only | |
/** | |
* An {@link InformixPlatform} that fixes many EclipseLink bugs | |
* related to Informix support. | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=401393">EclipseLink | |
* bug 401393</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=401718">EclipseLink | |
* bug 401718</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=401746">EclipseLink | |
* bug 401746</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402037">EclipseLink | |
* bug 402037</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402180">EclipseLink | |
* bug 402180</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402600">EclipseLink | |
* bug 402600</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402953">EclipseLink | |
* bug 402953</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=405645">EclipseLink | |
* bug 405645</a> | |
*/ | |
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 JenzabarInformixPlatform}. Calls {@link | |
* #setShouldBindLiterals(boolean)} with {@code false} as a | |
* parameter value. | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=401718">EclipseLink | |
* bug 401718</a> | |
*/ | |
public Informix11Platform() { | |
super(); | |
this.setShouldBindLiterals(false); | |
} | |
/** | |
* Fixes <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402600">EclipseLink | |
* bug 402600</a> 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 | |
* <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402953">EclipseLink | |
* bug 402953</a> 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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402600">EclipseLink | |
* bug 402600</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402953">EclipseLink | |
* bug 402953</a> | |
*/ | |
@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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402953">EclipseLink | |
* bug 402953</a> 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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402953">EclipseLink | |
* bug 402953</a> | |
*/ | |
protected ExpressionOperator currentDateOperator() { | |
return ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.CurrentDate, "CURRENT YEAR TO DAY"); | |
} | |
/** | |
* Fixes <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402953">EclipseLink | |
* bug 402953</a> 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} | |
* | |
* <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402953">EclipseLink | |
* bug 402953</a> | |
*/ | |
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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=401393">EclipseLink | |
* bug 401393</a> | |
*/ | |
@Override | |
public boolean isInformixOuterJoin() { | |
return false; | |
} | |
/** | |
* Fixes <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=401746">EclipseLink | |
* bug 401746</a> 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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=401393">EclipseLink | |
* bug 401393</a> | |
*/ | |
@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 <em>must not</em> be used when working with temporary | |
* tables as part of an {@code UPDATE} query. | |
* | |
* <p>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}."</p> | |
* | |
* @return {@code true} when invoked | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402037">EclipseLink | |
* bug 402037</a> | |
*/ | |
@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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402037">EclipseLink | |
* bug 402037</a> | |
*/ | |
@Override | |
public boolean shouldAlwaysUseTempStorageForModifyAll() { | |
return true; | |
} | |
/** | |
* Returns {@code true} when invoked to indicate that Informix does | |
* indeed support <em>local temporary tables</em>. | |
* | |
* <p><em>local</em> is defined by EclipseLink in {@link | |
* org.eclipse.persistence.internal.databaseaccess.DatabasePlatform} | |
* to mean:</p> | |
* | |
* <blockquote>"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.</blockquote> | |
* | |
* @return {@code true} when invoked | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402037">EclipseLink | |
* bug 402037</a> | |
*/ | |
@Override | |
public boolean supportsLocalTempTables() { | |
return true; | |
} | |
/** | |
* Returns {@code CREATE TEMP TABLE} when invoked, per | |
* the <a | |
* href="http://publib.boulder.ibm.com/infocenter/idshelp/v117/topic/com.ibm.sqls.doc/ids_sqs_0571.htm">Informix | |
* 11.70 Information Center documentation</a>. | |
* | |
* <p>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.</p> | |
* | |
* @return {@code CREATE TEMP TABLE} when invoked | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402037">EclipseLink | |
* bug 402037</a> | |
*/ | |
@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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402037">EclipseLink | |
* bug 402037</a> | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402180">EclipseLink | |
* bug 402180</a> | |
*/ | |
@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 <a | |
* href="http://publib.boulder.ibm.com/infocenter/idshelp/v117/topic/com.ibm.sqls.doc/ids_sqs_0571.htm">Informix | |
* 11.70 Information Center documentation</a>, since transactions | |
* are not needed on temp tables for the purposes for which | |
* EclipseLink uses them. | |
* | |
* @return {@code WITH NO LOG} when invoked | |
* | |
* @see <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402037">EclipseLink | |
* bug 402037</a> | |
*/ | |
@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. | |
* | |
* <p>The code for this method is identical to that of the stock | |
* {@link H2Platform}, since the H2 database has the same | |
* requirement.</p> | |
* | |
* @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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=402037">EclipseLink | |
* bug 402037</a> | |
*/ | |
@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}. | |
* | |
* <p>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.</p> | |
* | |
* @param resultSet the {@link ResultSet} to {@linkplain | |
* ResultSet#getObject(int) get an <code>Object</code>} 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 <a | |
* href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=405645">EclipseLink | |
* bug 405645</a> | |
*/ | |
@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); | |
} else { | |
returnValue = super.getObjectFromResultSet(resultSet, columnNumber, jdbcType, session); | |
} | |
break; | |
default: | |
returnValue = super.getObjectFromResultSet(resultSet, columnNumber, jdbcType, session); | |
} | |
return returnValue; | |
} | |
} | |