1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
|
/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle, IBM Corporation 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
* 12/11/2014 - Dalia Abo Sheasha
* - 454917 : Wrong SQL statement generated for Informix when GenerationType.IDENTITY strategy is used
* 02/19/2015 - Rick Curtis
* - 458877 : Add national character support
*****************************************************************************/
package org.eclipse.persistence.platform.database;
import java.io.*;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
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.*;
import org.eclipse.persistence.tools.schemaframework.FieldDefinition;
/**
* <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 {
@Override
public void initializeConnectionData(Connection connection) throws SQLException {
DatabaseMetaData dmd = connection.getMetaData();
// Wasn't able to find a driver that would support passing unicode values
this.driverSupportsNationalCharacterVarying = false;
}
/**
* 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));
if (getUseNationalCharacterVaryingTypeForString()) {
fieldTypeMapping.put(String.class, new FieldTypeDefinition("NVARCHAR", DEFAULT_VARCHAR_SIZE));
} else {
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 {@literal &} BigDecimal maximums are dependent upon their precision {@literal &} 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 {@literal &} BigDecimal minimums are dependent upon their precision {@literal &} 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 field type to a writer unless the field uses an Identity strategy to generate its value.
* In this case, the field type 'SERIAL' will be appended later.
*/
@Override
public void printFieldTypeSize(Writer writer, FieldDefinition field,
FieldTypeDefinition fieldType, boolean shouldPrintFieldIdentityClause) throws IOException {
if (!shouldPrintFieldIdentityClause)
printFieldTypeSize(writer, field, fieldType);
}
/**
* 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;
}
}
|