blob: 63078bd0b4e17672bd1a8b6ca4d2f19cc7b68b1d [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:
// Mike Norman: Jan 2012 - Initial implementation
package org.eclipse.persistence.tools.dbws.oracle;
import static org.eclipse.persistence.internal.helper.Helper.NL;
import static org.eclipse.persistence.tools.dbws.Util.COMMA;
import static org.eclipse.persistence.tools.dbws.Util.FLOAT_STR;
import static org.eclipse.persistence.tools.dbws.Util.INTEGER_STR;
import static org.eclipse.persistence.tools.dbws.Util.NUMERIC_STR;
import static org.eclipse.persistence.tools.dbws.Util.OTHER_STR;
import static org.eclipse.persistence.tools.dbws.Util.PERCENT;
import static org.eclipse.persistence.tools.dbws.Util.SEMICOLON;
import static org.eclipse.persistence.tools.dbws.Util.SINGLE_SPACE;
import static org.eclipse.persistence.tools.dbws.Util.UNDERSCORE;
import static org.eclipse.persistence.tools.dbws.Util.getJDBCTypeFromTypeName;
import static org.eclipse.persistence.tools.dbws.Util.getJDBCTypeNameFromType;
//javase imports
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
//EclipseLink imports
import org.eclipse.persistence.tools.oracleddl.metadata.CharType;
import org.eclipse.persistence.tools.oracleddl.metadata.CompositeDatabaseTypeWithEnclosedType;
import org.eclipse.persistence.tools.oracleddl.metadata.DatabaseType;
import org.eclipse.persistence.tools.oracleddl.metadata.FieldType;
import org.eclipse.persistence.tools.oracleddl.metadata.NumericType;
import org.eclipse.persistence.tools.oracleddl.metadata.PLSQLCollectionType;
import org.eclipse.persistence.tools.oracleddl.metadata.PLSQLPackageType;
import org.eclipse.persistence.tools.oracleddl.metadata.PLSQLRecordType;
import org.eclipse.persistence.tools.oracleddl.metadata.PLSQLType;
import org.eclipse.persistence.tools.oracleddl.metadata.PrecisionType;
import org.eclipse.persistence.tools.oracleddl.metadata.ROWTYPEType;
import org.eclipse.persistence.tools.oracleddl.metadata.RawType;
import org.eclipse.persistence.tools.oracleddl.metadata.ScalarDatabaseTypeEnum;
import org.eclipse.persistence.tools.oracleddl.metadata.SizedType;
import org.eclipse.persistence.tools.oracleddl.metadata.TYPEType;
import org.eclipse.persistence.tools.oracleddl.metadata.TableType;
import org.eclipse.persistence.tools.oracleddl.metadata.VarChar2Type;
import org.eclipse.persistence.tools.oracleddl.metadata.VarCharType;
import org.eclipse.persistence.tools.oracleddl.metadata.visit.BaseDatabaseTypeVisitor;
public class ShadowDDLGenerator {
static final String ROWTYPE_PREFIX = "_ROWTYPE_SQL";
protected PLSQLPackageType plsqlPackageType;
protected Map<String, Integer> rowTYPETypeCounts;
protected Map<CompositeDatabaseTypeWithEnclosedType, DDLWrapper> createDDLs =
new HashMap<CompositeDatabaseTypeWithEnclosedType, DDLWrapper>();
protected Map<CompositeDatabaseTypeWithEnclosedType, DDLWrapper> dropDDLs =
new HashMap<CompositeDatabaseTypeWithEnclosedType, DDLWrapper>();
public ShadowDDLGenerator(PLSQLPackageType plsqlPackageType) {
this.plsqlPackageType = plsqlPackageType;
//count all the %ROWTYPE's and assign unique number
CountROWTYPETypesVisitor countVisitor = new CountROWTYPETypesVisitor();
plsqlPackageType.accept(countVisitor);
rowTYPETypeCounts = countVisitor.getRowTYPETypeCounts();
// visit all the PLSQLType's
DDLWrapperGeneratorVisitor generatorVisitor = new DDLWrapperGeneratorVisitor();
plsqlPackageType.accept(generatorVisitor);
}
public String getCreateDDLFor(CompositeDatabaseTypeWithEnclosedType dType) {
return createDDLs.get(dType).ddl;
}
public List<String> getAllCreateDDLs() {
List<String> allDDLs = new ArrayList<String>();
for (Map.Entry<CompositeDatabaseTypeWithEnclosedType, DDLWrapper> me : createDDLs.entrySet()) {
allDDLs.add(me.getValue().ddl);
}
return allDDLs;
}
public String getDropDDLFor(CompositeDatabaseTypeWithEnclosedType dType) {
return dropDDLs.get(dType).ddl;
}
public List<String> getAllDropDDLs() {
List<String> allDDLs = new ArrayList<String>();
for (Map.Entry<CompositeDatabaseTypeWithEnclosedType, DDLWrapper> me : dropDDLs.entrySet()) {
allDDLs.add(me.getValue().ddl);
}
return allDDLs;
}
protected String getShadowROWTYPETypeName(ROWTYPEType rowTYPEType) {
return rowTYPEType.getTypeName().replace(PERCENT, UNDERSCORE);
}
static class CountROWTYPETypesVisitor extends BaseDatabaseTypeVisitor {
Map<String, Integer> rowTYPETypeCounts = new HashMap<String, Integer>();
int initialRowTYPETypeCount = 0;
@Override
public void beginVisit(ROWTYPEType rowTYPEType) {
Integer rowTYPETypeCount = rowTYPETypeCounts.get(rowTYPEType.getTypeName());
if (rowTYPETypeCount == null) {
rowTYPETypeCounts.put(rowTYPEType.getTypeName(), initialRowTYPETypeCount++);
}
}
public Map<String, Integer> getRowTYPETypeCounts() {
return rowTYPETypeCounts;
}
}
class DDLWrapperGeneratorVisitor extends BaseDatabaseTypeVisitor {
static final String COR_TYPE = "CREATE OR REPLACE TYPE ";
static final String DROP_TYPE = "DROP TYPE ";
static final String FORCE = " FORCE";
static final String LBRACKET = "(";
static final String RBRACKET = ")";
static final String SPACES = " ";
static final String AS_OBJECT = " AS OBJECT (";
static final String AS_TABLE_OF = " AS TABLE OF ";
static final String END_OBJECT = "\n);";
static final String NUMBER_STR = "NUMBER";
static final String ROWID_STR = "VARCHAR2(256)";
static final String NUMBER_DEFAULTSIZE = "38";
static final String FLOAT_DEFAULTSIZE = "63";
static final String DOUBLE_DEFAULTSIZE = "126";
static final String LONG_DEFAULTSIZE = "32760";
protected Stack<PLSQLRecordType> recordTypes = new Stack<PLSQLRecordType>();
protected Stack<ROWTYPEType> rowTYPETypes = new Stack<ROWTYPEType>();
@Override
public void beginVisit(PLSQLRecordType recordType) {
if (recordTypes.isEmpty()) {
recordTypes.push(recordType);
}
else if (!recordTypes.peek().equals(recordType)) {
recordTypes.push(recordType);
}
DDLWrapper ddlWrapper = createDDLs.get(recordType);
if (ddlWrapper == null) {
ddlWrapper = new DDLWrapper();
ddlWrapper.ddl = COR_TYPE + recordType.getParentType().getPackageName() + UNDERSCORE +
recordType.getTypeName().toUpperCase() + AS_OBJECT;
createDDLs.put(recordType, ddlWrapper);
}
}
@Override
public void endVisit(PLSQLRecordType recordType) {
recordTypes.pop();
DDLWrapper ddlWrapper = createDDLs.get(recordType);
if (ddlWrapper != null && !ddlWrapper.finished) {
ddlWrapper.ddl += END_OBJECT;
ddlWrapper.finished = true;
}
ddlWrapper = dropDDLs.get(recordType);
if (ddlWrapper == null) {
ddlWrapper = new DDLWrapper();
ddlWrapper.ddl = DROP_TYPE + recordType.getParentType().getPackageName() + UNDERSCORE +
recordType.getTypeName().toUpperCase() + FORCE + SEMICOLON;
ddlWrapper.finished = true;
dropDDLs.put(recordType, ddlWrapper);
}
}
@Override
public void beginVisit(FieldType fieldType) {
//skip fields from ObjectType's
if (!recordTypes.isEmpty()) {
PLSQLRecordType currentRecordType = recordTypes.peek();
if (currentRecordType != null) {
DDLWrapper ddlWrapper = createDDLs.get(currentRecordType);
if (!ddlWrapper.finished) {
String fieldName = fieldType.getFieldName();
for (int i = 0, len = currentRecordType.getFields().size(); i < len; i++) {
FieldType f = currentRecordType.getFields().get(i);
if (fieldName.equals(f.getFieldName())) {
ddlWrapper.ddl += SPACES + fieldName + SINGLE_SPACE;
DatabaseType fieldDataType = f.getEnclosedType();
String fieldShadowName = null;
if (fieldDataType.isPLSQLType()) {
fieldShadowName = ((PLSQLType)fieldDataType).getParentType().
getPackageName() + UNDERSCORE + fieldDataType.
getTypeName().toUpperCase();
} else if (fieldDataType.isTYPEType()) {
fieldShadowName = getShadowTypeName(fieldDataType);
TYPEType tType = (TYPEType) fieldDataType;
if (tType.getEnclosedType().isFieldType()) {
FieldType fType = (FieldType) tType.getEnclosedType();
if (fType.getEnclosedType().isPrecisionType()) {
PrecisionType pType = (PrecisionType) fType.getEnclosedType();
long precision = pType.getPrecision();
if (precision != pType.getDefaultPrecision()) {
long scale = pType.getScale();
if (scale != 0) {
fieldShadowName = fieldShadowName + LBRACKET + precision + COMMA + scale + RBRACKET;
} else {
fieldShadowName = fieldShadowName + LBRACKET + precision + RBRACKET;
}
}
} else if (fType.getEnclosedType().isSizedType()) {
SizedType sType = (SizedType) fType.getEnclosedType();
fieldShadowName = fieldShadowName + LBRACKET + sType.getSize() + RBRACKET;
}
}
} else {
fieldShadowName = getShadowTypeName(fieldDataType);
}
ddlWrapper.ddl += fieldShadowName;
if (i < len -1) {
ddlWrapper.ddl += COMMA + NL;
}
break;
}
}
}
}
}
}
@Override
public void beginVisit(PLSQLCollectionType collectionType) {
DDLWrapper ddlWrapper = createDDLs.get(collectionType);
if (ddlWrapper == null) {
ddlWrapper = new DDLWrapper();
}
if (!ddlWrapper.finished) {
String shadowNestedTypeName = null;
DatabaseType nestedType = collectionType.getEnclosedType();
if (nestedType.isNumericType()) {
NumericType nDataType = (NumericType)nestedType;
if (nDataType.isNumberSynonym()) {
shadowNestedTypeName = NUMBER_STR;
}
else {
shadowNestedTypeName = NUMERIC_STR;
}
PrecisionType pDataType = (PrecisionType)nestedType;
long defaultPrecision = pDataType.getDefaultPrecision();
long precision = pDataType.getPrecision();
long scale = pDataType.getScale();
if (precision != defaultPrecision) {
shadowNestedTypeName += LBRACKET + precision;
if (scale != 0) {
shadowNestedTypeName += COMMA + SINGLE_SPACE + scale;
}
shadowNestedTypeName += RBRACKET;
}
}
else {
shadowNestedTypeName = getShadowTypeName(nestedType);
}
ddlWrapper.ddl = COR_TYPE + collectionType.getParentType().getPackageName() +
UNDERSCORE + collectionType.getTypeName().toUpperCase() + AS_TABLE_OF +
shadowNestedTypeName + SEMICOLON;
ddlWrapper.finished = true;
createDDLs.put(collectionType, ddlWrapper);
}
}
@Override
public void endVisit(PLSQLCollectionType collectionType) {
DDLWrapper ddlWrapper = new DDLWrapper();
ddlWrapper.ddl = DROP_TYPE + collectionType.getParentType().getPackageName() + UNDERSCORE +
collectionType.getTypeName().toUpperCase() + FORCE + SEMICOLON;
ddlWrapper.finished = true;
dropDDLs.put(collectionType, ddlWrapper);
}
@Override
public void beginVisit(ROWTYPEType rowTYPEType) {
if (rowTYPETypes.isEmpty()) {
rowTYPETypes.push(rowTYPEType);
}
else if (!rowTYPETypes.peek().equals(rowTYPEType)) {
rowTYPETypes.push(rowTYPEType);
}
DDLWrapper ddlWrapper = createDDLs.get(rowTYPEType);
if (ddlWrapper == null) {
ddlWrapper = new DDLWrapper();
ddlWrapper.ddl = COR_TYPE + getShadowROWTYPETypeName(rowTYPEType) + AS_OBJECT;
//manually 'crawl' thru %ROWTYPE to TableType to columns and build up type fields
DatabaseType enclosedType = rowTYPEType.getEnclosedType();
if (enclosedType.isTableType()) {
TableType tableType = (TableType)enclosedType;
ddlWrapper.ddl += NL;
for (int i = 0, len = tableType.getColumns().size(); i < len; i++) {
FieldType f = tableType.getColumns().get(i);
ddlWrapper.ddl += SPACES + f.getFieldName() + SINGLE_SPACE;
DatabaseType fieldDataType = f.getEnclosedType();
String fieldShadowName = getShadowTypeName(fieldDataType);
ddlWrapper.ddl += fieldShadowName;
if (i < len -1) {
ddlWrapper.ddl += COMMA + NL;
}
}
createDDLs.put(rowTYPEType, ddlWrapper);
}
/*
else {
//Huh? a %ROWTYPE that points to something other than a TableType?
//ignore for now
}
*/
}
}
@Override
public void endVisit(ROWTYPEType rowTYPEType) {
rowTYPETypes.pop();
DDLWrapper ddlWrapper = createDDLs.get(rowTYPEType);
if (ddlWrapper != null && !ddlWrapper.finished) {
ddlWrapper.ddl += END_OBJECT;
ddlWrapper.finished = true;
}
ddlWrapper = dropDDLs.get(rowTYPEType);
if (ddlWrapper == null) {
ddlWrapper = new DDLWrapper();
ddlWrapper.ddl = DROP_TYPE + getShadowROWTYPETypeName(rowTYPEType) + FORCE + SEMICOLON;
ddlWrapper.finished = true;
dropDDLs.put(rowTYPEType, ddlWrapper);
}
}
protected String getShadowTypeName(DatabaseType dataType ) {
String shadowTypeName = null;
if (dataType == ScalarDatabaseTypeEnum.INTEGER_TYPE ||
dataType == ScalarDatabaseTypeEnum.SMALLINT_TYPE) {
shadowTypeName = NUMBER_STR + LBRACKET + NUMBER_DEFAULTSIZE + RBRACKET;
}
else if (dataType == ScalarDatabaseTypeEnum.BINARY_INTEGER_TYPE ||
dataType == ScalarDatabaseTypeEnum.BOOLEAN_TYPE ||
dataType == ScalarDatabaseTypeEnum.NATURAL_TYPE ||
dataType == ScalarDatabaseTypeEnum.PLS_INTEGER_TYPE ||
dataType == ScalarDatabaseTypeEnum.POSITIVE_TYPE ||
dataType == ScalarDatabaseTypeEnum.SIGN_TYPE ||
dataType == ScalarDatabaseTypeEnum.SIMPLE_INTEGER_TYPE) {
shadowTypeName = INTEGER_STR;
}
else if (dataType == ScalarDatabaseTypeEnum.ROWID_TYPE) {
shadowTypeName = ROWID_STR;
}
else if (dataType == ScalarDatabaseTypeEnum.SIMPLE_DOUBLE_TYPE) {
shadowTypeName = ScalarDatabaseTypeEnum.BINARY_DOUBLE_TYPE.getTypeName();
}
else if (dataType == ScalarDatabaseTypeEnum.SIMPLE_FLOAT_TYPE) {
shadowTypeName = ScalarDatabaseTypeEnum.BINARY_FLOAT_TYPE.getTypeName();
}
else if (dataType.isPrecisionType()) {
PrecisionType pDataType = (PrecisionType)dataType;
long defaultPrecision = pDataType.getDefaultPrecision();
String defaultPrecisionStr = NUMBER_DEFAULTSIZE;
if (dataType.isNumericType() || dataType.isDecimalType()) {
shadowTypeName = NUMBER_STR;
}
else {
shadowTypeName = FLOAT_STR;
defaultPrecisionStr = FLOAT_DEFAULTSIZE;
if (dataType.isFloatType() || dataType.isDoubleType()) {
defaultPrecisionStr = DOUBLE_DEFAULTSIZE;
}
}
if (!(dataType.isNumericType() && ((NumericType)dataType).isNumberSynonym())) {
long precision = pDataType.getPrecision();
long scale = pDataType.getScale();
if (precision != defaultPrecision) {
shadowTypeName += LBRACKET + precision;
if (scale != 0) {
shadowTypeName += COMMA + SINGLE_SPACE + scale;
}
}
else {
shadowTypeName += LBRACKET + defaultPrecisionStr;
}
shadowTypeName += RBRACKET;
}
}
else if (dataType.isVarCharType()) {
shadowTypeName = VarChar2Type.TYPENAME;
VarCharType vcharType = (VarCharType)dataType;
long defaultSize = vcharType.getDefaultSize();
long size = vcharType.getSize();
String sizeStr = Long.toString(size);
if (dataType.isLongType()) {
shadowTypeName = VarChar2Type.TYPENAME;
if (size == defaultSize) {
sizeStr = LONG_DEFAULTSIZE;
}
}
shadowTypeName += LBRACKET + sizeStr + RBRACKET;
}
else if (dataType.isCharType()) {
shadowTypeName = CharType.TYPENAME;
long size = ((CharType)dataType).getSize();
shadowTypeName += LBRACKET + size + RBRACKET;
}
else if (dataType.isSizedType()) {
shadowTypeName = dataType.getTypeName();
SizedType sDataType = (SizedType)dataType;
long defaultSize = sDataType.getDefaultSize();
String sizeStr = null;
long size = sDataType.getSize();
if (dataType.isLongType()) {
shadowTypeName = VarChar2Type.TYPENAME;
if (size == defaultSize) {
sizeStr = LONG_DEFAULTSIZE;
}
else {
sizeStr = Long.toString(size);
}
shadowTypeName += LBRACKET + sizeStr + RBRACKET;
}
else if (dataType.isLongRawType()) {
shadowTypeName = RawType.TYPENAME;
if (size == defaultSize) {
sizeStr = LONG_DEFAULTSIZE;
}
else {
sizeStr = Long.toString(size);
}
shadowTypeName += LBRACKET + sizeStr + RBRACKET;
}
else if (size != defaultSize) {
shadowTypeName += LBRACKET + size + RBRACKET;
}
}
else if (dataType.isPLSQLType()) {
shadowTypeName = ((PLSQLType)dataType).getParentType().getPackageName() +
UNDERSCORE + dataType.getTypeName();
}
else {
String typeName = dataType.getTypeName();
int typ = getJDBCTypeFromTypeName(typeName);
shadowTypeName = getJDBCTypeNameFromType(typ);
if (OTHER_STR.equals(shadowTypeName)) {
shadowTypeName = typeName;
}
}
return shadowTypeName;
}
}
static class DDLWrapper {
String ddl;
boolean finished = false;
@Override
public String toString() {
return ddl;
}
}
}