blob: 08165ff26cc163b12fb2631a0494141b221ad714 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 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
package org.eclipse.persistence.testing.tests.returning;
import java.util.*;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.sequencing.NativeSequence;
import org.eclipse.persistence.sessions.*;
import org.eclipse.persistence.descriptors.ReturningPolicy;
import org.eclipse.persistence.queries.DataModifyQuery;
import org.eclipse.persistence.tools.schemaframework.SequenceObjectDefinition;
import org.eclipse.persistence.testing.framework.*;
import org.eclipse.persistence.tools.schemaframework.SchemaManager;
/**
* This adapter creates INSERT trigger for the original model's tables
* that return a generated sequence value, and modifies the descriptors
* to use a ReturningPolicy to assign this sequence value.
*/
public class SubstituteSequencingWithReturningPolicyAdapter implements ProjectAndDatabaseAdapter {
public SubstituteSequencingWithReturningPolicyAdapter() {
this(true);
}
/**
* If isReadOnly parameter is false, then the PK value will not be generated by the trigger if a non-null
* value is passed in insert, otherwise the passed value is ignored.
*/
public SubstituteSequencingWithReturningPolicyAdapter(boolean isReadOnly) {
this(isReadOnly, true);
}
/**
* useExistingSequenceName parameter indicates whether to use the sequence name used by the project
* or just create sequence name using table name.
* For instance, for Employee class mapped to EMPLOYEE table and with sequenceNumberName==EMP_SEQ,
* useExistingSequenceName==true causes EMP_SEQ to be used
* as an Oracle sequence name, otherwise EMPLOYEE_SEQ name is used (tableName +"_SEQ").
*/
public SubstituteSequencingWithReturningPolicyAdapter(boolean isReadOnly, boolean useExistingSequenceName) {
this.isReadOnly = isReadOnly;
tableToField = new Hashtable();
if (useExistingSequenceName) {
tableToSequence = new Hashtable();
}
}
protected Hashtable tableToField;
protected Hashtable tableToSequence;
protected boolean isReadOnly;
public boolean usesExistingSequenceNames() {
return tableToSequence != null;
}
@Override
public boolean isOriginalSetupRequired() {
return true;
}
@Override
public void updateProject(Project project, Session session) {
Iterator<ClassDescriptor> it = project.getDescriptors().values().iterator();
while (it.hasNext()) {
ClassDescriptor desc = it.next();
if (desc.isAggregateDescriptor()) {
continue;
}
String sequenceName = desc.getSequenceNumberName();
String fieldName = desc.getSequenceNumberFieldName();
if (fieldName != null && sequenceName != null) {
String tableName = desc.getTableName();
int indexOfDot = fieldName.indexOf('.');
if (indexOfDot != -1) {
fieldName = fieldName.substring(indexOfDot + 1);
}
tableToField.put(tableName, fieldName);
if (tableToSequence != null) {
tableToSequence.put(tableName, sequenceName);
}
// remove sequencing from descriptor
desc.setSequenceNumberFieldName(null);
desc.setSequenceNumberName(null);
// add ReturningPolicy
String fieldQualifiedName = tableName + '.' + fieldName;
desc.setReturningPolicy(new ReturningPolicy());
if (isReadOnly) {
desc.getReturningPolicy().addFieldForInsertReturnOnly(fieldQualifiedName);
} else {
desc.getReturningPolicy().addFieldForInsert(fieldQualifiedName);
}
}
}
}
@Override
public void updateDatabase(Session session) {
try {
createSequences(session);
replaceOrCreateTriggers(session);
} finally {
clear();
}
}
public void createSequences(Session session) {
if (!session.getPlatform().supportsSequenceObjects()) {
throw new TestWarningException("Requires database platform that supports sequence objects (like Oracle) - they will be used by triggers");
}
SchemaManager schemaManager = new SchemaManager((DatabaseSession)session);
Hashtable sequenceNameToDefinition = new Hashtable();
Enumeration tableNames = tableToField.keys();
while (tableNames.hasMoreElements()) {
String tableName = (String)tableNames.nextElement();
String sequenceName = getSequenceNameFromTableName(tableName);
if (!sequenceNameToDefinition.containsKey(sequenceName)) {
SequenceObjectDefinition definition = new SequenceObjectDefinition(new NativeSequence(sequenceName, 1, false));
sequenceNameToDefinition.put(sequenceName, definition);
schemaManager.createObject(definition);
}
}
}
public void replaceOrCreateTriggers(Session session) {
if (!session.getPlatform().isOracle()) {
throw new TestWarningException("Currently supports Oracle platform only");
}
Enumeration tableNames = tableToField.keys();
while (tableNames.hasMoreElements()) {
String tableName = (String)tableNames.nextElement();
String fieldName = (String)tableToField.get(tableName);
String sequenceName = getSequenceNameFromTableName(tableName);
String triggerName = getTriggerNameFromTableName(tableName);
String strCommand = "CREATE OR REPLACE TRIGGER " + triggerName + " BEFORE INSERT ON " + tableName + " FOR EACH ROW ";
String strBegin = "BEGIN\n";
String strIf = " IF (:new." + fieldName + " IS NULL) OR (:new." + fieldName + " = 0) THEN\n ";
String strSeq = " SELECT " + sequenceName + ".NEXTVAL INTO :new." + fieldName + " FROM DUAL;\n";
String strEndIf = " END IF;\n";
String strEnd = "END;";
String str;
if (isReadOnly) {
str = strCommand + strBegin + strSeq + strEnd;
} else {
str = strCommand + strBegin + strIf + strSeq + strEndIf + strEnd;
}
execute(session, str, true);
}
}
public Map getTableToField() {
return this.tableToField;
}
public Map getTableToSequence() {
return this.tableToSequence;
}
protected String getSequenceNameFromTableName(String tableName) {
String sequenceName;
if (tableToSequence != null) {
sequenceName = (String)tableToSequence.get(tableName);
} else {
sequenceName = tableName + "_SEQ";
}
return sequenceName;
}
protected String getTriggerNameFromTableName(String tableName) {
return tableName + "_TRIGGER";
}
protected void execute(Session session, String str, boolean shouldThrowException) {
try {
DataModifyQuery query = new DataModifyQuery(str);
query.setShouldBindAllParameters(false);
session.executeQuery(query);
} catch (Exception e) {
if (shouldThrowException) {
throw new TestErrorException("FAILED: " + str, e);
}
}
}
protected void clear() {
tableToField.clear();
if (tableToSequence != null) {
tableToSequence.clear();
}
}
}