/******************************************************************************* | |
* Copyright (c) 1998, 2013 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 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: | |
* Oracle - initial API and implementation from Oracle TopLink | |
******************************************************************************/ | |
package org.eclipse.persistence.platform.database; | |
import java.io.*; | |
import java.util.*; | |
import org.eclipse.persistence.exceptions.*; | |
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition; | |
import org.eclipse.persistence.internal.helper.*; | |
import org.eclipse.persistence.queries.*; | |
/** | |
* <p><b>Purpose</b>: Provides Informix specific behavior. | |
* <p><b>Responsibilities</b>:<ul> | |
* <li> Types for schema creation. | |
* <li> Native sequencing using @@SERIAL. | |
* </ul> | |
* | |
* @since TOPLink/Java 1.0.1 | |
*/ | |
public class InformixPlatform extends org.eclipse.persistence.platform.database.DatabasePlatform { | |
/** | |
* Answer a platform correct string representation of a Date, suitable for SQL generation. | |
* Native format: 'yyyy-mm-dd | |
*/ | |
@Override | |
protected void appendDate(java.sql.Date date, Writer writer) throws IOException { | |
if (usesNativeSQL()) { | |
writer.write("'"); | |
writer.write(Helper.printDate(date)); | |
writer.write("'"); | |
} else { | |
super.appendDate(date, writer); | |
} | |
} | |
/** | |
* Write a timestamp in Informix specific format (yyyy-mm-dd hh:mm:ss.fff). | |
*/ | |
protected void appendInformixTimestamp(java.sql.Timestamp timestamp, Writer writer) throws IOException { | |
writer.write("'"); | |
writer.write(Helper.printTimestampWithoutNanos(timestamp)); | |
writer.write('.'); | |
// Must truncate the nanos to three decimal places, | |
// it is actually a complex algorithm... | |
String nanoString = Integer.toString(timestamp.getNanos()); | |
int numberOfZeros = 0; | |
for (int num = Math.min(9 - nanoString.length(), 3); num > 0; num--) { | |
writer.write('0'); | |
numberOfZeros++; | |
} | |
if ((nanoString.length() + numberOfZeros) > 3) { | |
nanoString = nanoString.substring(0, (3 - numberOfZeros)); | |
} | |
writer.write(nanoString); | |
writer.write("'"); | |
} | |
/** | |
* Answer a platform correct string representation of a Calendar, suitable for SQL generation. | |
* The date is printed in the ODBC platform independent format {d'YYYY-MM-DD'}. | |
*/ | |
@Override | |
protected void appendCalendar(Calendar calendar, Writer writer) throws IOException { | |
if (usesNativeSQL()) { | |
appendInformixCalendar(calendar, writer); | |
} else { | |
super.appendCalendar(calendar, writer); | |
} | |
} | |
/** | |
* Write a timestamp in Informix specific format ( yyyy-mm-dd hh:mm:ss.fff) | |
*/ | |
protected void appendInformixCalendar(Calendar calendar, Writer writer) throws IOException { | |
writer.write("'"); | |
writer.write(Helper.printCalendar(calendar)); | |
writer.write("'"); | |
} | |
/** | |
* Answer a platform correct string representation of a Time, suitable for SQL generation. | |
* The time is printed in the ODBC platform independent format {t'hh:mm:ss'}. | |
*/ | |
@Override | |
protected void appendTime(java.sql.Time time, Writer writer) throws IOException { | |
if (usesNativeSQL()) { | |
writer.write("'"); | |
writer.write(Helper.printTime(time)); | |
writer.write("'"); | |
} else { | |
super.appendTime(time, writer); | |
} | |
} | |
/** | |
* Answer a platform correct string representation of a Timestamp, suitable for SQL generation. | |
* The date is printed in the ODBC platform independent format {d'YYYY-MM-DD'}. | |
*/ | |
@Override | |
protected void appendTimestamp(java.sql.Timestamp timestamp, Writer writer) throws IOException { | |
if (usesNativeSQL()) { | |
appendInformixTimestamp(timestamp, writer); | |
} else { | |
super.appendTimestamp(timestamp, writer); | |
} | |
} | |
@Override | |
protected Hashtable buildFieldTypes() { | |
Hashtable fieldTypeMapping; | |
fieldTypeMapping = new Hashtable(); | |
fieldTypeMapping.put(Boolean.class, new FieldTypeDefinition("SMALLINT default 0", false)); | |
fieldTypeMapping.put(Integer.class, new FieldTypeDefinition("INTEGER", false)); | |
fieldTypeMapping.put(Long.class, new FieldTypeDefinition("NUMERIC", 19)); | |
fieldTypeMapping.put(Float.class, new FieldTypeDefinition("FLOAT(16)", false)); | |
// Bug 218183: Informix 11 FLOAT precision max is 16 - substitute DECIMAL(32) for FLOAT(32) | |
fieldTypeMapping.put(Double.class, new FieldTypeDefinition("DECIMAL(32)", false)); | |
fieldTypeMapping.put(Short.class, new FieldTypeDefinition("SMALLINT", false)); | |
fieldTypeMapping.put(Byte.class, new FieldTypeDefinition("SMALLINT", false)); | |
fieldTypeMapping.put(java.math.BigInteger.class, new FieldTypeDefinition("DECIMAL", 32)); | |
fieldTypeMapping.put(java.math.BigDecimal.class, new FieldTypeDefinition("DECIMAL", 32).setLimits(32, -19, 19)); | |
fieldTypeMapping.put(Number.class, new FieldTypeDefinition("DECIMAL", 32).setLimits(32, -19, 19)); | |
fieldTypeMapping.put(String.class, new FieldTypeDefinition("VARCHAR", DEFAULT_VARCHAR_SIZE)); | |
fieldTypeMapping.put(Character.class, new FieldTypeDefinition("CHAR", 1)); | |
fieldTypeMapping.put(Byte[].class, new FieldTypeDefinition("BYTE", false)); | |
fieldTypeMapping.put(Character[].class, new FieldTypeDefinition("TEXT", false)); | |
fieldTypeMapping.put(byte[].class, new FieldTypeDefinition("BYTE", false)); | |
fieldTypeMapping.put(char[].class, new FieldTypeDefinition("TEXT", false)); | |
fieldTypeMapping.put(java.sql.Blob.class, new FieldTypeDefinition("BYTE", false)); | |
fieldTypeMapping.put(java.sql.Clob.class, new FieldTypeDefinition("TEXT", false)); | |
fieldTypeMapping.put(java.sql.Date.class, new FieldTypeDefinition("DATE", false)); | |
fieldTypeMapping.put(java.sql.Time.class, new FieldTypeDefinition("DATETIME HOUR TO SECOND", false)); | |
fieldTypeMapping.put(java.sql.Timestamp.class, new FieldTypeDefinition("DATETIME YEAR TO FRACTION(5)", false)); | |
return fieldTypeMapping; | |
} | |
/** | |
* INTERNAL: | |
* Build the identity query for native sequencing. | |
*/ | |
@Override | |
public ValueReadQuery buildSelectQueryForIdentity() { | |
ValueReadQuery selectQuery = new ValueReadQuery(); | |
StringWriter writer = new StringWriter(); | |
writer.write("SELECT DISTINCT(DBINFO('sqlca.sqlerrd1')) FROM systables"); | |
selectQuery.setSQLString(writer.toString()); | |
return selectQuery; | |
} | |
/** | |
* INTERNAL: | |
* returns the maximum number of characters that can be used in a field | |
* name on this platform. | |
*/ | |
@Override | |
public int getMaxFieldNameSize() { | |
return 18; | |
} | |
/** | |
* Informix seems to like this syntax instead of the OF * syntax. | |
*/ | |
@Override | |
public String getSelectForUpdateString() { | |
return " FOR UPDATE"; | |
} | |
@Override | |
public boolean isInformix() { | |
return true; | |
} | |
/** | |
* Some database require outer joins to be given in the where clause, others require it in the from clause. | |
* Informix requires it in the from clause with no ON expression. | |
*/ | |
@Override | |
public boolean isInformixOuterJoin() { | |
return true; | |
} | |
/** | |
* Informix seemed to require this at some point. | |
* Not sure if it still does. | |
*/ | |
@Override | |
public boolean shouldSelectIncludeOrderBy() { | |
return true; | |
} | |
/** | |
* Builds a table of maximum numeric values keyed on java class. This is used for type testing but | |
* might also be useful to end users attempting to sanitize values. | |
* <p><b>NOTE</b>: BigInteger & BigDecimal maximums are dependent upon their precision & Scale | |
*/ | |
@Override | |
public Hashtable maximumNumericValues() { | |
Hashtable values = new Hashtable(); | |
values.put(Integer.class, Integer.valueOf(Integer.MAX_VALUE)); | |
values.put(Long.class, Long.valueOf(Long.MAX_VALUE)); | |
values.put(Double.class, Double.valueOf(Float.MAX_VALUE)); | |
values.put(Short.class, Short.valueOf(Short.MAX_VALUE)); | |
values.put(Byte.class, Byte.valueOf(Byte.MAX_VALUE)); | |
values.put(Float.class, Float.valueOf(Float.MAX_VALUE)); | |
values.put(java.math.BigInteger.class, new java.math.BigInteger("99999999999999999999999999999999999999")); | |
values.put(java.math.BigDecimal.class, new java.math.BigDecimal("9999999999999999999.9999999999999999999")); | |
return values; | |
} | |
/** | |
* Builds a table of minimum numeric values keyed on java class. This is used for type testing but | |
* might also be useful to end users attempting to sanitize values. | |
* <p><b>NOTE</b>: BigInteger & BigDecimal minimums are dependent upon their precision & Scale | |
*/ | |
@Override | |
public Hashtable minimumNumericValues() { | |
Hashtable values = new Hashtable(); | |
values.put(Integer.class, Integer.valueOf(Integer.MIN_VALUE)); | |
values.put(Long.class, Long.valueOf(Long.MIN_VALUE)); | |
values.put(Double.class, Double.valueOf(1.4012984643247149E-44));// The double values are weird. They lose precision at E-45 | |
values.put(Short.class, Short.valueOf(Short.MIN_VALUE)); | |
values.put(Byte.class, Byte.valueOf(Byte.MIN_VALUE)); | |
values.put(Float.class, Float.valueOf(Float.MIN_VALUE)); | |
values.put(java.math.BigInteger.class, new java.math.BigInteger("-99999999999999999999999999999999999999")); | |
values.put(java.math.BigDecimal.class, new java.math.BigDecimal("-9999999999999999999.9999999999999999999")); | |
return values; | |
} | |
/** | |
* Append the receiver's field serial constraint clause to a writer. | |
*/ | |
@Override | |
public void printFieldIdentityClause(Writer writer) throws ValidationException { | |
try { | |
writer.write(" SERIAL"); | |
} catch (IOException ioException) { | |
throw ValidationException.fileError(ioException); | |
} | |
} | |
/** | |
* Used for sp calls. | |
*/ | |
@Override | |
public boolean requiresProcedureCallBrackets() { | |
return false; | |
} | |
/** | |
* Some Platforms want the constraint name after the constraint definition. | |
*/ | |
@Override | |
public boolean shouldPrintConstraintNameAfter() { | |
return true; | |
} | |
/** | |
* INTERNAL: | |
* Indicates whether the platform supports identity. | |
* Informix does this through SERIAL field types. | |
* This method is to be used *ONLY* by sequencing classes | |
*/ | |
@Override | |
public boolean supportsIdentity() { | |
return true; | |
} | |
/** | |
* INTERNAL: | |
* Indicates whether the platform supports sequence objects. | |
* This method is to be used *ONLY* by sequencing classes | |
*/ | |
@Override | |
public boolean supportsSequenceObjects() { | |
return true; | |
} | |
/** | |
* INTERNAL: | |
* Returns query used to read value generated by sequence object (like Oracle sequence). | |
* This method is called when sequence object NativeSequence is connected, | |
* the returned query used until the sequence is disconnected. | |
* If the platform supportsSequenceObjects then (at least) one of buildSelectQueryForSequenceObject | |
* methods should return non-null query. | |
*/ | |
@Override | |
public ValueReadQuery buildSelectQueryForSequenceObject(String qualifiedSeqName, Integer size) { | |
return new ValueReadQuery("select " + qualifiedSeqName + ".nextval from systables where tabid = 1"); | |
} | |
/** | |
* INTERNAL: | |
* Override this method if the platform supports sequence objects | |
* and it's possible to alter sequence object's increment in the database. | |
*/ | |
@Override | |
public boolean isAlterSequenceObjectSupported() { | |
return true; | |
} | |
} |