blob: 12f48d0830e2e7c240a1f736e2b4f555344d44ac [file] [log] [blame]
/*
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2015 IBM Corporation.
* Copyright (c) 2010, 2015 Dies Koper (Fujitsu).
*
* 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:
// Created Feb 19, 2010 - Dies Koper (Fujitsu)
// bug 288715: Drop Table Restrictions: "table locked" errors when dropping
// tables in several Core and many JPA LRG tests on Symfoware.
// Jul 19, 2014 - Tomas Kraus (Oracle)
// bug 437578: Added few helper methods to simplify table builder methods.
// Jan 06, 2015 - Dalia Abo Sheasha (IBM)
// bug 454917: Moved a few helper methods from child class to be visible to all table creators.
package org.eclipse.persistence.testing.framework;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.tools.schemaframework.FieldDefinition;
import org.eclipse.persistence.tools.schemaframework.ForeignKeyConstraint;
import org.eclipse.persistence.tools.schemaframework.SchemaManager;
import org.eclipse.persistence.tools.schemaframework.TableCreator;
import org.eclipse.persistence.tools.schemaframework.TableDefinition;
/**
* Many JPA and a few Core tests use the same tables names, so at the start of
* the tests tables are dropped and recreated. If the tables were created in a
* previous test and the connection used to create it is still open, Symfoware
* complains that the tables are locked when it tries to drop them.
* <p>
*
* This class sets a flag before the subsequent recreation of the tables to
* delete the rows instead. The first time it is called it does not set the
* flag, to allow the tables to be initially created.
* <p>
*
* To enable this functionality, the system property
* "eclipselink.test.toggle-fast-table-creator" needs to be set to "true".
* <p>
*
* This class should be positioned between the test's table creator class and
* TableCreator by making the's test table creator extend this class.
*
* @author Dies Koper, Tomas Kraus
*
*/
public class TogglingFastTableCreator extends TableCreator {
/** Character to separate table name and column name in full column identifier. */
protected static final char TABLE_FIELD_SEPARATOR = '.';
protected static Set fastTableCreators = new HashSet();
protected static boolean useFastTableCreatorAfterInitialCreate = Boolean
.getBoolean("eclipselink.test.toggle-fast-table-creator");
/**
* Delegates to super's constructor.
*/
public TogglingFastTableCreator() {
super();
}
/**
* Delegates to super's constructor.
*/
public TogglingFastTableCreator(Vector tableDefinitions) {
super(tableDefinitions);
}
@Override
public void replaceTables(DatabaseSession session) {
// on Symfoware, to avoid table locking issues only the first invocation
// of an instance of this class (drops & re-)creates the tables
// if the system property is set.
session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: useFastTableCreatorAfterInitialCreate: "
+ useFastTableCreatorAfterInitialCreate);
boolean isFirstCreate = !isFastTableCreator();
session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: " + getTableCreatorName()
+ " - isFirstCreate: " + isFirstCreate);
session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: Current fastTableCreators: "
+ fastTableCreators);
if (useFastTableCreatorAfterInitialCreate && !isFirstCreate) {
session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: " + getTableCreatorName()
+ " - toggling true");
String sequenceTableName = getSequenceTableName(session);
List<TableDefinition> tables = getTableDefinitions();
for (TableDefinition table : tables) {
if (!table.getName().equals(sequenceTableName)) {
SchemaManager schemaManager = new SchemaManager(session);
AbstractSession abstarctSession = schemaManager.getSession();
try {
abstarctSession.priviledgedExecuteNonSelectingCall(new org.eclipse.persistence.queries.SQLCall("DELETE FROM " + table.getFullName()));
} catch (DatabaseException ex) {
//Ignore database exception. eg. If there is no table to delete, it gives database exception.
}
}
}
} else {
super.replaceTables(session);
}
// next time just delete the rows instead.
if (useFastTableCreatorAfterInitialCreate) {
setFastTableCreator();
session.getSessionLog().log(SessionLog.FINEST, "TogglingFastTableCreator: " + getTableCreatorName()
+ " added to fastTableCreators");
}
}
public boolean resetFastTableCreator() {
AbstractSessionLog.getLog().log(SessionLog.FINEST, "TogglingFastTableCreator: removing table creator: "
+ getTableCreatorName());
return fastTableCreators.remove(getTableCreatorName());
}
public boolean setFastTableCreator() {
AbstractSessionLog.getLog().log(SessionLog.FINEST, "TogglingFastTableCreator: adding table creator: "
+ getTableCreatorName());
return fastTableCreators.add(getTableCreatorName());
}
public boolean isFastTableCreator() {
return fastTableCreators.contains(getTableCreatorName());
}
public String getTableCreatorName() {
return this.getClass().getName();
}
/**
* Helper method to concatenate table name and column name into full column name.
* @param tableName Table name.
* @param columnName Column name.
* @return Full column name (prefixed with table name).
*/
protected static String buildFullColumnName(
final String tableName, final String columnName) {
final StringBuilder sb = new StringBuilder(tableName.length() + columnName.length() + 1);
sb.append(tableName).append(TABLE_FIELD_SEPARATOR).append(columnName);
return sb.toString();
}
/**
* Helper method to create {@link TableDefinition} instance with given table name.
* @param name Table name.
* @return {@link TableDefinition} instance with name set.
*/
protected static TableDefinition createTable(final String name) {
final TableDefinition table = new TableDefinition();
table.setName(name);
return table;
}
/**
* Helper method to create {@link FieldDefinition} instance for unique
* numeric primary key with given name and size.
* @param name Column name.
* @param size Column numeric type size.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createNumericPk(
final String name, final int size) {
final FieldDefinition field = new FieldDefinition();
field.setName(name);
field.setTypeName("NUMERIC");
field.setSize(size);
field.setShouldAllowNull(false);
field.setIsPrimaryKey(true);
field.setUnique(true);
field.setIsIdentity(true);
return field;
}
/**
* Helper method to create {@link FieldDefinition} instance for unique
* numeric primary key with given name and default size of {@code 15}.
* @param name Column name.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createNumericPk(final String name) {
return createNumericPk(name, 15);
}
/**
* Helper method to create {@link FieldDefinition} instance for
* numeric foreign key with given name, size and foreign key name.
* @param name Column name.
* @param size Column numeric type size.
* @param fkName Foreign key name (e.g. {@code "MY_TABLE.ID"}.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createNumericFk(
final String name, final int size, final String fkName) {
final FieldDefinition field = new FieldDefinition();
field.setName(name);
field.setTypeName("NUMERIC");
field.setSize(size);
field.setShouldAllowNull(false);
field.setIsPrimaryKey(false);
field.setUnique(false);
field.setIsIdentity(false);
field.setForeignKeyFieldName(fkName);
return field;
}
/**
* Helper method to create {@link FieldDefinition} instance for
* numeric foreign key with given name, foreign key name and default size
* of {@code 15}.
* @param name Column name.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createNumericFk(
final String name, final String fkName) {
return createNumericFk(name, 15, fkName);
}
/**
* Helper method to create {@link FieldDefinition} instance for numeric column
* with given name and size and without any additional constraints.
* @param name Column name.
* @param size Column numeric type size.
* @param allowNull Allow {@code null} values for column.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createNumericColumn(
final String name, final int size, final boolean allowNull) {
final FieldDefinition field = new FieldDefinition();
field.setName(name);
field.setTypeName("NUMERIC");
field.setSize(size);
field.setShouldAllowNull(allowNull);
field.setIsPrimaryKey(false);
field.setUnique(false);
field.setIsIdentity(false);
return field;
}
/**
* Helper method to create {@link FieldDefinition} instance for numeric column
* with given name, size of {@code 15}, with {@code null} value allowed and
* without any additional constraints.
* @param name Column name.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createNumericColumn(
final String name) {
return createNumericColumn(name, 15, true);
}
/**
* Helper method to create {@link FieldDefinition} instance for <code>DTYPE</code>
* column used for inheritance in model.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createDTypeColumn() {
final FieldDefinition field = new FieldDefinition();
field.setName("DTYPE");
field.setTypeName("VARCHAR2");
field.setSize(15);
field.setSubSize(0);
field.setIsPrimaryKey(false);
field.setIsIdentity(false);
field.setUnique(false);
field.setShouldAllowNull(true);
return field;
}
/**
* Helper method to create {@link FieldDefinition} instance for {@link String} column
* with given name and size and without any additional constraints.
* @param name Column name.
* @param size Column numeric type size.
* @param allowNull Allow {@code null} values for column.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createStringColumn(
final String name, final int size, final boolean allowNull) {
final FieldDefinition field = new FieldDefinition();
field.setName(name);
field.setTypeName("VARCHAR");
field.setSize(size);
field.setShouldAllowNull(allowNull);
field.setIsPrimaryKey(false);
field.setUnique(false);
field.setIsIdentity(false);
return field;
}
/**
* Helper method to create {@link FieldDefinition} instance for {@link String} column
* with given name size of {@code 32}, with {@code null} value allowed and
* without any additional constraints.
* @param name Column name.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createStringColumn(
final String name) {
return createStringColumn(name, 32, true);
}
/**
* Helper method to create {@link FieldDefinition} instance
* for {@link java.util.Date} column with given name and size and without
* any additional constraints.
* @param name Column name.
* @param size Column date size.
* @param allowNull Allow {@code null} values for column.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createDateColumn(
final String name, final int size, final boolean allowNull) {
final FieldDefinition field = new FieldDefinition();
field.setName(name);
field.setTypeName("DATE");
field.setSize(size);
field.setShouldAllowNull(allowNull);
field.setIsPrimaryKey(false);
field.setUnique(false);
field.setIsIdentity(false);
return field;
}
/**
* Helper method to create {@link FieldDefinition} instance
* for {@link java.util.Date} column with given name size of {@code 23},
* with {@code null} value allowed and without any additional constraints.
* @param name Column name.
* @return Initialized {@link FieldDefinition} instance.
*/
protected static FieldDefinition createDateColumn(
final String name) {
return createDateColumn(name, 23, true);
}
protected void adjustForeignKeyFieldTypes(DatabaseSession session) {
for (TableDefinition sourceTableDefinition : getTableDefinitions() ) {
for (FieldDefinition sourceFieldDefinition : sourceTableDefinition.getFields()) { // see TableDefinition, l.789
if (sourceFieldDefinition.getForeignKeyFieldName() != null) {
// We need to build each foreign key constraint on the fly, because TableDefinition.buildFieldTypes() has not been called yet
ForeignKeyConstraint foreignKeyConstraint = buildForeignKeyConstraint(sourceFieldDefinition, session.getPlatform());
// Assume only one of each (as in Field Defintion)
String sourceFieldName = foreignKeyConstraint.getSourceFields().get(0);
String targetFieldName = foreignKeyConstraint.getTargetFields().get(0);
// Find the target table and the target field
TableDefinition targetTableDefinition = getTableDefinition(foreignKeyConstraint.getTargetTable());
// session.getSessionLog().log(SessionLog.FINEST, "AdvancedTableCreator: Found target table " + foreignKeyConstraint.getTargetTable() + " for foreign key " + foreignKeyConstraint.getName());
FieldDefinition targetFieldDefinition = getFieldDefinition(targetTableDefinition, targetFieldName);
// session.getSessionLog().log(SessionLog.FINEST, "AdvancedTableCreator: Found target field " + targetFieldDefinition.getName() + " in table " + targetTableDefinition.getName());
// Only change source column if target is identity
String qualifiedName = targetTableDefinition.getFullName() + '.' + targetFieldDefinition.getName();
if (targetFieldDefinition.isIdentity() && session.getPlatform().shouldPrintFieldIdentityClause((AbstractSession)session, qualifiedName)) {
session.getSessionLog().log(SessionLog.FINEST, "AdvancedTableCreator.adjustForeignKeyFieldTypes(): Changing data type of source field " + sourceFieldDefinition.getName() + "to INTEGER in table " + sourceTableDefinition.getName());
sourceFieldDefinition.setTypeName("INTEGER");
sourceFieldDefinition.setSize(0);
}
}
}
}
}
// Helper methods for adjustForeignKeyFieldTypes()
private FieldDefinition getFieldDefinition(TableDefinition tableDefinition, String fieldName) {
for (FieldDefinition targetField : tableDefinition.getFields()) { // see TableDefinition, l.351
if (targetField.getName().equals(fieldName)) {
return targetField;
}
}
return null;
}
private TableDefinition getTableDefinition(String tableName) {
for (TableDefinition targetTable : getTableDefinitions()) { // see TableCreator, l.87
if (targetTable.getName().equals(tableName)) {
return targetTable;
}
}
return null;
}
// Mostly cloned from TableDefinition.buildForeignKeyConstraint()
private ForeignKeyConstraint buildForeignKeyConstraint(FieldDefinition field, DatabasePlatform platform) {
Vector sourceFields = new Vector();
Vector targetFields = new Vector();
ForeignKeyConstraint fkConstraint = new ForeignKeyConstraint();
DatabaseField tempTargetField = new DatabaseField(field.getForeignKeyFieldName());
DatabaseField tempSourceField = new DatabaseField(field.getName());
sourceFields.add(tempSourceField.getName());
targetFields.add(tempTargetField.getName());
fkConstraint.setSourceFields(sourceFields);
fkConstraint.setTargetFields(targetFields);
fkConstraint.setTargetTable(tempTargetField.getTable().getQualifiedNameDelimited(platform));
return fkConstraint;
}
}