blob: 301862b09de3c75c6dd727773c1256bfe96fcc47 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019 IBM Corporation. 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:
// Oracle - initial API and implementation from Oracle TopLink
// Markus KARG - Added methods allowing to support stored procedure creation on SQLAnywherePlatform.
// tware - added implementation of computeMaxRowsForSQL
// Dies Koper (Fujitsu) - bug fix for printFieldUnique()
// Dies Koper (Fujitsu) - added methods to create/drop indices
// Vikram Bhatia - added method for releasing temporary LOBs after conversion
// 09/09/2011-2.3.1 Guy Pelletier
// - 356197: Add new VPD type to MultitenantType
// 02/04/2013-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support
// 04/30/2014-2.6 Lukas Jungmann
// - 380101: Invalid MySQL SQL syntax in query with LIMIT and FOR UPDATE
// 02/19/2015 - Rick Curtis
// - 458877 : Add national character support
// 02/23/2015-2.6 Dalia Abo Sheasha
// - 460607: Change DatabasePlatform StoredProcedureTerminationToken to be configurable
// 11/12/2018 - Will Dazey
// - 540929 : 'jdbc.sql-cast' property does not copy
// 12/06/2018 - Will Dazey
// - 542491: Add new 'eclipselink.jdbc.force-bind-parameters' property to force enable binding
package org.eclipse.persistence.internal.databaseaccess;
// javase imports
import java.io.ByteArrayInputStream;
import java.io.CharArrayReader;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Struct;
import java.sql.Types;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
// EclipseLink imports
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.internal.expressions.ExpressionSQLPrinter;
import org.eclipse.persistence.internal.expressions.ParameterExpression;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.helper.JavaPlatform;
import org.eclipse.persistence.internal.sequencing.Sequencing;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.DatabaseSessionImpl;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
import org.eclipse.persistence.platform.database.AccessPlatform;
import org.eclipse.persistence.platform.database.DB2Platform;
import org.eclipse.persistence.platform.database.DBasePlatform;
import org.eclipse.persistence.platform.database.OraclePlatform;
import org.eclipse.persistence.platform.database.PostgreSQLPlatform;
import org.eclipse.persistence.platform.database.SybasePlatform;
import org.eclipse.persistence.platform.database.SymfowarePlatform;
import org.eclipse.persistence.platform.database.converters.StructConverter;
import org.eclipse.persistence.platform.database.partitioning.DataPartitioningCallback;
import org.eclipse.persistence.queries.Call;
import org.eclipse.persistence.queries.DataReadQuery;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ReportQuery;
import org.eclipse.persistence.queries.SQLCall;
import org.eclipse.persistence.queries.StoredProcedureCall;
import org.eclipse.persistence.sequencing.Sequence;
import org.eclipse.persistence.sequencing.TableSequence;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.tools.schemaframework.FieldDefinition;
import org.eclipse.persistence.tools.schemaframework.TableDefinition;
/**
* DatabasePlatform is private to EclipseLink. It encapsulates behavior specific to a database platform
* (eg. Oracle, Sybase, DBase), and provides protocol for EclipseLink to access this behavior. The behavior categories
* which require platform specific handling are SQL generation and sequence behavior. While database platform
* currently provides sequence number retrieval behavior, this will move to a sequence manager (when it is
* implemented).
*
* @see AccessPlatform
* @see DB2Platform
* @see DBasePlatform
* @see OraclePlatform
* @see SybasePlatform
*
* @since TOPLink/Java 1.0
*/
public class DatabasePlatform extends DatasourcePlatform {
/** Holds a map of values used to map JAVA types to database types for table creation */
protected transient Map<Class<?>, FieldTypeDefinition> fieldTypes;
/** Indicates that native SQL should be used for literal values instead of ODBC escape format
Only used with Oracle, Sybase &amp; DB2 */
protected boolean usesNativeSQL;
/** Indicates that binding will be used for BLOB data. NOTE: does not work well with ODBC. */
protected boolean usesByteArrayBinding;
/** Batch all write statements */
protected boolean usesBatchWriting;
/** Bind all arguments to any SQL statement. */
protected boolean shouldBindAllParameters;
/** Bind all arguments to any SQL statement. */
protected boolean shouldForceBindAllParameters;
/** Cache all prepared statements, this requires full parameter binding as well. */
protected boolean shouldCacheAllStatements;
/** The statement cache size for prepare parameterized statements. */
protected int statementCacheSize;
/** Can be used if the app expects upper case but the database is not return consistent case, i.e. different databases. */
protected boolean shouldForceFieldNamesToUpperCase;
/** Indicates (if true) to remove blanks characters from the right of CHAR strings. */
protected boolean shouldTrimStrings;
/** Indicates that streams will be used to store BLOB data. NOTE: does not work with ODBC */
protected boolean usesStreamsForBinding;
/** Indicates the size above which strings will be bound NOTE: does not work with ODBC */
protected int stringBindingSize;
/** Indicates that strings will above the stringBindingSize will be bound NOTE: does not work with ODBC */
protected boolean usesStringBinding;
/** Allow for the batch size to be set as many database have strict limits. **/
protected int maxBatchWritingSize;
/** used for casting of input parameters in certain DBs **/
protected int castSizeForVarcharParameter;
/** Allow for our batch writing support to be used in JDK 1.2. **/
protected boolean usesJDBCBatchWriting;
/** bug 4241441: Allow custom batch writing to enable batching with optimistic locking. **/
protected boolean usesNativeBatchWriting;
/** Allow for a custom batch writing mechanism. **/
protected BatchWritingMechanism batchWritingMechanism;
/** Allow configuration option to use Where clause outer joining or From clause joining. **/
protected Boolean printOuterJoinInWhereClause;
/** Allow configuration option to use Where clause joining or From clause joining. **/
protected Boolean printInnerJoinInWhereClause;
/** Allow for the code that is used for preparing cursored outs for a storedprocedure to be settable. **/
protected int cursorCode;
/** The transaction isolation level to be set on the connection (optional). */
protected int transactionIsolation;
/** Some JDBC drivers do not support AutoCommit in the way EclipseLink expects. (e.g. Attunity Connect, JConnect) */
protected boolean supportsAutoCommit;
/**
* Allow for driver level data conversion optimization to be disabled,
* required because some drivers can loose precision.
*/
protected boolean shouldOptimizeDataConversion;
/** Stores mapping of class types to database types for schema creation. */
protected transient Map<String, Class<?>> classTypes;
/** Allow for case in field names to be ignored as some databases are not case sensitive and when using custom this can be an issue. */
public static boolean shouldIgnoreCaseOnFieldComparisons = false;
/** Bug#3214927 The default is 32000 for DynamicSQLBatchWritingMechanism.
* It would become 100 when switched to ParameterizedSQLBatchWritingMechanism.
*/
public static final int DEFAULT_MAX_BATCH_WRITING_SIZE = 32000;
public static final int DEFAULT_PARAMETERIZED_MAX_BATCH_WRITING_SIZE = 100;
/** Timeout used is isValid() check for dead connections. */
public static final int IS_VALID_TIMEOUT = 0;
/** This attribute will store the SQL query that will be used to 'ping' the database
* connection in order to check the health of a connection.
*/
protected String pingSQL;
/** The following two maps, provide two ways of looking up StructConverters.
* They can be looked up by java Class or by Struct type
*/
protected Map<String, StructConverter> structConverters = null;
protected Map<Class, StructConverter> typeConverters = null;
/**
* Some platforms allow a query's maxRows and FirstResult settings to be
* specified in SQL. This setting allows it to be enabled/disabled
*/
protected boolean useRownumFiltering = true;
/**
* Allow platform specific cast to be enabled.
*/
protected boolean isCastRequired = false;
/**
* Allow user to require literals to be bound.
*/
protected boolean shouldBindLiterals = true;
/**
* String used on all table creation statements generated from the DefaultTableGenerator
* with a session using this project. This value will be appended to CreationSuffix strings
* stored within the DatabaseTable creationSuffix.
*/
protected String tableCreationSuffix;
/**
* The delimiter between stored procedures in multiple stored procedure
* calls.
*/
protected String storedProcedureTerminationToken;
/**
* Used to integrate with data partitioning in an external DataSource such as UCP.
*/
protected DataPartitioningCallback partitioningCallback;
/** Allows auto-indexing for foreign keys to be set. */
protected boolean shouldCreateIndicesOnForeignKeys;
protected Boolean useJDBCStoredProcedureSyntax;
protected String driverName;
/**
* Creates an instance of default database platform.
*/
public DatabasePlatform() {
this.tableQualifier = "";
this.usesNativeSQL = false;
this.usesByteArrayBinding = true;
this.usesStringBinding = false;
this.stringBindingSize = 255;
this.shouldTrimStrings = true;
this.shouldBindAllParameters = true;
this.shouldForceBindAllParameters = false;
this.shouldCacheAllStatements = false;
this.shouldOptimizeDataConversion = true;
this.statementCacheSize = 50;
this.shouldForceFieldNamesToUpperCase = false;
this.maxBatchWritingSize = 0;
this.usesJDBCBatchWriting = true;
this.transactionIsolation = -1;
this.cursorCode = -10;
this.supportsAutoCommit = true;
this.usesNativeBatchWriting = false;
this.castSizeForVarcharParameter = 32672;
this.startDelimiter = "\"";
this.endDelimiter = "\"";
this.useJDBCStoredProcedureSyntax = null;
this.storedProcedureTerminationToken = ";";
}
/**
* Initialize operators to avoid concurrency issues.
*/
@Override
public void initialize() {
getPlatformOperators();
}
/**
* Check if has callback.
* Used to integrate with data partitioning in an external DataSource such as UCP.
*/
public boolean hasPartitioningCallback() {
return this.partitioningCallback != null;
}
/**
* Return callback.
* Used to integrate with data partitioning in an external DataSource such as UCP.
*/
public DataPartitioningCallback getPartitioningCallback() {
return partitioningCallback;
}
/**
* Set callback.
* Used to integrate with data partitioning in an external DataSource such as UCP.
*/
public void setPartitioningCallback(DataPartitioningCallback partitioningCallback) {
this.partitioningCallback = partitioningCallback;
}
/**
* Return if casting is enabled for platforms that support it.
* Allow platform specific cast to be disabled.
*/
public boolean isCastRequired() {
return isCastRequired;
}
/**
* Set if casting is enabled for platforms that support it.
* Allow platform specific cast to be disabled.
*/
public void setIsCastRequired(boolean isCastRequired) {
this.isCastRequired = isCastRequired;
}
/**
* INTERNAL:
* Get the map of StructConverters that will be used to preprocess
* STRUCT data as it is read
*/
public Map<String, StructConverter> getStructConverters() {
return this.structConverters;
}
/**
* PUBLIC:
* Get the String used on all table creation statements generated from the DefaultTableGenerator
* with a session using this project (DDL generation). This value will be appended to CreationSuffix strings
* stored on the DatabaseTable or TableDefinition.
*/
public String getTableCreationSuffix(){
return this.tableCreationSuffix;
}
/**
* INTERNAL:
* Get the map of TypeConverters
* This map indexes StructConverters by the Java Class they are meant to
* convert
*/
public Map<Class, StructConverter> getTypeConverters() {
if (typeConverters == null){
typeConverters = new HashMap<>();
}
return this.typeConverters;
}
/**
* PUBLIC:
* Add a StructConverter to this DatabasePlatform
* This StructConverter will be invoked for all writes to the database for the class returned
* by its getJavaType() method and for all reads from the database for the Structs described
* by its getStructName() method
*/
public void addStructConverter(StructConverter converter) {
if (structConverters == null){
structConverters = new HashMap<>();
}
if (typeConverters == null){
typeConverters = new HashMap<>();
}
structConverters.put(converter.getStructName(), converter);
typeConverters.put(converter.getJavaType(), converter);
}
/**
* INTERNAL: This gets called on each iteration to add parameters to the batch
* Needs to be implemented so that it returns the number of rows successfully modified
* by this statement for optimistic locking purposes (if useNativeBatchWriting is enabled, and
* the call uses optimistic locking). Is used with parameterized SQL
*
* @return - number of rows modified/deleted by this statement if it was executed (0 if it wasn't)
*/
public int addBatch(PreparedStatement statement) throws java.sql.SQLException {
statement.addBatch();
return 0;
}
/**
* Used for stored procedure definitions.
*/
public boolean allowsSizeInProcedureArguments() {
return true;
}
/**
* Appends a Boolean value as a number
*/
protected void appendBoolean(Boolean bool, Writer writer) throws IOException {
if (bool) {
writer.write("1");
} else {
writer.write("0");
}
}
/**
* Append the ByteArray in ODBC literal format ({b hexString}).
* This limits the amount of Binary data by the length of the SQL. Binding should increase this limit.
*/
protected void appendByteArray(byte[] bytes, Writer writer) throws IOException {
writer.write("{b '");
Helper.writeHexString(bytes, writer);
writer.write("'}");
}
/**
* Answer a platform correct string representation of a Date, suitable for SQL generation.
* The date is printed in the ODBC platform independent format {d 'yyyy-mm-dd'}.
*/
protected void appendDate(java.sql.Date date, Writer writer) throws IOException {
writer.write("{d '");
writer.write(Helper.printDate(date));
writer.write("'}");
}
/**
* Write number to SQL string. This is provided so that database which do not support
* Exponential format can customize their printing.
*/
protected void appendNumber(Number number, Writer writer) throws IOException {
writer.write(number.toString());
}
/**
* INTERNAL:
* In case shouldBindLiterals is true, instead of null value a DatabaseField
* value may be passed (so that it's type could be used for binding null).
*/
public void appendLiteralToCall(Call call, Writer writer, Object literal) {
if(shouldBindLiterals()) {
appendLiteralToCallWithBinding(call, writer, literal);
} else {
int nParametersToAdd = appendParameterInternal(call, writer, literal);
for (int i = 0; i < nParametersToAdd; i++) {
((DatabaseCall)call).getParameterTypes().add(DatabaseCall.LITERAL);
}
}
}
/**
* INTERNAL:
* Override this method in case the platform needs to do something special for binding literals.
* Note that instead of null value a DatabaseField
* value may be passed (so that it's type could be used for binding null).
*/
protected void appendLiteralToCallWithBinding(Call call, Writer writer, Object literal) {
((DatabaseCall)call).appendLiteral(writer, literal);
}
/**
* Write a database-friendly representation of the given parameter to the writer.
* Determine the class of the object to be written, and invoke the appropriate print method
* for that object. The default is "toString".
* The platform may decide to bind some types, such as byte arrays and large strings.
* Should only be called in case binding is not used.
*/
@Override
public void appendParameter(Call call, Writer writer, Object parameter) {
appendParameterInternal(call, writer, parameter);
}
/**
* Returns the number of parameters that used binding.
* Should only be called in case binding is not used.
*/
public int appendParameterInternal(Call call, Writer writer, Object parameter) {
int nBoundParameters = 0;
DatabaseCall databaseCall = (DatabaseCall)call;
try {
// PERF: Print Calendars directly avoiding timestamp conversion,
// Must be before conversion as you cannot bind calendars.
if (parameter instanceof Calendar) {
appendCalendar((Calendar)parameter, writer);
return nBoundParameters;
}
Object dbValue = convertToDatabaseType(parameter);
if (dbValue instanceof String) {// String and number first as they are most common.
if (usesStringBinding() && (((String)dbValue).length() >= getStringBindingSize())) {
databaseCall.bindParameter(writer, dbValue);
nBoundParameters = 1;
} else {
appendString((String)dbValue, writer);
}
} else if (dbValue instanceof Number) {
appendNumber((Number)dbValue, writer);
} else if (dbValue instanceof java.sql.Time) {
appendTime((java.sql.Time)dbValue, writer);
} else if (dbValue instanceof java.sql.Timestamp) {
appendTimestamp((java.sql.Timestamp)dbValue, writer);
} else if (dbValue instanceof java.time.LocalDate){
appendDate(java.sql.Date.valueOf((java.time.LocalDate) dbValue), writer);
} else if (dbValue instanceof java.time.LocalDateTime){
appendTimestamp(java.sql.Timestamp.valueOf((java.time.LocalDateTime) dbValue), writer);
} else if (dbValue instanceof java.time.OffsetDateTime) {
appendTimestamp(java.sql.Timestamp.from(((java.time.OffsetDateTime) dbValue).toInstant()), writer);
} else if (dbValue instanceof java.time.LocalTime){
java.time.LocalTime lt = (java.time.LocalTime) dbValue;
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), lt));
appendTimestamp(ts, writer);
} else if (dbValue instanceof java.time.OffsetTime) {
java.time.OffsetTime ot = (java.time.OffsetTime) dbValue;
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), ot.toLocalTime()));
appendTimestamp(ts, writer);
} else if (dbValue instanceof java.sql.Date) {
appendDate((java.sql.Date)dbValue, writer);
} else if (dbValue == null) {
writer.write("NULL");
} else if (dbValue instanceof Boolean) {
appendBoolean((Boolean)dbValue, writer);
} else if (dbValue instanceof byte[]) {
if (usesByteArrayBinding()) {
databaseCall.bindParameter(writer, dbValue);
nBoundParameters = 1;
} else {
appendByteArray((byte[])dbValue, writer);
}
} else if (dbValue instanceof Collection) {
nBoundParameters = printValuelist((Collection)dbValue, databaseCall, writer);
} else if (typeConverters != null && typeConverters.containsKey(dbValue.getClass())){
dbValue = new BindCallCustomParameter(dbValue);
// custom binding is required, object to be bound is wrapped (example NCHAR, NVARCHAR2, NCLOB on Oracle9)
databaseCall.bindParameter(writer, dbValue);
} else if ((parameter instanceof Struct) || (parameter instanceof Array) || (parameter instanceof Ref)) {
databaseCall.bindParameter(writer, parameter);
nBoundParameters = 1;
} else if (dbValue.getClass() == int[].class) {
nBoundParameters = printValuelist((int[])dbValue, databaseCall, writer);
} else if (dbValue instanceof AppendCallCustomParameter) {
// custom append is required (example BLOB, CLOB on Oracle8)
((AppendCallCustomParameter)dbValue).append(writer);
nBoundParameters = 1;
} else if (dbValue instanceof BindCallCustomParameter) {
// custom binding is required, object to be bound is wrapped (example NCHAR, NVARCHAR2, NCLOB on Oracle9)
databaseCall.bindParameter(writer, dbValue);
nBoundParameters = 1;
} else {
// Assume database driver primitive that knows how to print itself, this is required for drivers
// such as Oracle JDBC, Informix JDBC and others, as well as client specific classes.
writer.write(dbValue.toString());
}
} catch (IOException exception) {
throw ValidationException.fileError(exception);
}
return nBoundParameters;
}
/**
* Write the string. Quotes must be double quoted.
*/
protected void appendString(String string, Writer writer) throws IOException {
writer.write('\'');
for (int position = 0; position < string.length(); position++) {
if (string.charAt(position) == '\'') {
writer.write("''");
} else {
writer.write(string.charAt(position));
}
}
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'}.
*/
protected void appendTime(java.sql.Time time, Writer writer) throws IOException {
writer.write("{t '");
writer.write(Helper.printTime(time));
writer.write("'}");
}
/**
* Answer a platform correct string representation of a Timestamp, suitable for SQL generation.
* The timestamp is printed in the ODBC platform independent timestamp format {ts'YYYY-MM-DD HH:MM:SS.NNNNNNNNN'}.
*/
protected void appendTimestamp(java.sql.Timestamp timestamp, Writer writer) throws IOException {
writer.write("{ts '");
writer.write(Helper.printTimestamp(timestamp));
writer.write("'}");
}
/**
* Answer a platform correct string representation of a Calendar as a Timestamp, suitable for SQL generation.
* The calendar is printed in the ODBC platform independent timestamp format {ts'YYYY-MM-DD HH:MM:SS.NNNNNNNNN'}.
*/
protected void appendCalendar(Calendar calendar, Writer writer) throws IOException {
writer.write("{ts '");
writer.write(Helper.printCalendar(calendar));
writer.write("'}");
}
/**
* Used by JDBC drivers that do not support autocommit so simulate an autocommit.
*/
public void autoCommit(DatabaseAccessor accessor) throws SQLException {
if (!supportsAutoCommit()) {
accessor.getConnection().commit();
}
}
/**
* Used for jdbc drivers which do not support autocommit to explicitly begin a transaction
* This method is a no-op for databases which implement autocommit as expected.
*/
public void beginTransaction(DatabaseAccessor accessor) throws SQLException {
if (!supportsAutoCommit()) {
Statement statement = accessor.getConnection().createStatement();
try {
statement.executeUpdate("BEGIN TRANSACTION");
} finally {
statement.close();
}
}
}
/**
* INTERNAL:
* Return the selection criteria used to IN batch fetching.
*/
public Expression buildBatchCriteria(ExpressionBuilder builder,Expression field) {
return field.in(
builder.getParameter(ForeignReferenceMapping.QUERY_BATCH_PARAMETER));
}
/**
* INTERNAL:
* Return the selection criteria used to IN batch fetching.
*/
public Expression buildBatchCriteriaForComplexId(ExpressionBuilder builder,List<Expression> fields) {
return builder.value(fields).in(
builder.getParameter(ForeignReferenceMapping.QUERY_BATCH_PARAMETER));
}
/**
* INTERNAL
* Returns null unless the platform supports call with returning
*/
public DatabaseCall buildCallWithReturning(SQLCall sqlCall, Vector returnFields) {
throw ValidationException.platformDoesNotSupportCallWithReturning(Helper.getShortClassName(this));
}
/**
* Return the mapping of class types to database types for the schema framework.
*/
protected Map<String, Class<?>> buildClassTypes() {
Map<String, Class<?>> classTypeMapping = new HashMap<>();
// Key the Map the other way for table creation.
classTypeMapping.put("NUMBER", java.math.BigInteger.class);
classTypeMapping.put("DECIMAL", java.math.BigDecimal.class);
classTypeMapping.put("INTEGER", Integer.class);
classTypeMapping.put("INT", Integer.class);
classTypeMapping.put("NUMERIC", java.math.BigInteger.class);
classTypeMapping.put("FLOAT(16)", Float.class);
classTypeMapping.put("FLOAT(32)", Double.class);
classTypeMapping.put("NUMBER(1) default 0", Boolean.class);
classTypeMapping.put("SHORT", Short.class);
classTypeMapping.put("BYTE", Byte.class);
classTypeMapping.put("DOUBLE", Double.class);
classTypeMapping.put("FLOAT", Float.class);
classTypeMapping.put("SMALLINT", Short.class);
classTypeMapping.put("BIT", Boolean.class);
classTypeMapping.put("SMALLINT DEFAULT 0", Boolean.class);
classTypeMapping.put("VARCHAR", String.class);
classTypeMapping.put("CHAR", Character.class);
classTypeMapping.put("LONGVARBINARY", Byte[].class);
classTypeMapping.put("TEXT", Character[].class);
classTypeMapping.put("LONGTEXT", Character[].class);
// classTypeMapping.put("BINARY", Byte[].class);
classTypeMapping.put("MEMO", Character[].class);
classTypeMapping.put("VARCHAR2", String.class);
classTypeMapping.put("LONG RAW", Byte[].class);
classTypeMapping.put("LONG", Character[].class);
classTypeMapping.put("DATE", java.sql.Date.class);
classTypeMapping.put("TIMESTAMP", java.sql.Timestamp.class);
classTypeMapping.put("TIME", java.sql.Time.class);
classTypeMapping.put("DATETIME", java.sql.Timestamp.class);
classTypeMapping.put("BIGINT", java.math.BigInteger.class);
classTypeMapping.put("DOUBLE PRECIS", Double.class);
classTypeMapping.put("IMAGE", Byte[].class);
classTypeMapping.put("LONGVARCHAR", Character[].class);
classTypeMapping.put("REAL", Float.class);
classTypeMapping.put("TINYINT", Short.class);
// classTypeMapping.put("VARBINARY", Byte[].class);
classTypeMapping.put("BLOB", Byte[].class);
classTypeMapping.put("CLOB", Character[].class);
return classTypeMapping;
}
/**
* Return the mapping of class types to database types for the schema framework.
*/
protected Hashtable<Class<?>, FieldTypeDefinition> buildFieldTypes() {
Hashtable<Class<?>, FieldTypeDefinition> fieldTypeMapping = new Hashtable<>();
fieldTypeMapping.put(Boolean.class, new FieldTypeDefinition("NUMBER", 1));
fieldTypeMapping.put(Integer.class, new FieldTypeDefinition("NUMBER", 10));
fieldTypeMapping.put(Long.class, new FieldTypeDefinition("NUMBER", 19));
fieldTypeMapping.put(Float.class, new FieldTypeDefinition("NUMBER", 12, 5).setLimits(19, 0, 19));
fieldTypeMapping.put(Double.class, new FieldTypeDefinition("NUMBER", 10, 5).setLimits(19, 0, 19));
fieldTypeMapping.put(Short.class, new FieldTypeDefinition("NUMBER", 5));
fieldTypeMapping.put(Byte.class, new FieldTypeDefinition("NUMBER", 3));
fieldTypeMapping.put(java.math.BigInteger.class, new FieldTypeDefinition("NUMBER", 19));
fieldTypeMapping.put(java.math.BigDecimal.class, new FieldTypeDefinition("NUMBER", 19, 0).setLimits(19, 0, 19));
fieldTypeMapping.put(String.class, new FieldTypeDefinition("VARCHAR"));
fieldTypeMapping.put(Character.class, new FieldTypeDefinition("CHAR"));
fieldTypeMapping.put(Byte[].class, new FieldTypeDefinition("BLOB"));
fieldTypeMapping.put(Character[].class, new FieldTypeDefinition("CLOB"));
fieldTypeMapping.put(byte[].class, new FieldTypeDefinition("BLOB"));
fieldTypeMapping.put(char[].class, new FieldTypeDefinition("CLOB"));
fieldTypeMapping.put(java.sql.Blob.class, new FieldTypeDefinition("BLOB"));
fieldTypeMapping.put(java.sql.Clob.class, new FieldTypeDefinition("CLOB"));
fieldTypeMapping.put(java.sql.Date.class, new FieldTypeDefinition("DATE"));
fieldTypeMapping.put(java.sql.Timestamp.class, new FieldTypeDefinition("TIMESTAMP"));
fieldTypeMapping.put(java.sql.Time.class, new FieldTypeDefinition("TIME"));
//bug 5871089 the default generator requires definitions based on all java types
fieldTypeMapping.put(java.util.Calendar.class, new FieldTypeDefinition("TIMESTAMP"));
fieldTypeMapping.put(java.util.Date.class, new FieldTypeDefinition("TIMESTAMP"));
fieldTypeMapping.put(java.lang.Number.class, new FieldTypeDefinition("NUMBER", 10));
fieldTypeMapping.put(java.time.LocalDate.class, new FieldTypeDefinition("DATE"));
fieldTypeMapping.put(java.time.LocalDateTime.class, new FieldTypeDefinition("TIMESTAMP"));
fieldTypeMapping.put(java.time.LocalTime.class, new FieldTypeDefinition("TIME"));
fieldTypeMapping.put(java.time.OffsetDateTime.class, new FieldTypeDefinition("TIMESTAMP"));
fieldTypeMapping.put(java.time.OffsetTime.class, new FieldTypeDefinition("TIME"));
return fieldTypeMapping;
}
/**
* Returns true iff:
* <ul>
* <li>tThe current driver supports calling get/setNString</li>
* <li> Strings are globally mapped to a national character varying type (useNationalCharacterVarying()).</li>
* </ul>
*/
public boolean shouldUseGetSetNString() {
return getDriverSupportsNVarChar() && getUseNationalCharacterVaryingTypeForString();
}
/**
* True if the current jdbc driver supports get/setNString methods
*/
protected boolean driverSupportsNationalCharacterVarying = false;
/**
* If true, the platform should map String columns to a type that supports
* national characters.
*/
protected boolean useNationalCharacterVarying = false;
public boolean getDriverSupportsNVarChar() {
return driverSupportsNationalCharacterVarying;
}
public void setDriverSupportsNVarChar(boolean b) {
driverSupportsNationalCharacterVarying = b;
}
public boolean getUseNationalCharacterVaryingTypeForString() {
return useNationalCharacterVarying;
}
public void setUseNationalCharacterVaryingTypeForString(boolean b) {
useNationalCharacterVarying = b;
}
/**
* Return the proc syntax for this platform.
*/
public String buildProcedureCallString(StoredProcedureCall call, AbstractSession session, AbstractRecord row) {
StringWriter writer = new StringWriter();
writer.write(call.getCallHeader(this));
writer.write(call.getProcedureName());
if (requiresProcedureCallBrackets()) {
writer.write("(");
} else {
writer.write(" ");
}
int indexFirst = call.getFirstParameterIndexForCallString();
int size = call.getParameters().size();
for (int index = indexFirst; index < size; index++) {
String name = call.getProcedureArgumentNames().get(index);
Object parameter = call.getParameters().get(index);
Integer parameterType = call.getParameterTypes().get(index);
// If the argument is optional and null, ignore it.
if (!call.hasOptionalArguments() || !call.getOptionalArguments().contains(parameter) || (row.get(parameter) != null)) {
writer.write(getProcedureArgument(name, parameter, parameterType, call, session));
if (DatasourceCall.isOutputParameterType(parameterType)) {
if (requiresProcedureCallOuputToken()) {
writer.write(" ");
writer.write(getOutputProcedureToken());
}
}
if ((index + 1) < call.getParameters().size()) {
writer.write(", ");
}
}
}
if (requiresProcedureCallBrackets()) {
writer.write(")");
}
writer.write(getProcedureCallTail());
return writer.toString();
}
/**
* INTERNAL
* Indicates whether the platform can build call with returning.
* In case this method returns true, buildCallWithReturning method
* may be called.
*/
public boolean canBuildCallWithReturning() {
return false;
}
/**
* INTERNAL:
* Supports Batch Writing with Optimistic Locking.
*/
public boolean canBatchWriteWithOptimisticLocking(DatabaseCall call) {
if (this.batchWritingMechanism != null) {
// Assume a custom batch mechanism can return a valid row count.
return true;
}
// The JDBC spec supports this, so assume it is implemented correctly by default.
return true;
}
/**
* INTERNAL:
* Use the JDBC maxResults and firstResultIndex setting to compute a value to use when
* limiting the results of a query in SQL. These limits tend to be used in two ways.
*
* 1. MaxRows is the index of the last row to be returned (like JDBC maxResults)
* 2. MaxRows is the number of rows to be returned
*
* By default, we assume case 1 and simply return the value of maxResults. Subclasses
* may provide an override
*
* @see org.eclipse.persistence.platform.database.MySQLPlatform
*/
public int computeMaxRowsForSQL(int firstResultIndex, int maxResults){
return maxResults;
}
/**
* Used for jdbc drivers which do not support autocommit to explicitly commit a transaction
* This method is a no-op for databases which implement autocommit as expected.
*/
public void commitTransaction(DatabaseAccessor accessor) throws SQLException {
if (!supportsAutoCommit()) {
accessor.getConnection().commit();
}
}
/**
* Any platform that supports VPD should implement this method.
*/
public DatabaseQuery getVPDClearIdentifierQuery(String vpdIdentifier) {
return null;
}
/**
* Any platform that supports VPD should implement this method. Used for DDL
* generation.
*/
public String getVPDCreationFunctionString(String tableName, String tenantFieldName) {
return null;
}
/**
* Any platform that supports VPD should implement this method. Used for DDL
* generation.
*/
public String getVPDCreationPolicyString(String tableName, AbstractSession session) {
return null;
}
/**
* Any platform that supports VPD should implement this method. Used for DDL
* generation.
*/
public String getVPDDeletionString(String tableName, AbstractSession session) {
return null;
}
/**
* Any platform that supports VPD should implement this method.
*/
public DatabaseQuery getVPDSetIdentifierQuery(String vpdIdentifier) {
return null;
}
/**
* INTERNAL
* We support more primitive than JDBC does so we must do conversion before printing or binding.
*/
public Object convertToDatabaseType(Object value) {
if (value == null) {
return null;
}
if (value.getClass() == ClassConstants.UTILDATE) {
return Helper.timestampFromDate((java.util.Date)value);
} else if (value instanceof Character) {
return ((Character)value).toString();
} else if (value instanceof Calendar) {
return Helper.timestampFromDate(((Calendar)value).getTime());
} else if (value instanceof BigInteger) {
return new BigDecimal((BigInteger)value);
} else if (value instanceof char[]) {
return new String((char[])value);
} else if (value instanceof Character[]) {
return convertObject(value, ClassConstants.STRING);
} else if (value instanceof Byte[]) {
return convertObject(value, ClassConstants.APBYTE);
}
return value;
}
/**
* Copy the state into the new platform.
*/
@Override
public void copyInto(Platform platform) {
super.copyInto(platform);
if (!(platform instanceof DatabasePlatform)) {
return;
}
DatabasePlatform databasePlatform = (DatabasePlatform)platform;
databasePlatform.setShouldTrimStrings(shouldTrimStrings());
databasePlatform.setUsesNativeSQL(usesNativeSQL());
databasePlatform.setUsesByteArrayBinding(usesByteArrayBinding());
databasePlatform.setUsesStringBinding(usesStringBinding());
databasePlatform.setShouldBindAllParameters(shouldBindAllParameters());
databasePlatform.setShouldForceBindAllParameters(shouldForceBindAllParameters());
databasePlatform.setShouldCacheAllStatements(shouldCacheAllStatements());
databasePlatform.setStatementCacheSize(getStatementCacheSize());
databasePlatform.setTransactionIsolation(getTransactionIsolation());
databasePlatform.setBatchWritingMechanism(getBatchWritingMechanism());
databasePlatform.setMaxBatchWritingSize(getMaxBatchWritingSize());
databasePlatform.setShouldForceFieldNamesToUpperCase(shouldForceFieldNamesToUpperCase());
databasePlatform.setShouldOptimizeDataConversion(shouldOptimizeDataConversion());
databasePlatform.setStringBindingSize(getStringBindingSize());
databasePlatform.setUsesBatchWriting(usesBatchWriting());
databasePlatform.setUsesJDBCBatchWriting(usesJDBCBatchWriting());
databasePlatform.setUsesNativeBatchWriting(usesNativeBatchWriting());
databasePlatform.setUsesStreamsForBinding(usesStreamsForBinding());
databasePlatform.shouldCreateIndicesOnForeignKeys = this.shouldCreateIndicesOnForeignKeys;
databasePlatform.printOuterJoinInWhereClause = this.printOuterJoinInWhereClause;
databasePlatform.printInnerJoinInWhereClause = this.printInnerJoinInWhereClause;
//use the variable directly to avoid custom platform strings - only want to copy user set values.
//specifically used for login platform detection
databasePlatform.setTableCreationSuffix(this.tableCreationSuffix);
databasePlatform.setIsCastRequired(isCastRequired());
}
/**
* Used for batch writing and sp defs.
*/
public String getBatchBeginString() {
return "";
}
/**
* Return if the platform does not maintain the row count on batch executes
* and requires an output parameter to maintain the row count.
*/
public boolean isRowCountOutputParameterRequired() {
return false;
}
/**
* Used for batch writing for row count return.
*/
public String getBatchRowCountDeclareString() {
return "";
}
/**
* Used for batch writing for row count return.
*/
public String getBatchRowCountAssignString() {
return "";
}
/**
* Used for batch writing for row count return.
*/
public String getBatchRowCountReturnString() {
return "";
}
/**
* Used for batch writing and sp defs.
*/
public String getBatchDelimiterString() {
return "; ";
}
/**
* Used for batch writing and sp defs.
*/
public String getBatchEndString() {
return "";
}
/**
* INTERNAL:
* This method is used to unwrap the oracle connection wrapped by
* the application server. EclipseLink needs this unwrapped connection for certain
* Oracle Specific support. (ie TIMESTAMPTZ)
* This is added as a workaround for bug 4565190
*/
public Connection getConnection(AbstractSession session, Connection connection) {
return connection;
}
/**
* Used for constraint deletion.
*/
public String getConstraintDeletionString() {
return " DROP CONSTRAINT ";
}
/**
* Used for constraint deletion.
*/
public String getUniqueConstraintDeletionString() {
return getConstraintDeletionString();
}
/**
/**
* Used for view creation.
*/
public String getCreateViewString() {
return "CREATE VIEW ";
}
/**
* Allows DROP TABLE to cascade dropping of any dependent constraints if the database supports this option.
*/
public String getDropCascadeString() {
return "";
}
/**
* This method determines if any special processing needs to occur prior to writing a field.
*
* It does things such as determining if a field must be bound and flagging the parameter as one
* that must be bound.
*/
@Override
public Object getCustomModifyValueForCall(Call call, Object value, DatabaseField field, boolean shouldBind) {
if (typeConverters != null){
StructConverter converter = typeConverters.get(field.getType());
if (converter != null) {
Object bindValue = value;
if (bindValue == null) {
bindValue = new ObjectRelationalDatabaseField(field);
((ObjectRelationalDatabaseField)bindValue).setSqlType(java.sql.Types.STRUCT);
((ObjectRelationalDatabaseField)bindValue).setSqlTypeName(converter.getStructName());
}
return new BindCallCustomParameter(bindValue);
}
}
return super.getCustomModifyValueForCall(call, value, field, shouldBind);
}
/**
* Used for stored procedure defs.
*/
public String getProcedureEndString() {
return getBatchEndString();
}
/**
* Used for stored procedure defs.
*/
public String getProcedureBeginString() {
return getBatchBeginString();
}
/**
* Used for stored procedure defs.
*/
public String getProcedureAsString() {
return " AS";
}
/**
* Some platforms have an option list
* Only to be used for stored procedure creation.
*
* @see org.eclipse.persistence.tools.schemaframework.StoredProcedureDefinition
*/
public String getProcedureOptionList() {
return "";
}
/**
* Return the class type to database type mapping for the schema framework.
*/
public Map<String, Class<?>> getClassTypes() {
if (classTypes == null) {
classTypes = buildClassTypes();
}
return classTypes;
}
/**
* Used for stored function calls.
*/
public String getAssignmentString() {
return "= ";
}
/**
* ADVANCED:
* Get the maximum length allowed by the database for a Varchar Parameter
* This is used by subclasses when writing SQL for parameters
* @see DB2Platform
*/
public int getCastSizeForVarcharParameter(){
return castSizeForVarcharParameter;
}
/**
* This method is used to print the required output parameter token for the
* specific platform. Used when stored procedures are created.
*/
public String getCreationInOutputProcedureToken() {
return getInOutputProcedureToken();
}
/**
* This method is used to print the required output parameter token for the
* specific platform. Used when stored procedures are created.
*/
public String getCreationOutputProcedureToken() {
return getOutputProcedureToken();
}
/**
* ADVANCED:
* Return the code for preparing cursored output
* parameters in a stored procedure
*/
public int getCursorCode() {
return cursorCode;
}
/**
* Returns the table name used by TableSequence by default.
*/
public String getDefaultSequenceTableName() {
return "SEQUENCE";
}
/**
* Return the create schema SQL syntax. Subclasses should override as needed.
*/
public String getCreateDatabaseSchemaString(String schema) {
return "CREATE SCHEMA " + schema;
}
/**
* Return the drop schema SQL syntax. Subclasses should override as needed.
*/
public String getDropDatabaseSchemaString(String schema) {
return "DROP SCHEMA " + schema;
}
/**
* Return the field type object describing this databases platform specific representation
* of the Java primitive class name.
*/
public FieldTypeDefinition getFieldTypeDefinition(Class<?> javaClass) {
return getFieldTypes().get(javaClass);
}
/**
* Return the class type to database type mappings for the schema framework.
*/
public Map<Class<?>, FieldTypeDefinition> getFieldTypes() {
if (this.fieldTypes == null) {
this.fieldTypes = buildFieldTypes();
}
return this.fieldTypes;
}
/**
* Used for stored function calls.
*/
public String getFunctionCallHeader() {
return getProcedureCallHeader() + "? " + getAssignmentString();
}
/**
* This method is used to print the output parameter token when stored
* procedures are called
*/
public String getInOutputProcedureToken() {
return "IN OUT";
}
/**
* Returns the JDBC outer join operator for SELECT statements.
*/
public String getJDBCOuterJoinString() {
return "{oj ";
}
/**
* Return the JDBC type for the given database field to be passed to Statement.setNull
*/
public int getJDBCTypeForSetNull(DatabaseField field) {
return getJDBCType(field);
}
/**
* Return the JDBC type for the given database field.
*/
public int getJDBCType(DatabaseField field) {
if (field != null) {
// If the field has a specified JDBC type, use it,
// otherwise compute the type from the Java class type.
if (field.getSqlType() != DatabaseField.NULL_SQL_TYPE) {
return field.getSqlType();
} else {
return getJDBCType(ConversionManager.getObjectClass(field.getType()));
}
} else {
return getJDBCType((Class)null);
}
}
/**
* Return the JDBC type for the Java type.
*/
public int getJDBCType(Class javaType) {
if (javaType == null) {
return Types.VARCHAR;// Best guess, sometimes we cannot determine type from mapping, this may fail on some drivers, other dont care what type it is.
} else if (javaType == ClassConstants.STRING) {
return Types.VARCHAR;
} else if (javaType == ClassConstants.BIGDECIMAL) {
return Types.DECIMAL;
} else if (javaType == ClassConstants.BIGINTEGER) {
return Types.BIGINT;
} else if (javaType == ClassConstants.BOOLEAN) {
return Types.BIT;
} else if (javaType == ClassConstants.BYTE) {
return Types.TINYINT;
} else if (javaType == ClassConstants.CHAR) {
return Types.CHAR;
} else if (javaType == ClassConstants.DOUBLE) {
return Types.DOUBLE;
} else if (javaType == ClassConstants.FLOAT) {
return Types.FLOAT;
} else if (javaType == ClassConstants.INTEGER) {
return Types.INTEGER;
} else if (javaType == ClassConstants.LONG) {
return Types.BIGINT;
} else if (javaType == ClassConstants.NUMBER) {
return Types.DECIMAL;
} else if (javaType == ClassConstants.SHORT ) {
return Types.SMALLINT;
} else if (javaType == ClassConstants.CALENDAR ) {
return Types.TIMESTAMP;
} else if (javaType == ClassConstants.UTILDATE ) {
return Types.TIMESTAMP;
} else if (javaType == ClassConstants.TIME ||
javaType == ClassConstants.TIME_LTIME) { //bug 546312
return Types.TIME;
} else if (javaType == ClassConstants.SQLDATE ||
javaType == ClassConstants.TIME_LDATE) { //bug 546312
return Types.DATE;
} else if (javaType == ClassConstants.TIMESTAMP ||
javaType == ClassConstants.UTILDATE || //bug 5237080, return TIMESTAMP for java.util.Date as well
javaType == ClassConstants.TIME_LDATETIME) { //bug 546312
return Types.TIMESTAMP;
} else if(javaType == ClassConstants.TIME_OTIME) { //bug 546312
return Types.TIME_WITH_TIMEZONE;
} else if(javaType == ClassConstants.TIME_ODATETIME) { //bug 546312
return Types.TIMESTAMP_WITH_TIMEZONE;
}else if (javaType == ClassConstants.ABYTE) {
return Types.LONGVARBINARY;
} else if (javaType == ClassConstants.APBYTE) {
return Types.LONGVARBINARY;
} else if (javaType == ClassConstants.BLOB) {
return Types.BLOB;
} else if (javaType == ClassConstants.ACHAR) {
return Types.LONGVARCHAR;
} else if (javaType == ClassConstants.APCHAR) {
return Types.LONGVARCHAR;
} else if (javaType == ClassConstants.CLOB) {
return Types.CLOB;
} else {
return Types.VARCHAR;// Best guess, sometimes we cannot determine type from mapping, this may fail on some drivers, other dont care what type it is.
}
}
/**
* INTERNAL:
* Returns the type name corresponding to the jdbc type
*/
public String getJdbcTypeName(int jdbcType) {
return null;
}
/**
* INTERNAL:
* Returns the minimum time increment supported by the platform.
*/
public long minimumTimeIncrement() {
return 1;
}
/**
* PUBLIC:
* Allow for the max batch writing size to be set.
* This allows for the batch size to be limited as most database have strict limits.
* The size is in characters, the default is 32000 but the real value depends on the database configuration.
*/
public int getMaxBatchWritingSize() {
return maxBatchWritingSize;
}
/**
* INTERNAL:
* returns the maximum number of characters that can be used in a field
* name on this platform.
*/
public int getMaxFieldNameSize() {
return 50;
}
/**
* INTERNAL:
* returns the maximum number of characters that can be used in a foreign key
* name on this platform.
*/
public int getMaxForeignKeyNameSize() {
return getMaxFieldNameSize();
}
/**
* INTERNAL:
* returns the maximum number of characters that can be used in an index
* name on this platform.
*/
public int getMaxIndexNameSize() {
return getMaxFieldNameSize();
}
/**
* INTERNAL:
* returns the maximum number of characters that can be used in a unique key
* name on this platform.
*/
public int getMaxUniqueKeyNameSize() {
return getMaxFieldNameSize();
}
/**
* INTERNAL:
* Get the object from the JDBC Result set. Added to allow other platforms to
* override.
* @see "org.eclipse.persistence.platform.database.oracle.Oracle9Plaform"
*/
public Object getObjectFromResultSet(ResultSet resultSet, int columnNumber, int type, AbstractSession session) throws java.sql.SQLException {
Object objectFromResultSet = resultSet.getObject(columnNumber);
if (objectFromResultSet != null){
if(structConverters != null && type == Types.STRUCT){
String structType = ((Struct)objectFromResultSet).getSQLTypeName();
if (getStructConverters().containsKey(structType)) {
return getStructConverters().get(structType).convertToObject((Struct)objectFromResultSet);
}
} else if(type == Types.SQLXML) {
return JavaPlatform.getStringAndFreeSQLXML(objectFromResultSet);
}
}
return objectFromResultSet;
}
/**
* Used for stored procedure creation: Prefix for INPUT parameters.
* Not required on most platforms.
*/
public String getInputProcedureToken() {
return "";
}
/**
* Used to allow platforms to define their own index prefixes
*/
public String getIndexNamePrefix(boolean isUniqueSetOnField){
return "IX_";
}
/**
* This method is used to print the output parameter token when stored
* procedures are called
*/
public String getOutputProcedureToken() {
return "OUT";
}
/**
* Used for determining if an SQL exception was communication based. This SQL should be
* as efficient as possible and ensure a round trip to the database.
*/
public String getPingSQL(){
return pingSQL;
}
/**
* Used for sp defs.
*/
public String getProcedureArgumentString() {
return "";
}
/**
* Obtain the platform specific argument string
*/
public String getProcedureArgument(String name, Object parameter, Integer parameterType, StoredProcedureCall call, AbstractSession session) {
if (name != null && shouldPrintStoredProcedureArgumentNameInCall()) {
return getProcedureArgumentString() + name + " = " + "?";
}
return "?";
}
/**
* Used for sp calls.
*/
public String getProcedureCallHeader() {
return "EXECUTE PROCEDURE ";
}
/**
* Used for sp calls.
*/
public String getProcedureCallTail() {
return "";
}
public String getQualifiedSequenceTableName() {
if (getDefaultSequence() instanceof TableSequence) {
return getQualifiedName(((TableSequence)getDefaultSequence()).getTableName());
} else {
throw ValidationException.wrongSequenceType(Helper.getShortClassName(getDefaultSequence()), "getTableName");
}
}
public String getQualifiedName(String name) {
if (getTableQualifier().equals("")) {
return name;
} else {
return getTableQualifier() + "." + name;
}
}
/**
* This syntax does no wait on the lock.
* (i.e. In Oracle adding NOWAIT to the end will accomplish this)
*/
public String getNoWaitString() {
return " NOWAIT";
}
/**
* This syntax does no wait on the lock.
* (i.e. In Oracle adding FOR UPDATE NOWAIT to the end will accomplish this)
*/
public String getSelectForUpdateNoWaitString() {
return getSelectForUpdateString() + getNoWaitString();
}
/**
* For fine-grained pessimistic locking the column names can be
* specified individually.
*/
public String getSelectForUpdateOfString() {
return " FOR UPDATE OF ";
}
/**
* Most database support a syntax. although don't actually lock the row.
* Some require the OF some don't like it.
*/
public String getSelectForUpdateString() {
return " FOR UPDATE";
}
/**
* Platforms that support the WAIT option should override this method.
* By default the wait timeout is ignored.
*
* @see DatabasePlatform#supportsWaitForUpdate()
*/
public String getSelectForUpdateWaitString(Integer waitTimeout) {
return getSelectForUpdateString();
}
public String getSequenceCounterFieldName() {
if (getDefaultSequence() instanceof TableSequence) {
return ((TableSequence)getDefaultSequence()).getCounterFieldName();
} else {
throw ValidationException.wrongSequenceType(Helper.getShortClassName(getDefaultSequence()), "getCounterFieldName");
}
}
public String getSequenceNameFieldName() {
if (getDefaultSequence() instanceof TableSequence) {
return ((TableSequence)getDefaultSequence()).getNameFieldName();
} else {
throw ValidationException.wrongSequenceType(Helper.getShortClassName(getDefaultSequence()), "getNameFieldName");
}
}
@Override
public int getSequencePreallocationSize() {
return getDefaultSequence().getPreallocationSize();
}
public String getSequenceTableName() {
if (getDefaultSequence() instanceof TableSequence) {
String tableName = ((TableSequence)getDefaultSequence()).getTableName();
if(tableName.length() == 0) {
tableName = this.getDefaultSequenceTableName();
}
return tableName;
} else {
throw ValidationException.wrongSequenceType(Helper.getShortClassName(getDefaultSequence()), "getTableName");
}
}
/**
* The statement cache size for prepare parameterized statements.
*/
public int getStatementCacheSize() {
return statementCacheSize;
}
public String getStoredProcedureParameterPrefix() {
return "";
}
/**
* Returns the delimiter between stored procedures in multiple stored
* procedure calls.
*/
public String getStoredProcedureTerminationToken() {
return storedProcedureTerminationToken;
}
public void setStoredProcedureTerminationToken(String storedProcedureTerminationToken) {
this.storedProcedureTerminationToken = storedProcedureTerminationToken;
}
public int getStringBindingSize() {
return stringBindingSize;
}
/**
* Returns the transaction isolation setting for a connection.
* Return -1 if it has not been set.
*/
public int getTransactionIsolation() {
return transactionIsolation;
}
/**
* 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.
*/
public boolean isInformixOuterJoin() {
return false;
}
/**
* Returns true if this platform complies with the expected behavior from
* a jdbc execute call. Most platforms do, some have issues:
*
* @see PostgreSQLPlatform
*/
public boolean isJDBCExecuteCompliant() {
return true;
}
/**
* Return true is the given exception occurred as a result of a lock
* time out exception (WAIT clause). If sub-platform supports this clause,
* this method should be necessary checks should be made.
*
* By default though, this method return false.
*
* @see OraclePlatform
*/
public boolean isLockTimeoutException(DatabaseException e) {
return false;
}
/**
* INTERNAL:
* Indicates whether SELECT DISTINCT ... FOR UPDATE is allowed by the platform (Oracle doesn't allow this).
*/
public boolean isForUpdateCompatibleWithDistinct() {
return true;
}
/**
* INTERNAL:
* Indicates whether SELECT DISTINCT lob FROM ... (where lob is BLOB or CLOB) is allowed by the platform (Oracle doesn't allow this).
*/
public boolean isLobCompatibleWithDistinct() {
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 &amp; BigDecimal maximums are dependent upon their precision &amp; Scale
*/
public Hashtable<Class<? extends Number>, ? super Number> maximumNumericValues() {
Hashtable<Class<? extends Number>, ? super Number> values = new Hashtable<>();
values.put(Integer.class, Integer.MAX_VALUE);
values.put(Long.class, Long.MAX_VALUE);
values.put(Double.class, Double.MAX_VALUE);
values.put(Short.class, Short.MAX_VALUE);
values.put(Byte.class, Byte.MAX_VALUE);
values.put(Float.class, Float.MAX_VALUE);
values.put(java.math.BigInteger.class, new java.math.BigInteger("999999999999999999999999999999999999999"));
values.put(java.math.BigDecimal.class, new java.math.BigDecimal("99999999999999999999.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 &amp; BigDecimal minimums are dependent upon their precision &amp; Scale
*/
public Hashtable<Class<? extends Number>, ? super Number> minimumNumericValues() {
Hashtable<Class<? extends Number>, ? super Number> values = new Hashtable<>();
values.put(Integer.class, Integer.MIN_VALUE);
values.put(Long.class, Long.MIN_VALUE);
values.put(Double.class, Double.MIN_VALUE);
values.put(Short.class, Short.MIN_VALUE);
values.put(Byte.class, Byte.MIN_VALUE);
values.put(Float.class, 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;
}
/**
* Internal: Allows setting the batch size on the statement
* Is used with parameterized SQL, and should only be passed in prepared statements
*
* @return statement to be used for batch writing
*/
public Statement prepareBatchStatement(Statement statement, int maxBatchWritingSize) throws java.sql.SQLException {
return statement;
}
/**
* Append the receiver's field 'identity' constraint clause to a writer.
*/
public void printFieldIdentityClause(Writer writer) throws ValidationException {
//The default is to do nothing.
}
/**
* Append the receiver's field 'NOT NULL' constraint clause to a writer.
*/
public void printFieldNotNullClause(Writer writer) throws ValidationException {
try {
writer.write(" NOT NULL");
} catch (IOException ioException) {
throw ValidationException.fileError(ioException);
}
}
/**
* Append the receiver's field 'NULL' constraint clause to a writer.
*/
public void printFieldNullClause(Writer writer) throws ValidationException {
// The default is to do nothing
}
/**
* Print the int array on the writer. Added to handle int[] passed as parameters to named queries
* Returns the number of objects using binding.
*/
public int printValuelist(int[] theObjects, DatabaseCall call, Writer writer) throws IOException {
int nBoundParameters = 0;
writer.write("(");
for (int i = 0; i < theObjects.length; i++) {
nBoundParameters = nBoundParameters + appendParameterInternal(call, writer, theObjects[i]);
if (i < (theObjects.length - 1)) {
writer.write(", ");
}
}
writer.write(")");
return nBoundParameters;
}
public int printValuelist(Collection theObjects, DatabaseCall call, Writer writer) throws IOException {
int nBoundParameters = 0;
writer.write("(");
Iterator iterator = theObjects.iterator();
while (iterator.hasNext()) {
nBoundParameters = nBoundParameters + appendParameterInternal(call, writer, iterator.next());
if (iterator.hasNext()) {
writer.write(", ");
}
}
writer.write(")");
return nBoundParameters;
}
/**
* This method is used to register output parameter on CallableStatements for Stored Procedures
* as each database seems to have a different method.
*
* @see java.sql.CallableStatement#registerOutParameter(int parameterIndex, int sqlType)
*/
public void registerOutputParameter(CallableStatement statement, int parameterIndex, int sqlType) throws SQLException {
statement.registerOutParameter(parameterIndex, sqlType);
}
/**
* This method is used to register output parameter on CallableStatements for Stored Procedures
* as each database seems to have a different method.
*
* @see java.sql.CallableStatement#registerOutParameter(int parameterIndex, int sqlType, String typeName)
*/
public void registerOutputParameter(CallableStatement statement, int parameterIndex, int sqlType, String typeName) throws SQLException {
statement.registerOutParameter(parameterIndex, sqlType, typeName);
}
/**
* This method is used to register output parameter on CallableStatements for Stored Procedures
* as each database seems to have a different method.
*
* @see java.sql.CallableStatement#registerOutParameter(String parameterName, int sqlType)
*/
public void registerOutputParameter(CallableStatement statement, String parameterName, int sqlType) throws SQLException {
statement.registerOutParameter(parameterName, sqlType);
}
/**
* This method is used to register output parameter on CallableStatements for Stored Procedures
* as each database seems to have a different method.
*
* @see java.sql.CallableStatement#registerOutParameter(String parameterName, int sqlType, String typeName)
*/
public void registerOutputParameter(CallableStatement statement, String parameterName, int sqlType, String typeName) throws SQLException {
statement.registerOutParameter(parameterName, sqlType, typeName);
}
/**
* This is used as some databases create the primary key constraint differently, i.e. Access.
*/
public boolean requiresNamedPrimaryKeyConstraints() {
return false;
}
/**
* Used for stored procedure creation: Some platforms need brackets around arguments declaration even if no arguments exist. Those platform will override this and return true. All other platforms will omit the brackets in this case.
*/
public boolean requiresProcedureBrackets() {
return false;
}
/**
* USed for sp calls.
*/
public boolean requiresProcedureCallBrackets() {
return true;
}
/**
* Used for sp calls. Sybase must print output after output params.
*/
public boolean requiresProcedureCallOuputToken() {
return false;
}
/**
* INTERNAL:
* Indicates whether the version of CallableStatement.registerOutputParameter method
* that takes type name should be used.
*/
public boolean requiresTypeNameToRegisterOutputParameter() {
return false;
}
/**
* Used for table creation. If a database platform does not support ALTER
* TABLE syntax to add/drop unique constraints (like Symfoware), overriding
* this method will allow the constraint to be specified in the CREATE TABLE
* statement.
* <p>
* This only affects unique constraints specified using the UniqueConstraint
* annotation or equivalent method. Columns for which the 'unique' attribute
* is set to true will be declared 'UNIQUE' in the CREATE TABLE statement
* regardless of the return value of this method.
*
* @return whether unique constraints should be declared as part of the
* CREATE TABLE statement instead of in separate ALTER TABLE
* ADD/DROP statements.
*/
public boolean requiresUniqueConstraintCreationOnTableCreate() {
return false;
}
/**
* INTERNAL:
* Used by Exists queries because they just need to select a single row.
* In most databases, we will select one of the primary key fields.
*
* On databases where, for some reason we cannot select one of the key fields
* this method can be overridden
* @see SymfowarePlatform
*/
public void retrieveFirstPrimaryKeyOrOne(ReportQuery subselect){
subselect.setShouldRetrieveFirstPrimaryKey(true);
}
/**
* Used for jdbc drivers which do not support autocommit to explicitly rollback a transaction
* This method is a no-op for databases which implement autocommit as expected.
*/
public void rollbackTransaction(DatabaseAccessor accessor) throws SQLException {
if (!supportsAutoCommit()) {
accessor.getConnection().rollback();
}
}
/**
* ADVANCED:
* Set the maximum length allowed by the database for a Varchar Parameter
* This is used by subclasses when writing SQL for parameters
* @see DB2Platform
*/
public void setCastSizeForVarcharParameter(int maxLength){
castSizeForVarcharParameter = maxLength;
}
protected void setClassTypes(Map<String, Class<?>> classTypes) {
this.classTypes = classTypes;
}
/**
* ADVANCED:
* Set the code for preparing cursored output
* parameters in a stored procedure
*/
public void setCursorCode(int cursorCode) {
this.cursorCode = cursorCode;
}
/**
* During auto-detect, the driver name is set on the platform.
*/
public void setDriverName(String driverName) {
this.driverName = driverName;
}
protected void setFieldTypes(Map<Class<?>, FieldTypeDefinition> theFieldTypes) {
fieldTypes = theFieldTypes;
}
/**
* PUBLIC:
* Allow for the max batch writing size to be set.
* This allows for the batch size to be limited as most database have strict limits.
* The size is in characters, the default is 32000 but the real value depends on the database configuration.
*/
public void setMaxBatchWritingSize(int maxBatchWritingSize) {
this.maxBatchWritingSize = maxBatchWritingSize;
}
public void setSequenceCounterFieldName(String name) {
if (getDefaultSequence() instanceof TableSequence) {
((TableSequence)getDefaultSequence()).setCounterFieldName(name);
} else {
if (!name.equals((new TableSequence()).getCounterFieldName())) {
ValidationException.wrongSequenceType(Helper.getShortClassName(getDefaultSequence()), "setCounterFieldName");
}
}
}
public void setSequenceNameFieldName(String name) {
if (getDefaultSequence() instanceof TableSequence) {
((TableSequence)getDefaultSequence()).setNameFieldName(name);
} else {
if (!name.equals((new TableSequence()).getNameFieldName())) {
throw ValidationException.wrongSequenceType(Helper.getShortClassName(getDefaultSequence()), "setNameFieldName");
}
}
}
public void setSequenceTableName(String name) {
if (getDefaultSequence() instanceof TableSequence) {
((TableSequence)getDefaultSequence()).setTableName(name);
} else {
if (!name.equals((new TableSequence()).getTableName())) {
throw ValidationException.wrongSequenceType(Helper.getShortClassName(getDefaultSequence()), "setTableName");
}
}
}
/**
* Bind all arguments to any SQL statement.
*/
public void setShouldBindAllParameters(boolean shouldBindAllParameters) {
this.shouldBindAllParameters = shouldBindAllParameters;
}
/**
* Cache all prepared statements, this requires full parameter binding as well.
*/
public void setShouldCacheAllStatements(boolean shouldCacheAllStatements) {
this.shouldCacheAllStatements = shouldCacheAllStatements;
}
/**
* Used to enable parameter binding and override the platform default
*/
public void setShouldForceBindAllParameters(boolean shouldForceBindAllParameters) {
this.shouldForceBindAllParameters = shouldForceBindAllParameters;
}
/**
* Can be used if the app expects upper case but the database is not return consistent case, i.e. different databases.
*/
public void setShouldForceFieldNamesToUpperCase(boolean shouldForceFieldNamesToUpperCase) {
this.shouldForceFieldNamesToUpperCase = shouldForceFieldNamesToUpperCase;
}
/**
* Allow for case in field names to be ignored as some databases are not case sensitive and when using custom this can be an issue.
*/
public static void setShouldIgnoreCaseOnFieldComparisons(boolean newShouldIgnoreCaseOnFieldComparisons) {
shouldIgnoreCaseOnFieldComparisons = newShouldIgnoreCaseOnFieldComparisons;
}
/**
* PUBLIC:
* Set if our driver level data conversion optimization is enabled.
* This can be disabled as some drivers perform data conversion themselves incorrectly.
*/
public void setShouldOptimizeDataConversion(boolean value) {
this.shouldOptimizeDataConversion = value;
}
public void setShouldTrimStrings(boolean aBoolean) {
shouldTrimStrings = aBoolean;
}
/**
* The statement cache size for prepare parameterized statements.
*/
public void setStatementCacheSize(int statementCacheSize) {
this.statementCacheSize = statementCacheSize;
}
public void setStringBindingSize(int aSize) {
stringBindingSize = aSize;
}
/**
* supportsAutoCommit can be set to false for JDBC drivers which do not support autocommit.
*/
public void setSupportsAutoCommit(boolean supportsAutoCommit) {
this.supportsAutoCommit = supportsAutoCommit;
}
/**
* PUBLIC:
* Get the String used on all table creation statements generated from the DefaultTableGenerator
* with a session using this project (DDL generation). This value will be appended to CreationSuffix strings
* stored on the DatabaseTable or TableDefinition.
* ie setTableCreationSuffix("engine=InnoDB");
*/
public void setTableCreationSuffix(String tableCreationSuffix){
this.tableCreationSuffix = tableCreationSuffix;
}
/**
* Set the transaction isolation setting for a connection.
*/
public void setTransactionIsolation(int isolationLevel) {
transactionIsolation = isolationLevel;
}
/**
* Return true if JDBC syntax should be used for stored procedure calls.
*/
public void setUseJDBCStoredProcedureSyntax(Boolean useJDBCStoredProcedureSyntax) {
this.useJDBCStoredProcedureSyntax = useJDBCStoredProcedureSyntax;
}
public void setUsesBatchWriting(boolean usesBatchWriting) {
this.usesBatchWriting = usesBatchWriting;
}
public void setUsesByteArrayBinding(boolean usesByteArrayBinding) {
this.usesByteArrayBinding = usesByteArrayBinding;
}
/**
* Some JDBC 2 drivers to not support batching, so this lets are own batching be used.
*/
public void setUsesJDBCBatchWriting(boolean usesJDBCBatchWriting) {
this.usesJDBCBatchWriting = usesJDBCBatchWriting;
}
/**
* Advanced:
* This is used to enable native batch writing on drivers that support it. Enabling
* Native batchwriting will result in the batch writing mechanisms to be used on objects
* that have optimistic locking, and so execution of statements on these objects will be
* delayed until the batch statement is executed. Only use this method with platforms that
* have overridden the prepareBatchStatement, addBatch and executeBatch as required
*
* Current support is limited to the Oracle9Platform class.
*
* @param usesNativeBatchWriting - flag to turn on/off native batch writing
*/
public void setUsesNativeBatchWriting(boolean usesNativeBatchWriting){
this.usesNativeBatchWriting = usesNativeBatchWriting;
}
public void setUsesNativeSQL(boolean usesNativeSQL) {
this.usesNativeSQL = usesNativeSQL;
}
/**
* Return the custom batch writing mechanism.
*/
public BatchWritingMechanism getBatchWritingMechanism() {
return batchWritingMechanism;
}
/**
* Set the custom batch writing mechanism.
*/
public void setBatchWritingMechanism(BatchWritingMechanism batchWritingMechanism) {
this.batchWritingMechanism = batchWritingMechanism;
}
/**
* PUBLIC:
* Set if SQL-Level pagination should be used for FirstResult and MaxRows settings.
* Default is true.
*
* Note: This setting is used to disable SQL-level pagination on platforms for which it is
* implemented. On platforms where we use JDBC for pagination, it will be ignored
*/
public void setShouldUseRownumFiltering(boolean useRownumFiltering) {
this.useRownumFiltering = useRownumFiltering;
}
public void setUsesStreamsForBinding(boolean usesStreamsForBinding) {
this.usesStreamsForBinding = usesStreamsForBinding;
}
/**
* PUBLIC:
* Changes the way that OuterJoins are done on the database. With a value of
* true, outerjoins are performed in the where clause using the outer join token
* for that database.
*
* With the value of false, outerjoins are performed in the from clause.
*/
public void setPrintOuterJoinInWhereClause(boolean printOuterJoinInWhereClause) {
this.printOuterJoinInWhereClause = printOuterJoinInWhereClause;
}
/**
* PUBLIC:
* Changes the way that inner joins are printed in generated SQL for the database.
* With a value of true, inner joins are printed in the WHERE clause,
* if false, inner joins are printed in the FROM clause.
*/
public void setPrintInnerJoinInWhereClause(boolean printInnerJoinInWhereClause) {
this.printInnerJoinInWhereClause = printInnerJoinInWhereClause;
}
public void setUsesStringBinding(boolean aBool) {
usesStringBinding = aBool;
}
/**
* Bind all arguments to any SQL statement.
*/
public boolean shouldBindAllParameters() {
return shouldBindAllParameters;
}
/**
* Cache all prepared statements, this requires full parameter binding as well.
*/
public boolean shouldCacheAllStatements() {
return shouldCacheAllStatements;
}
/**
* Used for table creation. Most databases create an index automatically
* when a primary key is created. Symfoware does not.
*
* @return whether an index should be created explicitly for primary keys
*/
public boolean shouldCreateIndicesForPrimaryKeys() {
return false;
}
/**
* Used for table creation. Most databases create an index automatically for
* columns with a unique constraint. Symfoware does not.
*
* @return whether an index should be created explicitly for unique
* constraints
*/
public boolean shouldCreateIndicesOnUniqueKeys() {
return false;
}
/**
* Used for table creation. Most databases do not create an index automatically for
* foreign key columns. Normally it is recommended to index foreign key columns.
* This allows for foreign key indexes to be configured, by default foreign keys are not indexed.
*
* @return whether an index should be created explicitly for foreign key constraints
*/
public boolean shouldCreateIndicesOnForeignKeys() {
return shouldCreateIndicesOnForeignKeys;
}
/**
* Used for table creation. Most databases do not create an index automatically for
* foreign key columns. Normally it is recommended to index foreign key columns.
* This allows for foreign key indexes to be configured, by default foreign keys are not indexed.
*/
public void setShouldCreateIndicesOnForeignKeys(boolean shouldCreateIndicesOnForeignKeys) {
this.shouldCreateIndicesOnForeignKeys = shouldCreateIndicesOnForeignKeys;
}
/**
* Used to enable parameter binding and override platform default
*/
public boolean shouldForceBindAllParameters() {
return this.shouldForceBindAllParameters;
}
/**
* Can be used if the app expects upper case but the database is not return consistent case, i.e. different databases.
*/
public boolean shouldForceFieldNamesToUpperCase() {
return shouldForceFieldNamesToUpperCase;
}
/**
* Allow for case in field names to be ignored as some databases are not case sensitive and when using custom this can be an issue.
*/
public static boolean shouldIgnoreCaseOnFieldComparisons() {
return shouldIgnoreCaseOnFieldComparisons;
}
/**
* Allow for the platform to ignore exceptions.
* This is required for DB2 which throws no-data modified as an exception.
*/
public boolean shouldIgnoreException(SQLException exception) {
// By default nothing is ignored.
return false;
}
/**
* Return if our driver level data conversion optimization is enabled.
* This can be disabled as some drivers perform data conversion themselves incorrectly.
*/
public boolean shouldOptimizeDataConversion() {
return shouldOptimizeDataConversion;
}
/**
* Used for stored procedure creation: Some platforms declare variables AFTER the procedure body's BEGIN string. These need to override and return true. All others will print the variable declaration BEFORE the body's BEGIN string.
*/
public boolean shouldPrintStoredProcedureVariablesAfterBeginString() {
return false;
}
/**
* Some Platforms want the constraint name after the constraint definition.
*/
public boolean shouldPrintConstraintNameAfter() {
return false;
}
/**
* This is required in the construction of the stored procedures with
* output parameters
*/
public boolean shouldPrintInOutputTokenBeforeType() {
return true;
}
/**
* Some database require outer joins to be given in the where clause, others require it in the from clause.
*/
public boolean shouldPrintOuterJoinInWhereClause() {
if(this.printOuterJoinInWhereClause == null) {
this.printOuterJoinInWhereClause = Boolean.FALSE;
}
return this.printOuterJoinInWhereClause;
}
/**
* This allows which clause inner joins are printed into in SQL generation.
* By default most platforms put inner joins in the WHERE clause.
* If set to false, inner joins will be printed in the FROM clause.
*/
public boolean shouldPrintInnerJoinInWhereClause() {
if (this.printInnerJoinInWhereClause == null) {
return true;
}
return this.printInnerJoinInWhereClause;
}
/**
* Used for stored procedure creation: Some platforms want to print prefix for INPUT arguments BEFORE NAME. If wanted, override and return true.
*/
public boolean shouldPrintInputTokenAtStart() {
return false;
}
/**
* This is required in the construction of the stored procedures with
* output parameters
*/
public boolean shouldPrintOutputTokenBeforeType() {
return true;
}
/**
* This is required in the construction of the stored procedures with
* output parameters
*/
public boolean shouldPrintOutputTokenAtStart() {
return false;
}
/**
* INTERNAL:
* Should the variable name of a stored procedure call be printed as part of the procedure call
* e.g. EXECUTE PROCEDURE MyStoredProc(myvariable = ?)
*/
public boolean shouldPrintStoredProcedureArgumentNameInCall(){
return true;
}
public boolean shouldPrintForUpdateClause() {
return true;
}
public boolean shouldTrimStrings() {
return shouldTrimStrings;
}
@Override
public boolean shouldUseCustomModifyForCall(DatabaseField field) {
return (field.getSqlType() == Types.STRUCT &&
(typeConverters != null && typeConverters.containsKey(field.getType()))) ||
super.shouldUseCustomModifyForCall(field);
}
/**
* JDBC defines and outer join syntax, many drivers do not support this. So we normally avoid it.
*/
public boolean shouldUseJDBCOuterJoinSyntax() {
return true;
}
/**
* PUBLIC:
* Return if Oracle ROWNUM pagination should be used for FirstResult and MaxRows settings.
* Default is true.
*
* Note: This setting is used to disable SQL-level pagination on platforms for which it is
* implemented. On platforms where we use JDBC for pagination, it will be ignored
*/
public boolean shouldUseRownumFiltering() {
return this.useRownumFiltering;
}
/**
* Indicates whether the ANSI syntax for inner joins (e.g. SELECT FROM t1
* JOIN t2 ON t1.pk = t2.fk) is supported by this platform.
*/
public boolean supportsANSIInnerJoinSyntax() {
return true;
}
/**
* supportsAutoCommit must sometimes be set to false for JDBC drivers which do not
* support autocommit. Used to determine how to handle transactions properly.
*/
public boolean supportsAutoCommit() {
return supportsAutoCommit;
}
/**
* Some db allow VARCHAR db field to be used in arithmetic operations automatically converting them to numeric:
* UPDATE OL_PHONE SET PHONE_ORDER_VARCHAR = (PHONE_ORDER_VARCHAR + 1) WHERE ...
* SELECT ... WHERE ... t0.MANAGED_ORDER_VARCHAR BETWEEN 1 AND 4 ...
*/
public boolean supportsAutoConversionToNumericForArithmeticOperations() {
return false;
}
public boolean supportsForeignKeyConstraints() {
return true;
}
public boolean supportsUniqueKeyConstraints() {
return true;
}
/**
* By default, platforms do not support VPD. Those that do need to override
* this method.
*/
public boolean supportsVPD() {
return false;
}
/**
* INTERNAL:
* Indicates whether the platform supports timeouts on For Update
*
* @see DatabasePlatform#getSelectForUpdateWaitString(Integer waitTimeout)
*/
public boolean supportsWaitForUpdate() {
return false;
}
public boolean supportsPrimaryKeyConstraint() {
return true;
}
public boolean supportsStoredFunctions() {
return false;
}
public boolean supportsDeleteOnCascade() {
return supportsForeignKeyConstraints();
}
/**
* Internal: This gets called on each batch statement execution
* Needs to be implemented so that it returns the number of rows successfully modified
* by this statement for optimistic locking purposes.
*
* @param isStatementPrepared - flag is set to true if this statement is prepared
* @return - number of rows modified/deleted by this statement
*/
public int executeBatch(Statement statement, boolean isStatementPrepared) throws java.sql.SQLException {
int[] rowCounts = statement.executeBatch();
int rowCount = 0;
// Otherwise check if the row counts were returned.
for (int count : rowCounts) {
if (count > 0) {
// The row count will be matched with the statement count.
rowCount ++;
} else {
// The row counts were not known, check for a total row count.
// If the total count is not known, then the update should fail,
// and the platform must override canBatchWriteWithOptimisticLocking() to return false.
return statement.getUpdateCount();
}
}
return rowCount;
}
/**
* because each platform has different requirements for accessing stored procedures and
* the way that we can combine resultsets and output params, the stored procedure call
* is being executed on the platform.
*/
public Object executeStoredProcedure(DatabaseCall dbCall, PreparedStatement statement, DatabaseAccessor accessor, AbstractSession session) throws SQLException {
Object result = null;
ResultSet resultSet = null;
if (!dbCall.getReturnsResultSet()) {// no result set is expected
if (dbCall.isCursorOutputProcedure()) {
result = accessor.executeNoSelect(dbCall, statement, session);
int index = dbCall.getCursorOutIndex();
resultSet = (ResultSet)dbCall.getOutputParameterValue((CallableStatement)statement, index - 1, session);
} else {
accessor.executeDirectNoSelect(statement, dbCall, session);
// Meaning we have at least one out parameter (or out cursors).
if (dbCall.shouldBuildOutputRow() || dbCall.hasOutputCursors()) {
result = accessor.buildOutputRow((CallableStatement)statement, dbCall, session);
// ReadAllQuery may be returning just output params, or they
// may be executing a DataReadQuery, which also assumes a vector
if (dbCall.areManyRowsReturned()) {
Vector tempResult = new Vector();
tempResult.add(result);
result = tempResult;
}
} else {
// No out params whatsover, return an empty list.
result = new Vector();
}
}
} else {
// so specifically in Sybase JConnect 5.5 we must create the result vector before accessing the
// output params in the case where the user is returning both. this is a driver limitation
resultSet = accessor.executeSelect(dbCall, statement, session);
}
if (resultSet != null) {
dbCall.matchFieldOrder(resultSet, accessor, session);
if (dbCall.isCursorReturned()) {
dbCall.setStatement(statement);
dbCall.setResult(resultSet);
return dbCall;
}
result = accessor.processResultSet(resultSet, dbCall, statement, session);
}
// If the output is not allowed with the result set, we must hold off till the result set has
// been processed before accessing the out parameters.
if (dbCall.shouldBuildOutputRow() && ! isOutputAllowWithResultSet()) {
AbstractRecord outputRow = accessor.buildOutputRow((CallableStatement)statement, dbCall, session);
dbCall.getQuery().setProperty("output", outputRow);
session.getEventManager().outputParametersDetected(outputRow, dbCall);
}
return result;
}
/**
* Used for determining if an SQL exception was communication based. This SQL should be
* as efficient as possible and ensure a round trip to the database.
*/
public void setPingSQL(String pingSQL) {
this.pingSQL = pingSQL;
}
/**
* INTERNAL
* Set the parameter in the JDBC statement at the given index.
* This support a wide range of different parameter types,
* and is heavily optimized for common types.
*/
public void setParameterValueInDatabaseCall(Object parameter,
PreparedStatement statement, int index, AbstractSession session)
throws SQLException {
// Process common types first.
if (parameter instanceof String) {
// Check for stream binding of large strings.
if (usesStringBinding() && (((String)parameter).length() > getStringBindingSize())) {
CharArrayReader reader = new CharArrayReader(((String)parameter).toCharArray());
statement.setCharacterStream(index, reader, ((String)parameter).length());
} else {
if (shouldUseGetSetNString()) {
statement.setNString(index, (String) parameter);
} else {
statement.setString(index, (String) parameter);
}
}
} else if (parameter instanceof Number) {
Number number = (Number) parameter;
if (number instanceof Integer) {
statement.setInt(index, number.intValue());
} else if (number instanceof Long) {
statement.setLong(index, number.longValue());
} else if (number instanceof BigDecimal) {
statement.setBigDecimal(index, (BigDecimal) number);
} else if (number instanceof Double) {
statement.setDouble(index, number.doubleValue());
} else if (number instanceof Float) {
statement.setFloat(index, number.floatValue());
} else if (number instanceof Short) {
statement.setShort(index, number.shortValue());
} else if (number instanceof Byte) {
statement.setByte(index, number.byteValue());
} else if (number instanceof BigInteger) {
// Convert to BigDecimal.
statement.setBigDecimal(index, new BigDecimal((BigInteger) number));
} else {
statement.setObject(index, parameter);
}
} else if (parameter instanceof java.sql.Date){
statement.setDate(index,(java.sql.Date)parameter);
} else if (parameter instanceof java.time.LocalDate){
statement.setDate(index, java.sql.Date.valueOf((java.time.LocalDate) parameter));
} else if (parameter instanceof java.sql.Timestamp){
statement.setTimestamp(index,(java.sql.Timestamp)parameter);
} else if (parameter instanceof java.time.LocalDateTime){
statement.setTimestamp(index, java.sql.Timestamp.valueOf((java.time.LocalDateTime) parameter));
} else if (parameter instanceof java.time.OffsetDateTime) {
statement.setTimestamp(index, java.sql.Timestamp.from(((java.time.OffsetDateTime) parameter).toInstant()));
} else if (parameter instanceof java.sql.Time){
statement.setTime(index,(java.sql.Time)parameter);
} else if (parameter instanceof java.time.LocalTime){
java.time.LocalTime lt = (java.time.LocalTime) parameter;
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), lt));
statement.setTimestamp(index, ts);
} else if (parameter instanceof java.time.OffsetTime) {
java.time.OffsetTime ot = (java.time.OffsetTime) parameter;
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), ot.toLocalTime()));
statement.setTimestamp(index, ts);
} else if (parameter instanceof Boolean) {
statement.setBoolean(index, (Boolean) parameter);
} else if (parameter == null) {
// Normally null is passed as a DatabaseField so the type is included, but in some case may be passed directly.
statement.setNull(index, getJDBCType((Class)null));
} else if (parameter instanceof DatabaseField) {
setNullFromDatabaseField((DatabaseField)parameter, statement, index);
} else if (parameter instanceof byte[]) {
if (usesStreamsForBinding()) {
ByteArrayInputStream inputStream = new ByteArrayInputStream((byte[])parameter);
statement.setBinaryStream(index, inputStream, ((byte[])parameter).length);
} else {
statement.setBytes(index, (byte[])parameter);
}
}
// Next process types that need conversion.
else if (parameter instanceof Calendar) {
statement.setTimestamp(index, Helper.timestampFromDate(((Calendar)parameter).getTime()));
} else if (parameter.getClass() == ClassConstants.UTILDATE) {
statement.setTimestamp(index, Helper.timestampFromDate((java.util.Date) parameter));
} else if (parameter instanceof Character) {
statement.setString(index, ((Character)parameter).toString());
} else if (parameter instanceof char[]) {
statement.setString(index, new String((char[])parameter));
} else if (parameter instanceof Character[]) {
statement.setString(index, convertObject(parameter, ClassConstants.STRING));
} else if (parameter instanceof Byte[]) {
statement.setBytes(index, (byte[])convertObject(parameter, ClassConstants.APBYTE));
} else if (parameter instanceof SQLXML) {
statement.setSQLXML(index, (SQLXML) parameter);
} else if (parameter instanceof BindCallCustomParameter) {
((BindCallCustomParameter)(parameter)).set(this, statement, index, session);
} else if (typeConverters != null && typeConverters.containsKey(parameter.getClass())){
StructConverter converter = typeConverters.get(parameter.getClass());
parameter = converter.convertToStruct(parameter, getConnection(session, statement.getConnection()));
statement.setObject(index, parameter);
} else {
statement.setObject(index, parameter);
}
}
/**
* INTERNAL
* Set the parameter in the JDBC statement with the given name.
* This support a wide range of different parameter types,
* and is heavily optimized for common types.
*/
public void setParameterValueInDatabaseCall(Object parameter,
CallableStatement statement, String name, AbstractSession session)
throws SQLException {
// Process common types first.
if (parameter instanceof String) {
// Check for stream binding of large strings.
if (usesStringBinding() && (((String)parameter).length() > getStringBindingSize())) {
CharArrayReader reader = new CharArrayReader(((String)parameter).toCharArray());
statement.setCharacterStream(name, reader, ((String)parameter).length());
} else {
if (shouldUseGetSetNString()) {
statement.setNString(name, (String) parameter);
} else {
statement.setString(name, (String) parameter);
}
}
} else if (parameter instanceof Number) {
Number number = (Number) parameter;
if (number instanceof Integer) {
statement.setInt(name, number.intValue());
} else if (number instanceof Long) {
statement.setLong(name, number.longValue());
} else if (number instanceof BigDecimal) {
statement.setBigDecimal(name, (BigDecimal) number);
} else if (number instanceof Double) {
statement.setDouble(name, number.doubleValue());
} else if (number instanceof Float) {
statement.setFloat(name, number.floatValue());
} else if (number instanceof Short) {
statement.setShort(name, number.shortValue());
} else if (number instanceof Byte) {
statement.setByte(name, number.byteValue());
} else if (number instanceof BigInteger) {
// Convert to BigDecimal.
statement.setBigDecimal(name, new BigDecimal((BigInteger) number));
} else {
statement.setObject(name, parameter);
}
} else if (parameter instanceof java.sql.Date){
statement.setDate(name,(java.sql.Date)parameter);
} else if (parameter instanceof java.time.LocalDate){
statement.setDate(name, java.sql.Date.valueOf((java.time.LocalDate) parameter));
} else if (parameter instanceof java.sql.Timestamp){
statement.setTimestamp(name,(java.sql.Timestamp)parameter);
} else if (parameter instanceof java.time.LocalDateTime){
statement.setTimestamp(name, java.sql.Timestamp.valueOf((java.time.LocalDateTime) parameter));
} else if (parameter instanceof java.time.OffsetDateTime) {
statement.setTimestamp(name, java.sql.Timestamp.from(((java.time.OffsetDateTime) parameter).toInstant()));
} else if (parameter instanceof java.sql.Time){
statement.setTime(name,(java.sql.Time)parameter);
} else if (parameter instanceof java.time.LocalTime){
java.time.LocalTime lt = (java.time.LocalTime) parameter;
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), lt));
statement.setTimestamp(name, ts);
} else if (parameter instanceof java.time.OffsetTime) {
java.time.OffsetTime ot = (java.time.OffsetTime) parameter;
java.sql.Timestamp ts = java.sql.Timestamp.valueOf(java.time.LocalDateTime.of(java.time.LocalDate.ofEpochDay(0), ot.toLocalTime()));
statement.setTimestamp(name, ts);
} else if (parameter instanceof Boolean) {
statement.setBoolean(name, (Boolean) parameter);
} else if (parameter == null) {
// Normally null is passed as a DatabaseField so the type is included, but in some case may be passed directly.
statement.setNull(name, getJDBCType((Class)null));
} else if (parameter instanceof DatabaseField) {
setNullFromDatabaseField((DatabaseField)parameter, statement, name);
} else if (parameter instanceof byte[]) {
if (usesStreamsForBinding()) {
ByteArrayInputStream inputStream = new ByteArrayInputStream((byte[])parameter);
statement.setBinaryStream(name, inputStream, ((byte[])parameter).length);
} else {
statement.setBytes(name, (byte[])parameter);
}
}
// Next process types that need conversion.
else if (parameter instanceof Calendar) {
statement.setTimestamp(name, Helper.timestampFromDate(((Calendar)parameter).getTime()));
} else if (parameter.getClass() == ClassConstants.UTILDATE) {
statement.setTimestamp(name, Helper.timestampFromDate((java.util.Date) parameter));
} else if (parameter instanceof Character) {
statement.setString(name, ((Character)parameter).toString());
} else if (parameter instanceof char[]) {
statement.setString(name, new String((char[])parameter));
} else if (parameter instanceof Character[]) {
statement.setString(name, convertObject(parameter, ClassConstants.STRING));
} else if (parameter instanceof Byte[]) {
statement.setBytes(name, (byte[])convertObject(parameter, ClassConstants.APBYTE));
} else if (parameter instanceof SQLXML) {
statement.setSQLXML(name, (SQLXML) parameter);
} else if (parameter instanceof BindCallCustomParameter) {
((BindCallCustomParameter)(parameter)).set(this, statement, name, session);
} else if (typeConverters != null && typeConverters.containsKey(parameter.getClass())){
StructConverter converter = typeConverters.get(parameter.getClass());
parameter = converter.convertToStruct(parameter, getConnection(session, statement.getConnection()));
statement.setObject(name, parameter);
} else {
statement.setObject(name, parameter);
}
}
protected void setNullFromDatabaseField(DatabaseField databaseField, PreparedStatement statement, int index) throws SQLException {
// Substituted null value for the corresponding DatabaseField.
// Cannot bind null through set object, so we must compute the type, this is not good.
// Fix for bug 2730536: for ARRAY/REF/STRUCT types must pass in the
// user defined type to setNull as well.
if (databaseField instanceof ObjectRelationalDatabaseField) {
ObjectRelationalDatabaseField field = (ObjectRelationalDatabaseField)databaseField;
statement.setNull(index, field.getSqlType(), field.getSqlTypeName());
} else {
int jdbcType = getJDBCTypeForSetNull(databaseField);
statement.setNull(index, jdbcType);
}
}
protected void setNullFromDatabaseField(DatabaseField databaseField, CallableStatement statement, String name) throws SQLException {
// Substituted null value for the corresponding DatabaseField.
// Cannot bind null through set object, so we must compute the type, this is not good.
// Fix for bug 2730536: for ARRAY/REF/STRUCT types must pass in the
// user defined type to setNull as well.
if (databaseField instanceof ObjectRelationalDatabaseField) {
ObjectRelationalDatabaseField field = (ObjectRelationalDatabaseField)databaseField;
statement.setNull(name, field.getSqlType(), field.getSqlTypeName());
} else {
int jdbcType = getJDBCTypeForSetNull(databaseField);
statement.setNull(name, jdbcType);
}
}
/**
* INTERNAL
* Get the parameter from the JDBC statement with the given index.
* @param index - 1-based index in the argument list
*/
public Object getParameterValueFromDatabaseCall(CallableStatement statement, int index, AbstractSession session) throws SQLException {
return statement.getObject(index);
}
/**
* INTERNAL
* Get the parameter from the JDBC statement with the given name.
*/
public Object getParameterValueFromDatabaseCall(CallableStatement statement, String name, AbstractSession session) throws SQLException {
return statement.getObject(name);
}
public boolean usesBatchWriting() {
return usesBatchWriting;
}
public boolean usesByteArrayBinding() {
return usesByteArrayBinding;
}
public boolean usesSequenceTable() {
return getDefaultSequence() instanceof TableSequence;
}
/**
* Some JDBC 2 drivers to not support batching, so this lets are own batching be used.
*/
public boolean usesJDBCBatchWriting() {
return usesJDBCBatchWriting;
}
public boolean usesNativeBatchWriting(){
return usesNativeBatchWriting;
}
public boolean usesNativeSQL() {
return usesNativeSQL;
}
public boolean usesStreamsForBinding() {
return usesStreamsForBinding;
}
public boolean usesStringBinding() {
return usesStringBinding;
}
/**
* INTERNAL:
* Write LOB value - only on Oracle8 and up
*/
public void writeLOB(DatabaseField field, Object value, ResultSet resultSet, AbstractSession session) throws SQLException {
// used by Oracle8Platform
}
/**
* INTERNAL:
* Indicates whether the platform supports the count distinct function with multiple fields.
*/
public boolean supportsCountDistinctWithMultipleFields() {
return false;
}
/**
* INTERNAL:
* Return if this database support index creation.
*/
public boolean supportsIndexes() {
return true;
}
/**
* INTERNAL:
* Return if this database requires the table name when dropping an index.
*/
public boolean requiresTableInIndexDropDDL() {
return false;
}
/**
* INTERNAL:
* Create platform-default Sequence
*/
@Override
protected Sequence createPlatformDefaultSequence() {
return new TableSequence();
}
/**
* INTERNAL:
* Indicates whether the platform supports temporary tables.
* Temporary tables may be used by UpdateAllQueries:
* though attempt is always made to perform UpdateAll without using temporary
* storage there are some scenarios that can't be fulfilled without it.
* Don't override this method.
* If the platform support temporary tables then override
* either supportsLocalTempTables() or supportsGlobalTempTables()
* method.
*/
public boolean supportsTempTables() {
return supportsLocalTempTables() || supportsGlobalTempTables();
}
/**
* INTERNAL:
* Indicates whether the platform supports local temporary tables.
* "Local" means that several threads may create
* temporary tables with the same name.
* Local temporary table is created in the beginning of UpdateAllQuery
* execution and dropped in the end of it.
* Override this method if the platform supports local temporary tables.
*/
public boolean supportsLocalTempTables() {
return false;
}
/**
* INTERNAL:
* Indicates whether the platform supports global temporary tables.
* "Global" means that an attempt to create temporary table with the same
* name for the second time results in exception.
* EclipseLink attempts to create global temporary table in the beginning of UpdateAllQuery,
* execution and assumes that it already exists in case SQLException results.
* In the end of UpdateAllQuery execution all rows are removed from the temporary table -
* it is necessary in case the same temporary table will be used by another UpdateAllQuery
* in the same transaction.
* Override this method if the platform supports global temporary tables.
* Note that this method is ignored in case supportsLocalTempTables() returns true.
*/
public boolean supportsGlobalTempTables() {
return false;
}
/**
* INTERNAL:
* Override this method if the platform supports temporary tables.
* This should contain the beginning of sql string for
* creating temporary table - the sql statement name, for instance:
* "CREATE GLOBAL TEMPORARY TABLE ".
* Don't forget to end it with a space.
*/
protected String getCreateTempTableSqlPrefix() {
throw ValidationException.platformDoesNotOverrideGetCreateTempTableSqlPrefix(Helper.getShortClassName(this));
}
/**
* INTERNAL:
* May override this method if the platform support temporary tables.
* @param table is original table for which temp table is created.
* @return temporary table
*/
public DatabaseTable getTempTableForTable(DatabaseTable table) {
return new DatabaseTable("TL_" + table.getName(), table.getTableQualifier(), table.shouldUseDelimiters(), getStartDelimiter(), getEndDelimiter());
}
/**
* INTERNAL:
* May override this method if the platform support temporary tables.
* This should contain the ending of sql string for
* creating temporary table, for instance:
* " ON COMMIT DELETE ROWS"
* Don't forget to begin it with a space.
*/
protected String getCreateTempTableSqlSuffix() {
return "";
}
/**
* INTERNAL:
* May override this method if the platform supports temporary tables.
* With this method not overridden the sql string for temporary table creation
* will include a list of database fields extracted from descriptor:
* getCreateTempTableSqlPrefix() + getTempTableForTable(table).getQualifiedName() +
* (list of database fields) + getCreateTempTableSqlSuffix().
* If this method is overridden its output will be used instead of fields' list:
* getCreateTempTableSqlPrefix() + getTempTableForTable(table).getQualifiedName() +
* getCreateTempTableSqlBodyForTable(table) + getCreateTempTableSqlSuffix().
* Don't forget to begin it with a space.
* Example: " LIKE " + table.getQualifiedName();
* @param table is original table for which temp table is created.
* @return String
*/
protected String getCreateTempTableSqlBodyForTable(DatabaseTable table) {
return null;
}
/**
* INTERNAL:
* Indicates whether temporary table can specify primary keys (some platforms don't allow that).
* Used by writeCreateTempTableSql method.
*/
protected boolean shouldTempTableSpecifyPrimaryKeys() {
return true;
}
/**
* INTERNAL:
* Don't override this method.
* Write an sql string for creation of the temporary table.
* Note that in case of local temp table support it's possible to limit
* the fields in the temp table to those needed for the operation it supports (usedFields) -
* the temp table will be dropped in the end of query execution.
* Alternatively, in global temp table case the table with a given name is created just once
* and will be potentially used by various operations with various sets of used fields,
* therefore global temp table should contain all mapped fields (allFields).
* Precondition: supportsTempTables() == true.
* Precondition: pkFields contained in usedFields contained in allFields
* @param writer for writing the sql
* @param table is original table for which temp table is created.
* @param pkFields primary key fields for the original table.
* @param usedFields fields that will be used by operation for which temp table is created.
* @param allFields all mapped fields for the original table.
*/
public void writeCreateTempTableSql(Writer writer, DatabaseTable table, AbstractSession session,
Collection pkFields,
Collection usedFields,
Collection allFields) throws IOException
{
String body = getCreateTempTableSqlBodyForTable(table);
if(body == null) {
TableDefinition tableDef = new TableDefinition();
Collection fields;
if(supportsLocalTempTables()) {
fields = usedFields;
} else {
// supportsGlobalTempTables() == true
fields = allFields;
}
Iterator itFields = fields.iterator();
while (itFields.hasNext()) {
DatabaseField field = (DatabaseField)itFields.next();
FieldDefinition fieldDef;
//gfbug3307, should use columnDefinition if it was defined.
if ((field.getColumnDefinition()!= null) && (field.getColumnDefinition().length() == 0)) {
Class<? extends Class> type = ConversionManager.getObjectClass(field.getType());
// Default type to VARCHAR, if unknown.
if (type == null) {
type = ConversionManager.getObjectClass(ClassConstants.STRING);
}
fieldDef = new FieldDefinition(field.getNameDelimited(this), type);
} else {
fieldDef = new FieldDefinition(field.getNameDelimited(this), field.getColumnDefinition());
}
if (pkFields.contains(field) && shouldTempTableSpecifyPrimaryKeys()) {
fieldDef.setIsPrimaryKey(true);
}
tableDef.addField(fieldDef);
}
tableDef.setCreationPrefix(getCreateTempTableSqlPrefix());
tableDef.setName(getTempTableForTable(table).getQualifiedNameDelimited(this));
tableDef.setCreationSuffix(getCreateTempTableSqlSuffix());
tableDef.buildCreationWriter(session, writer);
} else {
writer.write(getCreateTempTableSqlPrefix());
writer.write(getTempTableForTable(table).getQualifiedNameDelimited(this));
writer.write(body);
writer.write(getCreateTempTableSqlSuffix());
}
}
/**
* INTERNAL:
* May need to override this method if the platform supports temporary tables
* and the generated sql doesn't work.
* Write an sql string for insertion into the temporary table.
* Precondition: supportsTempTables() == true.
* @param writer for writing the sql
* @param table is original table for which temp table is created.
* @param usedFields fields that will be used by operation for which temp table is created.
*/
public void writeInsertIntoTableSql(Writer writer, DatabaseTable table, Collection usedFields) throws IOException {
writer.write("INSERT INTO ");
writer.write(getTempTableForTable(table).getQualifiedNameDelimited(this));
writer.write(" (");
writeFieldsList(writer, usedFields, this);
writer.write(") ");
}
/**
* INTERNAL:
* Override this if the platform cannot handle NULL in select clause.
*/
public boolean isNullAllowedInSelectClause() {
return true;
}
/**
* INTERNAL:
* Return true if output parameters can be built with result sets.
*/
public boolean isOutputAllowWithResultSet() {
return true;
}
/**
* INTERNAL:
* Write used on all table creation statements generated from the DefaultTableGenerator
* with a session using this project (DDL generation). This writes the passed in string argument as
* well as the value returned from the DatabasePlatform's getTableCreationSuffix()
*/
public void writeTableCreationSuffix(Writer writer, String tableCreationSuffix) throws IOException {
if(tableCreationSuffix!=null && tableCreationSuffix.length() > 0) {
writer.write(" " + tableCreationSuffix);
}
String defaultTableCreationSuffix = getTableCreationSuffix();
if (defaultTableCreationSuffix !=null && defaultTableCreationSuffix.length()>0) {
writer.write(" " + defaultTableCreationSuffix);
}
}
/**
* INTERNAL:
* May need to override this method if the platform supports temporary tables
* and the generated sql doesn't work.
* Write an sql string for updating the original table from the temporary table.
* Precondition: supportsTempTables() == true.
* Precondition: pkFields and assignFields don't intersect.
* @param writer for writing the sql
* @param table is original table for which temp table is created.
* @param pkFields primary key fields for the original table.
* @param assignedFields fields to be assigned a new value.
*/
public void writeUpdateOriginalFromTempTableSql(Writer writer, DatabaseTable table,
Collection pkFields,
Collection assignedFields) throws IOException
{
writer.write("UPDATE ");
String tableName = table.getQualifiedNameDelimited(this);
writer.write(tableName);
writer.write(" SET (");
writeFieldsList(writer, assignedFields, this);
writer.write(") = (SELECT ");
writeFieldsList(writer, assignedFields, this);
writer.write(" FROM ");
String tempTableName = 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(")");
}
/**
* INTERNAL:
* Write an sql string for deletion from target table using temporary table.
* At this point temporary table should contains pks for the rows that should be
* deleted from target table.
* Temporary tables are not required for DeleteAllQuery, however will be used if
* shouldAlwaysUseTempStorageForModifyAll()==true
* May need to override this method in case it generates sql that doesn't work on the platform.
* Precondition: supportsTempTables() == true.
* @param writer for writing the sql
* @param table is original table for which temp table is created.
* @param targetTable is a table from which to delete.
* @param pkFields primary key fields for the original table.
* @param targetPkFields primary key fields for the target table.
*/
public void writeDeleteFromTargetTableUsingTempTableSql(Writer writer, DatabaseTable table, DatabaseTable targetTable,
Collection pkFields,
Collection targetPkFields, DatasourcePlatform platform) throws IOException
{
writer.write("DELETE FROM ");
String targetTableName = targetTable.getQualifiedNameDelimited(this);
writer.write(targetTableName);
writer.write(" WHERE EXISTS(SELECT ");
writer.write(((DatabaseField)pkFields.iterator().next()).getNameDelimited(platform));
writer.write(" FROM ");
String tempTableName = getTempTableForTable(table).getQualifiedNameDelimited(this);
writer.write(tempTableName);
writeJoinWhereClause(writer, null, targetTableName, pkFields, targetPkFields, this);
writer.write(")");
}
public boolean wasFailureCommunicationBased(SQLException exception, Connection connection, AbstractSession sessionForProfile){
if (connection == null) {
//Without a connection we are unable to determine what caused the error so return false.
//The only case where connection will be null should be External Connection Pooling so
//returning false is ok as there is no connection management requirement
return false;
} else if (this.pingSQL == null) {
// By default use the JDBC isValid API unless a ping SQL has been set.
// The ping SQL is set by most platforms, but user could set to null to used optimized JDBC check if desired.
try {
return connection.isValid(IS_VALID_TIMEOUT);
} catch (Throwable failed) {
// Catch throwable as old JDBC drivers may not support isValid.
return false;
}
}
PreparedStatement statement = null;
try{
sessionForProfile.startOperationProfile(SessionProfiler.ConnectionPing);
if (sessionForProfile.shouldLog(SessionLog.FINE, SessionLog.SQL)) {// Avoid printing if no logging required.
sessionForProfile.log(SessionLog.FINE, SessionLog.SQL, getPingSQL(), null, null, false);
}
statement = connection.prepareStatement(getPingSQL());
ResultSet result = statement.executeQuery();
result.close();
statement.close();
}catch (SQLException ex){
try{
//try to close statement again in case the query or result.close() caused an exception.
if (statement != null) statement.close();
}catch (SQLException exception2){
//ignore;
}
return true;
}finally{
sessionForProfile.endOperationProfile(SessionProfiler.ConnectionPing);
}
return false;
}
/**
* INTERNAL:
* Don't override this method.
* Write an sql string for clean up of the temporary table.
* Drop a local temp table or delete all from a global temp table (so that it's
* ready to be used again in the same transaction).
* Precondition: supportsTempTables() == true.
* @param writer for writing the sql
* @param table is original table for which temp table is created.
*/
public void writeCleanUpTempTableSql(Writer writer, DatabaseTable table) throws IOException {
if(supportsLocalTempTables()) {
writer.write("DROP TABLE ");
} else {
// supportsGlobalTempTables() == true
writer.write("DELETE FROM ");
}
writer.write(getTempTableForTable(table).getQualifiedNameDelimited(this));
}
/**
* INTERNAL:
* That method affects UpdateAllQuery and DeleteAllQuery execution.
* In case it returns false modify all queries would attempt to proceed
* without using temporary storage if it is possible.
* In case it returns true modify all queries would use temporary storage unless
* each modify statement doesn't reference any other tables.
* May need to override this method if the platform can't handle the sql
* generated for modify all queries without using temporary storage.
*/
public boolean shouldAlwaysUseTempStorageForModifyAll() {
return false;
}
/**
* INTERNAL:
* May need to override this method if the sql generated for UpdateAllQuery
* using temp tables fails in case parameter binding is used.
*/
public boolean dontBindUpdateAllQueryUsingTempTables() {
return false;
}
/**
* INTERNAL:
* helper method, don't override.
*/
protected static void writeFieldsList(Writer writer, Collection fields, DatasourcePlatform platform) throws IOException {
boolean isFirst = true;
Iterator itFields = fields.iterator();
while(itFields.hasNext()) {
if(isFirst) {
isFirst = false;
} else {
writer.write(", ");
}
DatabaseField field = (DatabaseField)itFields.next();
writer.write(field.getNameDelimited(platform));
}
}
/**
* INTERNAL:
* helper method, don't override.
*/
protected static void writeAutoAssignmentSetClause(Writer writer, String tableName1, String tableName2, Collection fields, DatasourcePlatform platform) throws IOException {
writer.write(" SET ");
writeFieldsAutoClause(writer, tableName1, tableName2, fields, ", ", platform);
}
/**
* INTERNAL:
* helper method, don't override.
*/
protected static void writeAutoJoinWhereClause(Writer writer, String tableName1, String tableName2, Collection pkFields, DatasourcePlatform platform) throws IOException {
writer.write(" WHERE ");
writeFieldsAutoClause(writer, tableName1, tableName2, pkFields, " AND ", platform);
}
/**
* INTERNAL:
* helper method, don't override.
*/
protected static void writeFieldsAutoClause(Writer writer, String tableName1, String tableName2, Collection fields, String separator, DatasourcePlatform platform) throws IOException {
writeFields(writer, tableName1, tableName2, fields, fields, separator, platform);
}
/**
* INTERNAL:
* helper method, don't override.
*/
protected static void writeJoinWhereClause(Writer writer, String tableName1, String tableName2, Collection pkFields1, Collection pkFields2, DatasourcePlatform platform) throws IOException {
writer.write(" WHERE ");
writeFields(writer, tableName1, tableName2, pkFields1, pkFields2, " AND ", platform);
}
/**
* INTERNAL:
* helper method, don't override.
*/
protected static void writeFields(Writer writer, String tableName1, String tableName2, Collection fields1, Collection fields2, String separator, DatasourcePlatform platform) throws IOException {
boolean isFirst = true;
Iterator itFields1 = fields1.iterator();
Iterator itFields2 = fields2.iterator();
while(itFields1.hasNext()) {
if(isFirst) {
isFirst = false;
} else {
writer.write(separator);
}
if(tableName1 != null) {
writer.write(tableName1);
writer.write(".");
}
String fieldName1 = ((DatabaseField)itFields1.next()).getNameDelimited(platform);
writer.write(fieldName1);
writer.write(" = ");
if(tableName2 != null) {
writer.write(tableName2);
writer.write(".");
}
String fieldName2 = ((DatabaseField)itFields2.next()).getNameDelimited(platform);
writer.write(fieldName2);
}
}
public boolean shouldPrintFieldIdentityClause(AbstractSession session, String qualifiedFieldName) {
if (!supportsIdentity()) {
return false;
}
if ((session.getSequencing() == null) || (session.getSequencing().whenShouldAcquireValueForAll() == Sequencing.BEFORE_INSERT)) {
return false;
}
boolean shouldAcquireSequenceValueAfterInsert = false;
DatabaseField field = new DatabaseField(qualifiedFieldName, getStartDelimiter(), getEndDelimiter());
Iterator<ClassDescriptor> descriptors = session.getDescriptors().values().iterator();
while (descriptors.hasNext()) {
ClassDescriptor descriptor = descriptors.next();
if (!descriptor.usesSequenceNumbers()) {
continue;
}
if (descriptor.getSequenceNumberField().equals(field)) {
String seqName = descriptor.getSequenceNumberName();
Sequence sequence = getSequence(seqName);
shouldAcquireSequenceValueAfterInsert = sequence.shouldAcquireValueAfterInsert();
break;
}
}
return shouldAcquireSequenceValueAfterInsert;
}
public void printFieldTypeSize(Writer writer, FieldDefinition field,
FieldTypeDefinition fieldType, boolean shouldPrintFieldIdentityClause) throws IOException {
printFieldTypeSize(writer, field, fieldType);
}
protected void printFieldTypeSize(Writer writer, FieldDefinition field,
FieldTypeDefinition fieldType) throws IOException {
writer.write(fieldType.getName());
if ((fieldType.isSizeAllowed()) && ((field.getSize() != 0) || (fieldType.isSizeRequired()))) {
writer.write("(");
if (field.getSize() == 0) {
writer.write(Integer.toString(fieldType.getDefaultSize()));
} else {
writer.write(Integer.toString(field.getSize()));
}
if (field.getSubSize() != 0) {
writer.write(",");
writer.write(Integer.toString(field.getSubSize()));
} else if (fieldType.getDefaultSubSize() != 0) {
writer.write(",");
writer.write(Integer.toString(fieldType.getDefaultSubSize()));
}
writer.write(")");
}
}
/**
* Allows unique columns to be defined as constraint if the UNIQUE keyword is not support on a column defintion.
*/
public boolean supportsUniqueColumns() {
return true;
}
public void printFieldUnique(Writer writer, boolean shouldPrintFieldIdentityClause) throws IOException {
printFieldUnique(writer);
}
protected void printFieldUnique(Writer writer) throws IOException {
if (supportsUniqueKeyConstraints()) {
writer.write(" UNIQUE");
}
}
public void writeParameterMarker(Writer writer, ParameterExpression expression, AbstractRecord record, DatabaseCall call) throws IOException {
writer.write("?");
}
/**
* INTERNAL:
* This method builds an Array using the unwrapped connection within the session
* @return Array
*/
public Array createArray(String elementDataTypeName, Object[] elements, AbstractSession session, Connection connection) throws SQLException {
//Bug#5200836 need unwrap the connection prior to using.
java.sql.Connection unwrappedConnection = getConnection(session, connection);
return createArray(elementDataTypeName,elements,unwrappedConnection);
}
/**
* INTERNAL:
* This method builds a Struct using the unwrapped connection within the session
* @return Struct
*/
public Struct createStruct(String structTypeName, Object[] attributes, AbstractSession session, Connection connection) throws SQLException {
java.sql.Connection unwrappedConnection = getConnection(session, connection);
return createStruct(structTypeName,attributes,unwrappedConnection);
}
/**
* INTERNAL:
* This method builds a Struct using the unwrapped connection within the session
* @return Struct
*/
public Struct createStruct(String structTypeName, Object[] attributes, AbstractRecord row, Vector orderedFields, AbstractSession session, Connection connection) throws SQLException {
java.sql.Connection unwrappedConnection = getConnection(session, connection);
return createStruct(structTypeName,attributes,unwrappedConnection);
}
/**
* INTERNAL:
* Platforms that support java.sql.Array may override this method.
* @return Array
*/
public Array createArray(String elementDataTypeName, Object[] elements, Connection connection) throws SQLException {
return connection.createArrayOf(elementDataTypeName, elements);
}
/**
* INTERNAL:
* Platforms that support java.sql.Struct may override this method.
* @return Struct
*/
public Struct createStruct(String structTypeName, Object[] attributes, Connection connection) throws SQLException {
return connection.createStruct(structTypeName, attributes);
}
/**
* INTERNAL:
* Indicates whether the passed object is an instance of XDBDocument.
* To avoid dependency on oracle.xdb the method returns false.
* Overridden in Oracle9Platform
* @return String
*/
public boolean isXDBDocument(Object obj) {
return false;
}
/**
* PUBLIC:
* Allows platform to choose whether to bind literals in DatabaseCalls or not.
*/
public boolean shouldBindLiterals() {
return this.shouldBindLiterals;
}
/**
* PUBLIC:
* Allows user to choose whether to bind literals in DatabaseCalls or not.
*/
public void setShouldBindLiterals(boolean shouldBindLiterals) {
this.shouldBindLiterals = shouldBindLiterals;
}
/**
* INTERNAL:
* Some databases have issues with using parameters on certain functions and relations.
* This allows statements to disable binding only in these cases.
*/
public boolean isDynamicSQLRequiredForFunctions() {
return false;
}
/**
* INTERNAL:
* Platforms that support java.sql.Ref may override this method.
* @return Object
*/
public Object getRefValue(Ref ref,Connection connection) throws SQLException {
return ref.getObject();
}
/**
* INTERNAL:
* This method builds a REF using the unwrapped connection within the session
* @return Object
*/
public Object getRefValue(Ref ref,AbstractSession executionSession,Connection connection) throws SQLException {
//Bug#6068155, ensure connection is lived when processing the REF type value.
java.sql.Connection unwrappedConnection = getConnection(executionSession,connection);
return getRefValue(ref,unwrappedConnection);
}
/**
* INTERNAL:
* Prints return keyword for StoredFunctionDefinition:
* CREATE FUNCTION StoredFunction_In (P_IN BIGINT)
* RETURN BIGINT
* The method was introduced because MySQL requires "RETURNS" instead:
* CREATE FUNCTION StoredFunction_In (P_IN BIGINT)
* RETURNS BIGINT
*/
public void printStoredFunctionReturnKeyWord(Writer writer) throws IOException {
writer.write("\n\t RETURN ");
}
/**
* INTERNAL:
* Print the SQL representation of the statement on a stream, storing the fields
* in the DatabaseCall.
*/
public void printSQLSelectStatement(DatabaseCall call, ExpressionSQLPrinter printer, SQLSelectStatement statement){
call.setFields(statement.printSQL(printer));
}
/**
* INTERNAL:
* Indicates whether locking clause should be printed after where clause by SQLSelectStatement.
* Example:
* on Oracle platform (method returns true):
* SELECT ADDRESS_ID, ... FROM ADDRESS WHERE (ADDRESS_ID = ?) FOR UPDATE
* on SQLServer platform (method returns false):
* SELECT ADDRESS_ID, ... FROM ADDRESS WITH (UPDLOCK) WHERE (ADDRESS_ID = ?)
*/
public boolean shouldPrintLockingClauseAfterWhereClause() {
return true;
}
/**
* INTERNAL:
* Indicates whether locking clause could be selectively applied only to some tables in a ReadQuery.
* Example: the following locks the rows in SALARY table, doesn't lock the rows in EMPLOYEE table:
* on Oracle platform (method returns true):
* SELECT t0.EMP_ID..., t1.SALARY FROM EMPLOYEE t0, SALARY t1 WHERE ... FOR UPDATE t1.SALARY
* on SQLServer platform (method returns true):
* SELECT t0.EMP_ID..., t1.SALARY FROM EMPLOYEE t0, SALARY t1 WITH (UPDLOCK) WHERE ...
*/
public boolean supportsIndividualTableLocking() {
return true;
}
/**
* INTERNAL:
* Indicates whether locking clause could be applied to the query that has more than one table
*/
public boolean supportsLockingQueriesWithMultipleTables() {
return true;
}
/**
* INTERNAL:
* Indicates whether locking OF clause should print alias for field.
* Example:
* on Oracle platform (method returns false):
* SELECT ADDRESS_ID, ... FROM ADDRESS T1 WHERE (T1.ADDRESS_ID = ?) FOR UPDATE OF T1.ADDRESS_ID
* on Postgres platform (method returns true):
* SELECT ADDRESS_ID, ... FROM ADDRESS T1 WHERE (T1.ADDRESS_ID = ?) FOR UPDATE OF T1
*/
public boolean shouldPrintAliasForUpdate() {
return false;
}
/**
* INTERNAL:
* Don't override this method.
*
* @param fullTableName
* qualified name of the table the index is to be created on
* @param indexName
* name of the index
* @param columnNames
* one or more columns the index is created for
*/
public String buildCreateIndex(String fullTableName, String indexName, String... columnNames) {
return buildCreateIndex(fullTableName, indexName, "", false, columnNames);
}
/**
* INTERNAL:
* Override this method with the platform's CREATE INDEX statement.
*
* @param fullTableName
* qualified name of the table the index is to be created on
* @param indexName
* name of the index
* @param qualifier
* qualifier to construct qualified name of index if needed
* @param isUnique
* Indicates whether unique index is created
* @param columnNames
* one or more columns the index is created for
*/
public String buildCreateIndex(String fullTableName, String indexName, String qualifier, boolean isUnique, String... columnNames) {
StringBuilder queryString = new StringBuilder();
if (isUnique) {
queryString.append("CREATE UNIQUE INDEX ");
} else {
queryString.append("CREATE INDEX ");
}
if (!qualifier.equals("")) {
queryString.append(qualifier).append(".");
}
queryString.append(indexName).append(" ON ").append(fullTableName).append(" (");
queryString.append(columnNames[0]);
for (int i = 1; i < columnNames.length; i++) {
queryString.append(", ").append(columnNames[i]);
}
queryString.append(")");
return queryString.toString();
}
/**
* INTERNAL:
* Don't override this method.
*
* @param fullTableName
* qualified name of the table the index is to be removed from
* @param indexName
* name of the index
*/
public String buildDropIndex(String fullTableName, String indexName) {
return buildDropIndex(fullTableName, indexName, "");
}
/**
* INTERNAL:
* Override this method with the platform's DROP INDEX statement.
*
* @param fullTableName
* qualified name of the table the index is to be removed from
* @param indexName
* name of the index
* @param qualifier
* qualifier to construct qualified name of index if needed
*/
public String buildDropIndex(String fullTableName, String indexName, String qualifier) {
StringBuilder queryString = new StringBuilder();
queryString.append("DROP INDEX ");
if (!qualifier.equals("")) {
queryString.append(qualifier).append(".");
}
queryString.append(indexName);
if (requiresTableInIndexDropDDL()) {
queryString.append(" ON ").append(fullTableName);
}
return queryString.toString();
}
/**
* INTERNAL:
* Returns sql used to create sequence object in the database.
*/
public Writer buildSequenceObjectCreationWriter(Writer writer, String fullSeqName, int increment, int start) throws IOException {
writer.write("CREATE SEQUENCE ");
writer.write(fullSeqName);
if (increment != 1) {
writer.write(" INCREMENT BY " + increment);
}
writer.write(" START WITH " + start);
return writer;
}
/**
* INTERNAL:
* Returns sql used to delete sequence object from the database.
*/
public Writer buildSequenceObjectDeletionWriter(Writer writer, String fullSeqName) throws IOException {
writer.write("DROP SEQUENCE ");
writer.write(fullSeqName);
return writer;
}
/**
* INTERNAL:
* Returns sql used to alter sequence object's increment in the database.
*/
public Writer buildSequenceObjectAlterIncrementWriter(Writer writer, String fullSeqName, int increment) throws IOException {
writer.write("ALTER SEQUENCE ");
writer.write(fullSeqName);
writer.write(" INCREMENT BY " + increment);
return writer;
}
/**
* INTERNAL:
* Override this method if the platform supports sequence objects
* and it's possible to alter sequence object's increment in the database.
*/
public boolean isAlterSequenceObjectSupported() {
return false;
}
/**
* INTERNAL:
* Return if nesting outer joins is supported, i.e. each join must be followed by the ON clause.
*/
public boolean supportsNestingOuterJoins() {
return true;
}
/**
* INTERNAL:
* Return if brackets can be used in the ON clause for outer joins.
*/
public boolean supportsOuterJoinsWithBrackets() {
return true;
}
/**
* INTERNAL:
* Used by some platforms during reading of ResultSet to free temporary objects.
*/
public void freeTemporaryObject(Object value) throws SQLException {
}
/**
* INTERNAL:
* Allow initialization from the connection.
*/
public void initializeConnectionData(Connection connection) throws SQLException {
}
/**
* INTERNAL:
* May need to override this method if the platform supports ALTER TABLE ADD &lt;column&gt;
* and the generated sql doesn't work.
* Write the string that follows ALTER TABLE to create a sql statement for
* the platform in order to append a new column to an existing table.
*/
public void writeAddColumnClause(Writer writer, AbstractSession session, TableDefinition table, FieldDefinition field) throws IOException {
writer.write("ADD ");
field.appendDBString(writer, session, table);
}
/**
* INTERNAL:
* Override this method if the platform supports storing JDBC connection user name during
* {@link #initializeConnectionData(Connection)}.
* @return Always returns {@code false}
*/
public boolean supportsConnectionUserName() {
return false;
}
/**
* INTERNAL:
* Returns user name retrieved from JDBC connection.
* @throws UnsupportedOperationException on every single call until overridden.
*/
public String getConnectionUserName() {
throw new UnsupportedOperationException("Connection user name is not supported.");
}
// Value of shouldCheckResultTableExistsQuery must be false.
/**
* INTERNAL:
* Returns query to check whether given table exists.
* Query execution throws an exception when no such table exists.
* @param table database table meta-data
* @return query to check whether given table exists
*/
protected DataReadQuery getTableExistsQuery(final TableDefinition table) {
final String sql = "SELECT 1 FROM " + table.getFullName();
final DataReadQuery query = new DataReadQuery(sql);
query.setMaxRows(1);
return query;
}
/**
* INTERNAL:
* Executes and evaluates query to check whether given table exists.
* Returned value is always {@code true}, because an exception is thrown
* when given table does not exists.
* @param session current database session
* @param table database table meta-data
* @param suppressLogging whether to suppress logging during query execution
* @return value of {@code true} if given table exists or {@code false} otherwise
*/
public boolean checkTableExists(final DatabaseSessionImpl session,
final TableDefinition table, final boolean suppressLogging) {
try {
session.setLoggingOff(suppressLogging);
session.executeQuery(getTableExistsQuery(table));
return true;
} catch (Exception notFound) {
return false;
}
}
}