blob: 7b874977ba5cdda19e9794f4e2604ba91874039f [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 - from Proof-of-concept, become production code
package dbws.testing.shadowddlgeneration.oldjpub;
//javase imports
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//EclipseLink imports
import dbws.testing.shadowddlgeneration.oldjpub.PublisherException;
import dbws.testing.shadowddlgeneration.oldjpub.Util;
import dbws.testing.shadowddlgeneration.oldjpub.ViewCache;
import static dbws.testing.shadowddlgeneration.oldjpub.Util.SCHEMA_IF_NEEDED;
import static dbws.testing.shadowddlgeneration.oldjpub.Util.printTypeWithLength;
public class SqlType extends TypeClass {
// The following type constants are used by the database
// to distinguish the various Opaque and SQLJ Object type
// flavours
public static final int CODE_OPAQUE = 58;
public static final int CODE_SQLJTYPE = 108;
public static final int SQLJTYPE_SQLDATA = 1;
public static final int SQLJTYPE_CUSTOMDATUM = 2;
public static final int SQLJTYPE_SERIALIZABLE = 3;
public static final int SQLJTYPE_ORADATA = 5;
public static final int SQLJTYPE_BOTH = 6;
public static final int SQLJTYPE_BOTH8I = 7;
// The following type constants are internal to JDBC
public static final int ORACLE_TYPES_NCHAR = -72054;
public static final int ORACLE_TYPES_NCLOB = -72055;
public static final int ORACLE_TYPES_BOOLEAN = -72056;
public static final int ORACLE_TYPES_TBD = -72057;
protected String m_version;
protected SqlReflector m_reflector; // null for predefined types
protected ViewCache m_viewCache;
protected SqlType m_parentType;
protected boolean m_isReused;
public SqlName getSqlName() {
return (SqlName)m_name;
}
public boolean isRef() {
return getTypecode() == OracleTypes.REF;
}
public boolean isCollection() {
return (getTypecode() == OracleTypes.ARRAY || getTypecode() == OracleTypes.TABLE || isPlsqlTable());
}
public boolean isPlsqlTable() {
return (getTypecode() == OracleTypes.PLSQL_INDEX_TABLE
|| getTypecode() == OracleTypes.PLSQL_NESTED_TABLE || getTypecode() == OracleTypes.PLSQL_VARRAY_TABLE);
}
public boolean isPlsqlRecord() {
return (getTypecode() == OracleTypes.PLSQL_RECORD);
}
public boolean isOpaque() {
return getTypecode() == OracleTypes.OPAQUE;
}
public boolean isJavaStruct() {
return getTypecode() == OracleTypes.JAVA_STRUCT;
}
public boolean isSqlStatement() {
return false;
}
public int getSqljKind() {
return 0;
}
public boolean isStruct() {
return getTypecode() == OracleTypes.STRUCT;
}
/**
* Returns the version string of a SqlType.
*
* * @return the version string of the type.
*/
public String getVersion() {
return m_version;
}
/**
* Set the version string of a declared type.
*
* * @param version the version string of the type
*/
public void setVersion(String version) {
m_version = version;
}
/**
* Get the attribute hashtable associated with a SqlType. The attributes in the hashtable were
* registered via addAttribute().
*/
@SuppressWarnings("unchecked")
public Map<String, String> getAttributes() {
return (Map<String, String>)getAnnotation();
}
/**
* Add an attribute to the collection of attributes of a SqlType. JPub only adds attributes
* mentioned in the input file.
*/
public void addAttribute(String sqlField, String javaField) {
// TODO: check for duplicate Java field names!
String dbField = m_viewCache.dbifyName(sqlField);
if (javaField == null) {
javaField = sqlField;
}
@SuppressWarnings("unchecked")
Map<String, String> h = (Map<String, String>)getAnnotation();
List<String> v = getNamedTranslations();
if (h == null) {
h = new HashMap<String, String>();
setAnnotation(h);
}
if (v == null) {
v = new ArrayList<String>();
setNamedTranslations(v);
}
Object old = h.put(dbField, javaField);
v.add(sqlField);
if (old != null) {
throw new IllegalArgumentException("Redeclaration of field " + dbField + " in " + this
+ "!");
}
}
/**
* SqlType CONSTRUCTORS
*/
/**
* This constructor is used for user-defined and REF types. It may not be called more than once
* for the same non-null sqlName.
*/
protected SqlType(SqlName sqlName, int typecode, boolean generateMe, SqlType parentType,
SqlReflector reflector) {
this(sqlName, typecode, generateMe, false, parentType, reflector);
m_isReused = false;
}
SqlType(SqlName sqlName, int typecode, boolean generateMe, boolean isPrimitive,
SqlType parentType, SqlReflector reflector) {
super(sqlName, typecode, isPrimitive);
m_isReused = false;
m_reflector = reflector;
if (m_reflector != null) {
m_viewCache = m_reflector.getViewCache();
}
if (sqlName != null) { // For SqlRefType, sqlName==null
if (m_reflector != null) {
m_reflector.addType(sqlName, this, generateMe);
}
}
m_parentType = parentType;
}
/**
* This constructor is used for predefined SQL types. It may not be called more than once for
* the same name.
*/
public SqlType(String name, int typecode) {
super(new SqlName(null, name, true, false, true, null, null, null, null/* reflector */),
typecode, true);
}
/**
* This constructor is used for predefined PL/SQL to SQL type mapping.
*/
SqlType(SqlName name, int typecode) {
super(name, typecode, true);
}
// Determine whether "this" type depends on "t"
// For instance, if "t" is a field of "this",
// or the element type of "this"
private List<SqlType> m_dependTypes = null;
boolean dependsOn(SqlType t) {
if (m_dependTypes == null) {
m_dependTypes = new ArrayList<SqlType>();
if (isPlsqlRecord() || this instanceof DefaultArgsHolderType) {
try {
List<AttributeField> fields = getDeclaredFields(true);
for (int i = 0; i < fields.size(); i++) {
SqlType st = (SqlType)fields.get(i).getType();
if (st.isPlsqlRecord() || st.isPlsqlTable()) {
m_dependTypes.add(st);
}
}
}
catch (Exception e) {
System.err.println(e.getMessage());
}
}
else if (this instanceof PlsqlTableType) { // to fend off PlsqlIndexTableType
try {
SqlType st = (SqlType)((PlsqlTableType)this).getComponentType();
if (st.isPlsqlRecord() || st.isPlsqlTable()) {
m_dependTypes.add(st);
}
}
catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
if (m_dependTypes.contains(t)) {
return true;
}
for (int i = 0; i < m_dependTypes.size(); i++) {
if (m_dependTypes.get(i).dependsOn(t)) {
return true;
}
}
return false;
}
@Override
public boolean hasConversion() {
// For SqlRefType, getSqlName()==null
if (getSqlName() == null) {
return false;
}
return getSqlName().hasConversion();
}
/**
* Reports the SQL type into which the PL/SQL can be converted. Returns null if not a PL/SQL
* type or if it doed not have a user-defined conversion.
*/
public String getTargetTypeName() {
return getSqlName().getTargetTypeName();
}
public String getTargetTypeName(int schemaNames) {
return getSqlName().getFullTargetTypeName(schemaNames);
}
public String getTypeName() {
return getSqlName().getTypeName();
}
/**
* Returns the PL/SQL function to be used for converting this PL/SQL into a SQL type.
* <br>
* Returns null if this is not a PL/SQL type or if it does not have user-defined conversions.
*/
//static Hashtable m_convFuns = new Hashtable();
@Override
public String getOutOfConversion() {
// For SqlRefType, getSqlName()==null
if (getSqlName() == null) {
return null;
}
return getSqlName().getOutOfConversion();
}
/**
* Returns the PL/SQL function to be used for converting a SQL type into this PL/SQL type.
* <br>
* Returns null if this is not a PL/SQL type or if it does not have user-defined conversions.
*/
@Override
public String getIntoConversion() {
// For SqlRefType, getSqlName()==null
if (getSqlName() == null) {
return null;
}
return getSqlName().getIntoConversion();
}
// Return the conversion function name
// prefixed with package name,
// if the conversion function is a generated one
@Override
public String getOutOfConversionQualified() {
// For SqlRefType, getSqlName()==null
if (getSqlName() == null) {
return null;
}
return getSqlName().getOutOfConversionQualified();
}
// Return the conversion function name
// prefixed with package name,
// if the conversion function is a generated one
@Override
public String getIntoConversionQualified() {
// For SqlRefType, getSqlName()==null
if (getSqlName() == null) {
return null;
}
return getSqlName().getIntoConversionQualified();
}
// SQL declaration of the generated SQL types
public String getSqlTypeDecl() throws SQLException, PublisherException {
String sqlTypeDecl = "";
if (isPlsqlRecord() && !getSqlName().isReused()) {
sqlTypeDecl += "CREATE OR REPLACE TYPE " + getTargetTypeName() + " AS OBJECT (\n";
List<AttributeField> fields = this.getFields(true);
for (int i = 0; i < fields.size(); i++) {
if (i != 0) {
sqlTypeDecl += ",\n";
}
sqlTypeDecl += " " + Util.unreserveSql(fields.get(i).getName()) + " ";
sqlTypeDecl += fields.get(i).printTypeWithLength(SCHEMA_IF_NEEDED);
}
sqlTypeDecl += "\n);";
}
else if (isPlsqlTable() && !getSqlName().isReused()) {
PlsqlTableType plType = (PlsqlTableType)this;
SqlType eleType = (SqlType)plType.getComponentType();
sqlTypeDecl += "CREATE OR REPLACE TYPE " + getTargetTypeName();
sqlTypeDecl += " AS TABLE OF ";
sqlTypeDecl += printTypeWithLength(eleType.getTargetTypeName(SCHEMA_IF_NEEDED), plType
.getElemTypeLength(), plType.getElemTypePrecision(), plType.getElemTypeScale());
sqlTypeDecl += ";";
}
else if ((this instanceof DefaultArgsHolderType) && !getSqlName().isReused()) {
DefaultArgsHolderType holder = (DefaultArgsHolderType)this;
sqlTypeDecl += "CREATE OR REPLACE TYPE " + getTypeName() + " AS OBJECT (\n";
AttributeField vfield = holder.getFields(false).get(0);
sqlTypeDecl += " " + vfield.getName() + " ";
sqlTypeDecl += vfield.printTypeWithLength();
sqlTypeDecl += "\n);";
}
return sqlTypeDecl;
}
// Drop the generated SQL type
public String getSqlTypeDrop() throws SQLException, PublisherException {
//return getSqlName().isReused() ? "" : "DROP TYPE " + getTargetTypeName() + " FORCE; \n"
// + "show errors\n";
return "DROP TYPE " + getTargetTypeName() + " FORCE;";
}
// PL/SQL package declaration for conversion functions
// between SQL and PL/SQL
public String getConversionFunDecl() throws PublisherException, SQLException {
if (!hasConversion()) {
return "";
}
String sqlConvPkgDecl = "";
// If this type is a ROWTYPE, then redefine it as a normal PL/SQL record
if (getSqlName().isRowType()) {
sqlConvPkgDecl += "\t-- Redefine a PL/SQL RECORD type originally defined via CURSOR%ROWTYPE"
+ "\n";
sqlConvPkgDecl += "\tTYPE " + getTypeName() + " IS RECORD (\n";
List<AttributeField> fields = this.getFields(true);
for (int i = 0; i < fields.size(); i++) {
if (i != 0) {
sqlConvPkgDecl += ",\n";
}
sqlConvPkgDecl += "\t\t" + fields.get(i).getName() + " "
+ fields.get(i).printTypeWithLength();
}
sqlConvPkgDecl += ");";
}
// PL/SQL to SQL
sqlConvPkgDecl += "\t-- Declare the conversion functions the PL/SQL type " + getTypeName()
+ "\n" + "\tFUNCTION " + getOutOfConversion() + "(aPlsqlItem " + getTypeName() + ")\n"
+ " \tRETURN " + getTargetTypeName() + ";\n";
// SQL to PL/SQL
sqlConvPkgDecl += "\tFUNCTION " + getIntoConversion() + "(aSqlItem " + getTargetTypeName()
+ ")\n" + "\tRETURN " + getTypeName() + ";";
return sqlConvPkgDecl;
}
// PL/SQL package body for conversion functions between SQL and PL/SQL
public String getConversionPL2SQLFunBody() throws SQLException, PublisherException {
if (!hasConversion()) {
return "";
}
// PL/SQL to SQL
String sqlConvPkgBody = "\tFUNCTION " + getOutOfConversion() + "(aPlsqlItem "
+ getTypeName() + ")\n " + "\tRETURN " + getTargetTypeName() + " IS \n" + "\taSqlItem "
+ getTargetTypeName() + "; \n" + "\tBEGIN \n"
+ getOutOfConvStmts("\t\t", "aPlsqlItem", "aSqlItem") + "\t\tRETURN aSqlItem;\n"
+ "\tEND " + getOutOfConversion() + ";\n";
return sqlConvPkgBody;
}
// PL/SQL package body for conversion functions between SQL and PL/SQL
public String getConversionSQL2PLFunBody() throws SQLException, PublisherException {
if (!hasConversion()) {
return "";
}
// SQL to PL/SQL
String sqlConvPkgBody = "\tFUNCTION " + getIntoConversion() + "(aSqlItem "
+ getTargetTypeName() + ") \n" + "\tRETURN " + getTypeName() + " IS \n"
+ "\taPlsqlItem " + getTypeName() + "; \n" + "\tBEGIN \n"
+ getIntoConvStmts("\t\t", "aSqlItem", "aPlsqlItem") + "\t\tRETURN aPlsqlItem;\n"
+ "\tEND " + getIntoConversion() + ";\n";
return sqlConvPkgBody;
}
// both conversion functions
public String getBothConversions() throws SQLException, PublisherException {
return getConversionSQL2PLFunBody() + getConversionPL2SQLFunBody();
}
// Return assignment statements from SQL objects to PL/SQL records
public String getIntoConvStmts(String formatPrefix, String maybeSql, String maybePlsql)
throws SQLException, PublisherException {
if (!isPlsqlRecord() && !isPlsqlTable()) {
return formatPrefix + maybePlsql + "." + getName() + " := " + maybeSql + getName()
+ ";\n";
}
String stmts = "";
if (isPlsqlRecord()) {
List<AttributeField> fields = getFields(true);
for (int i = 0; i < java.lang.reflect.Array.getLength(fields); i++) {
AttributeField af = fields.get(i);
stmts += formatPrefix
+ maybePlsql
+ "."
+ af.getName()
+ " := "
+ ((af.getType().hasConversion() && af.getType()
.getIntoConversion() != null) ? af.getType().getIntoConversion()
+ "(" + maybeSql + "." + Util.unreserveSql(af.getName()) + ");\n"
: maybeSql + "." + Util.unreserveSql(af.getName()) + ";\n");
}
}
else { // if (isPlsqlTable())
TypeClass compType = ((PlsqlTableType)this).getComponentType();
if (getTypecode() == OracleTypes.PLSQL_NESTED_TABLE
|| getTypecode() == OracleTypes.PLSQL_VARRAY_TABLE) {
stmts += formatPrefix + maybePlsql + " := " + getTypeName() + "();\n";
stmts += formatPrefix + maybePlsql + ".EXTEND" + "(" + maybeSql + ".COUNT);\n";
}
stmts += formatPrefix
+ "IF "
+ maybeSql
+ ".COUNT>0 THEN\n"
+ formatPrefix
+ "FOR I IN 1.."
+ maybeSql
+ ".COUNT LOOP\n"
+ formatPrefix
+ "\t"
+ maybePlsql
+ "(I)"
+ " := "
+ ((compType.hasConversion() && compType.getIntoConversion() != null) ? compType
.getIntoConversion()
+ "(" + maybeSql + "(I)" + ");\n" : maybeSql + "(I);\n") + formatPrefix
+ "END LOOP; \n" + formatPrefix + "END IF;\n";
}
return stmts;
}
// Return assignment statements from PL/SQL records to SQL objects
public String getOutOfConvStmts(String formatPrefix, String maybePlsql, String maybeSql)
throws SQLException, PublisherException {
if (!isPlsqlRecord() && !isPlsqlTable()) {
return formatPrefix + maybeSql + " := " + maybePlsql + ";\n";
}
String stmts = "";
if (isPlsqlRecord()) {
List<AttributeField> fields = getFields(true);
stmts += formatPrefix + maybeSql + " := " + getTargetTypeName() + "(NULL";
for (int i = 1; i < fields.size(); i++) {
stmts += ", NULL";
}
stmts += ");\n";
for (int i = 0; i < fields.size(); i++) {
AttributeField af = fields.get(i);
stmts += formatPrefix
+ maybeSql
+ "."
+ Util.unreserveSql(af.getName())
+ " := "
+ ((af.getType().hasConversion() && af.getType()
.getOutOfConversion() != null) ? af.getType().getOutOfConversion()
+ "(" + maybePlsql + "." + af.getName() + ");\n" : maybePlsql + "."
+ af.getName() + ";\n");
}
}
else { // if (isPlsqlTable())
TypeClass compType = ((PlsqlTableType)this).getComponentType();
stmts += formatPrefix
+ maybeSql
+ " := "
+ getTargetTypeName()
+ "();\n"
+ formatPrefix
+ maybeSql
+ ".EXTEND("
+ maybePlsql
+ ".COUNT);\n"
+ formatPrefix
+ "IF "
+ maybePlsql
+ ".COUNT>0 THEN\n"
+ formatPrefix
+ "FOR I IN "
+ maybePlsql
+ ".FIRST.."
+ maybePlsql
+ ".LAST LOOP\n"
+ formatPrefix
+ "\t"
+ maybeSql
+ "(I + 1 - "
+ maybePlsql
+ ".FIRST)"
+ " := "
+ ((compType.hasConversion() || compType.getOutOfConversion() != null) ? compType
.getOutOfConversion()
+ "(" + maybePlsql + "(I)" + ");\n" : maybePlsql + "(I);\n") + formatPrefix
+ "END LOOP; \n" + formatPrefix + "END IF; \n";
}
return stmts;
}
}