| /* |
| * 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. |
| * <p/> |
| * 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. |
| * <p/> |
| * 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; |
| } |
| } |