| /* |
| * 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; |
| |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ALL_ARGUMENTS; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ALL_COLL_TYPES; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ALL_OBJECTS; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ALL_TYPES; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ALL_TYPE_ATTRS; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ATTR_NAME; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ATTR_TYPE_NAME; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.DEFAULT_VARCHAR_LEN; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.ELEM_TYPE_NAME; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.IS_COLLECTION; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.IS_PACKAGE; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.IS_TOPLEVEL; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.IS_TYPE; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.MAX_IDENTIFIER_LENGTH; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.OBJECT_NAME; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.OVERLOAD; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.OWNER; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.PACKAGE_NAME; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.TOPLEVEL; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.TYPE_NAME; |
| import static dbws.testing.shadowddlgeneration.oldjpub.Util.getDefaultTypeLen; |
| |
| //javase imports |
| import java.sql.Connection; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| /* |
| * SQL Type Reflection Facility |
| */ |
| public class SqlReflector { |
| |
| public static final String CHAR_CS = "CHAR_CS"; |
| public static final String NCHAR_CS = "NCHAR_CS"; |
| public static final String ROWTYPE = "ROWTYPE"; |
| public static final String ROWTYPE_PL = ROWTYPE + "_PL"; |
| public static final String ROWTYPE_SQL = ROWTYPE + "_SQL"; |
| private static final int MAGIC_NUMBER = 0; |
| |
| public static final SqlType BFILE_TYPE = new SqlType("BFILE", OracleTypes.BFILE); |
| public static final SqlType BINARY_INTEGER_TYPE = |
| // new SqlType("BINARY_INTEGER", OracleTypes.INTEGER); |
| new SqlType(new SqlName("BINARY_INTEGER", "INTEGER"), OracleTypes.INTEGER); |
| public static final SqlType BLOB_TYPE = new SqlType("BLOB", OracleTypes.BLOB); |
| // public static final SqlType BOOLEAN_TYPE = |
| // new SqlType("BOOLEAN", SqlType.OracleTypes_BOOLEAN); |
| public static final SqlType CHAR_TYPE = new SqlType("CHAR", OracleTypes.CHAR); |
| public static final SqlType CLOB_TYPE = new SqlType("CLOB", OracleTypes.CLOB); |
| public static final SqlType DATE_TYPE = new SqlType("DATE", OracleTypes.DATE); |
| public static final SqlType NCHAR_TYPE = new SqlType("NCHAR", SqlType.ORACLE_TYPES_NCHAR); |
| public static final SqlType NCLOB_TYPE = new SqlType("NCLOB", SqlType.ORACLE_TYPES_NCLOB); |
| /* |
| * public static final SqlType TIME_TYPE = new SqlType("TIME", OracleTypes.TIME); public static |
| * final SqlType TIME_WTZ_TYPE = new SqlType("TIME WITH TIME ZONE", OracleTypes.TIME); public |
| * static final SqlType TIMETZ_TYPE = // alternate spelling new SqlType("TIME WITH TZ", |
| * OracleTypes.TIME); |
| */ |
| public static final SqlType TIMESTAMP_TYPE = new SqlType("TIMESTAMP", OracleTypes.TIMESTAMP); |
| public static final SqlType TIMESTAMP_WTZ_TYPE = |
| new SqlType("TIMESTAMP WITH TZ", -101 ); // ==OracleTypes.TIMESTAMPTZ |
| public static final SqlType TIMESTAMPTZ_TYPE = // alternate spelling |
| new SqlType("TIMESTAMP WITH TIME ZONE", -101 ); // ==OracleTypes.TIMESTAMPTZ |
| public static final SqlType TIMESTAMPTZ_TYPE0 = // alternate spelling |
| new SqlType("TIMESTAMPTZ", -101 ); // ==OracleTypes.TIMESTAMPTZ |
| public static final SqlType TIMESTAMP_WLTZ_TYPE = |
| new SqlType("TIMESTAMP WITH LOCAL TZ", -102 ); // ==OracleTypes.TIMESTAMPLTZ |
| public static final SqlType TIMESTAMPLTZ_TYPE = // alternate spelling |
| new SqlType("TIMESTAMP WITH LOCAL TIME ZONE", -102 ); // ==OracleTypes.TIMESTAMPLTZ |
| public static final SqlType TIMESTAMPLTZ_TYPE0 = // alternate spelling |
| new SqlType("TIMESTAMPLTZ", -102 ); // ==OracleTypes.TIMESTAMPLTZ |
| public static final SqlType DECIMAL_TYPE = new SqlType("DECIMAL", OracleTypes.DECIMAL); |
| public static final SqlType DOUBLE_PRECISION_TYPE = new SqlType("DOUBLE PRECISION", |
| OracleTypes.DOUBLE); |
| public static final SqlType FLOAT_TYPE = new SqlType("FLOAT", OracleTypes.FLOAT); |
| public static final SqlType FLOAT38TYPE = new SqlType("FLOAT38", OracleTypes.NUMBER); |
| // TBD: Using the two types, 101 (OracleTypes.BDOUBLE), |
| // 100 (OracleType.BFLOAT), results in <unsupported type>, |
| // e.g., for the command |
| // jpub -u scott/tiger -sql=sys.xmltype:XMLType |
| public static final SqlType BINARY_DOUBLE_TYPE = new SqlType("BINARY_DOUBLE", 101); // OracleTypes.BDOUBLE |
| public static final SqlType BINARY_FLOAT_TYPE = new SqlType("BINARY_FLOAT", 100); // OracleTypes.BFLOAT |
| // Note: not referencing OracleTypes.BDOULBE and BFLOAT is |
| // to avoid incompatible with pre-10i JDBC drivers |
| public static final SqlType INTEGER_TYPE = new SqlType("INTEGER", OracleTypes.INTEGER); |
| public static final SqlType INT_TYPE = new SqlType("INT", OracleTypes.INTEGER); |
| public static final SqlType LONG_TYPE = |
| // new SqlType("LONG", OracleTypes.LONGVARBINARY); |
| new SqlType(new SqlName("LONG", "VARCHAR2"), OracleTypes.VARCHAR); |
| public static final SqlType LONG_RAW_TYPE = |
| // new SqlType("LONG RAW", OracleTypes.RAW); |
| new SqlType(new SqlName("LONG RAW", "RAW"), OracleTypes.RAW); |
| public static final SqlType NUMBER_TYPE = new SqlType("NUMBER", OracleTypes.NUMBER); |
| public static final SqlType NUMERIC_TYPE = new SqlType("NUMERIC", OracleTypes.NUMERIC); |
| public static final SqlType NVARCHAR2_TYPE = new SqlType("NVARCHAR2", |
| SqlType.ORACLE_TYPES_NCHAR); |
| public static final SqlType PLS_INTEGER_TYPE = |
| // new SqlType("PLS_INTEGER", OracleTypes.INTEGER); |
| new SqlType(new SqlName("PLS_INTEGER", "INTEGER"), OracleTypes.INTEGER); |
| // public static final SqlType PLSQL_RECORD_TYPE = |
| // new SqlType("PL/SQL RECORD", OracleTypes.UNSUPPORTED); |
| public static final SqlType RAW_TYPE = new SqlType("RAW", OracleTypes.RAW); |
| public static final SqlType REAL_TYPE = new SqlType("REAL", OracleTypes.REAL); |
| public static final SqlType REF_CURSOR_TYPE = new SqlType("REF CURSOR", OracleTypes.CURSOR); |
| public static final SqlType PLSQL_REF_CURSOR_TYPE = new SqlType("PL/SQL REF CURSOR", |
| OracleTypes.CURSOR); |
| public static final SqlType ROWID_TYPE = |
| // new SqlType("ROWID", OracleTypes.ROWID); |
| new SqlType(new SqlName("ROWID", "VARCHAR2"), OracleTypes.VARCHAR); |
| public static final SqlType SMALLINT_TYPE = new SqlType("SMALLINT", OracleTypes.SMALLINT); |
| public static final SqlType STRING_TYPE = new SqlType("STRING", OracleTypes.VARCHAR); |
| public static final SqlType UROWID_TYPE = |
| // new SqlType("UROWID", OracleTypes.ROWID); |
| new SqlType(new SqlName("UROWID", "VARCHAR2"), OracleTypes.VARCHAR); |
| public static final SqlType VARCHAR_TYPE = new SqlType("VARCHAR", OracleTypes.VARCHAR); |
| public static final SqlType VARCHAR2_TYPE = new SqlType("VARCHAR2", OracleTypes.VARCHAR); |
| public static SqlType plsqlTableDouble = null; |
| public static SqlType plsqlTableFloat = null; |
| public static SqlType plsqlTableInt = null; |
| public static SqlType plsqlTableShort = null; |
| public static SqlType plsqlTableJldouble = null; |
| public static SqlType plsqlTableJlfloat = null; |
| public static SqlType plsqlTableJlinteger = null; |
| public static SqlType plsqlTableJlshort = null; |
| public static SqlType plsqlTableJmbigdecimal = null; |
| public static SqlType plsqlTableString = null; |
| public static final SqlType UNKNOWN_TYPE = new SqlType("<unknown type or type not found>", |
| OracleTypes.UNSUPPORTED); |
| |
| protected ViewCacheManager m_viewCacheManager; |
| protected ViewCache m_viewCache; |
| protected Connection m_conn; |
| protected Boolean m_isPre920 = null; |
| protected boolean m_geq9i; |
| protected boolean m_transitive = true; |
| protected String m_user; |
| // A hashtable holding both predefined types and user types |
| // for quick looking up. |
| protected Map<Name, TypeClass> m_allTypes; |
| protected Map<Name, TypeClass> m_predefTypes; |
| /* |
| * User types to be published can be added into m_userTypes in difference phases of publishing: |
| * (1) First the types specified by the -sql option in the jpub command line are added (2) |
| * JavaPublisher starts to publish the types in m_userTypes (3) When publishing a type, the |
| * types on which that types depend on are reflected and added to the m_userTypes (4) On |
| * finishing a type, JavaPublisher publishes the next type in the m_userTypes list. The |
| * publishing order seems to be breadth-first. |
| */ |
| protected List<TypeClass> m_userTypes; |
| // -overwritedbtypes=false |
| protected HashSet<String> m_allTypeNames; |
| protected HashSet<String> m_allGeneratedTypeNames; |
| protected int m_allGeneratedTypeNamesMagicNumber; |
| protected Map<String, String> m_allDefaultArgsHolderTypeNames; |
| protected HashSet<String>m_isReused; |
| protected boolean m_getTypeCodeWarning = false; |
| protected SqlStmtType m_sqlStmtType; |
| protected int m_rowtypeDistinguisher = 0; |
| protected WrapperPackageMetadata m_wrapperPackageMetadata; |
| protected Map<String, SqlType> m_typeMap = new HashMap<String, SqlType>(); |
| |
| public SqlReflector(Connection conn, String user) { |
| m_viewCacheManager = new ViewCacheManager(conn); |
| m_user = user; |
| reset(conn); |
| } |
| |
| public void reset(Connection conn) { |
| if (m_conn != null) { |
| try { |
| m_conn.close(); |
| } |
| catch (SQLException e) { |
| // Closing resources. OK to ignore exception. |
| } |
| } |
| m_conn = conn; |
| |
| m_viewCache = m_viewCacheManager.get(m_user); |
| if (m_viewCache == null) { |
| m_viewCache = new ViewCache(m_conn, m_user); |
| m_viewCacheManager.add(m_viewCache); |
| } |
| else { |
| m_viewCache.reset(m_conn); |
| } |
| |
| m_allTypes = new HashMap<Name, TypeClass>(); |
| m_predefTypes = new HashMap<Name, TypeClass>(); |
| m_userTypes = new ArrayList<TypeClass>(); |
| sqlTypeInit(); |
| |
| m_allTypeNames = new HashSet<String>(); |
| m_allGeneratedTypeNames = new HashSet<String>(); |
| m_allGeneratedTypeNamesMagicNumber = MAGIC_NUMBER; |
| m_allDefaultArgsHolderTypeNames = new HashMap<String, String>(); |
| m_sqlStmtType = null; |
| |
| m_isReused = new HashSet<String>(); |
| m_wrapperPackageMetadata = null; |
| |
| if (conn != null) { |
| m_geq9i = SqlReflector.geqOracle9(conn); |
| } |
| } |
| |
| public void loadAllTypeNames() { |
| try { |
| Iterator<ViewRow> iter = m_viewCache.getRows(ALL_TYPES, new String[]{TYPE_NAME}, |
| new String[0], new Object[0], new String[0]); |
| while (iter.hasNext()) { |
| SingleColumnViewRow row = (SingleColumnViewRow)iter.next(); |
| m_allTypeNames.add(row.getValue()); |
| } |
| } |
| catch (Exception e) { /* Cannot access ALL_TYPES table */ |
| e.printStackTrace();; |
| } |
| } |
| |
| // Determine the SQL type name for a PL/SQL type |
| public String determineSqlName(String packageName, String[] sourceName, TypeClass parentType, |
| boolean[] isRowType, List<AttributeField> fields, SqlType elemType, SqlType valueType) |
| throws SQLException { |
| String parentName = null; // Java name for the PL/SQL package |
| if (parentType != null) { |
| SqlName parentSqlName = (SqlName)parentType.getNameObject(); |
| if (parentSqlName != null) { |
| parentName = parentSqlName.getTypeName().toUpperCase(); |
| } |
| } |
| String name = sourceName[0]; |
| m_allGeneratedTypeNames.add(name); |
| if (name.indexOf('.') >= 0 && parentName != null) { |
| // Replace the package name with the correpsonding Java name |
| name = parentName + "_" + name.substring(name.indexOf('.') + 1); |
| m_allGeneratedTypeNames.add(parentName); |
| } |
| name = name.replace('.', '_').replace(' ', '_'); |
| |
| boolean toBeDistinguished = false; |
| if (sourceName[0].equals("PL/SQL RECORD")) { // %ROWTYPE |
| isRowType[0] = true; |
| sourceName[0] = ROWTYPE_PL + (m_rowtypeDistinguisher++); |
| name = ROWTYPE_SQL; |
| if (packageName != null && !packageName.equals("")) { |
| if (parentName != null) { |
| name = parentName + "_" + name; |
| } |
| else { |
| name = packageName + "_" + name; |
| } |
| } |
| toBeDistinguished = true; |
| } |
| return determineSqlName(name, toBeDistinguished, fields, elemType, valueType); |
| } |
| |
| private String determineSqlName(String name, boolean toBeDistinguished, |
| List<AttributeField> fields, SqlType elemType, SqlType valueType) throws SQLException { |
| |
| String origName = name; |
| if (valueType != null) { |
| String uniqueName = m_allDefaultArgsHolderTypeNames.get(origName); |
| if (uniqueName != null) { |
| return uniqueName; |
| } |
| } |
| |
| if (m_allTypeNames.contains(name)) { |
| boolean match = true; |
| Iterator<ViewRow> iter; |
| if (fields != null) { |
| HashSet<String> hs = new HashSet<String>(); |
| for (int i = 0; i < fields.size(); i++) { |
| AttributeField af = fields.get(i); |
| String fieldType = ((SqlName)af.getType().getNameObject()) |
| .getTargetTypeName(); |
| hs.add(fieldType + af.getName()); |
| } |
| iter = m_viewCache.getRows(ALL_TYPE_ATTRS, new String[]{"CONCAT(" |
| + ATTR_TYPE_NAME + "," + ATTR_NAME + ")"}, |
| new String[]{TYPE_NAME}, new Object[]{name}, new String[0]); |
| int count = 0; |
| while (iter.hasNext()) { |
| String attr = ((SingleColumnViewRow)iter.next()).getValue(); |
| if (!hs.contains(attr)) { |
| match = false; |
| } |
| count++; |
| } |
| match = match && (count == hs.size()); |
| } |
| else if (elemType != null) { |
| iter = m_viewCache.getRows(ALL_COLL_TYPES, new String[]{ELEM_TYPE_NAME}, |
| new String[]{TYPE_NAME}, new Object[]{name}, new String[0]); |
| if (iter.hasNext()) { |
| String elemTypeNameInDb = ((SingleColumnViewRow)iter.next()).getValue(); |
| String elemTypeName = elemType.getSqlName().getTargetTypeName(); |
| match = elemTypeNameInDb.equalsIgnoreCase(elemTypeName); |
| } |
| else { |
| match = false; |
| } |
| } |
| else if (valueType != null) { |
| iter = m_viewCache.getRows(ALL_TYPE_ATTRS, new String[]{ATTR_TYPE_NAME}, |
| new String[]{TYPE_NAME}, new Object[]{name}, new String[0]); |
| if (iter.hasNext()) { |
| String valueTypeNameInDb = ((SingleColumnViewRow)iter.next()).getValue(); |
| String valueTypeName = valueType.getSqlName().getTargetTypeName(); |
| match = valueTypeNameInDb.equalsIgnoreCase(valueTypeName); |
| } |
| else { |
| match = false; |
| } |
| } |
| if (match) { |
| m_isReused.add(name); |
| return name; |
| } |
| } |
| String uniqueName = name.toUpperCase(); |
| if (m_allTypeNames.contains(uniqueName) |
| || (toBeDistinguished && m_allGeneratedTypeNames.contains(uniqueName))) { |
| do { |
| int len1 = name.length(); |
| int len2 = Integer.toString(m_allGeneratedTypeNamesMagicNumber++).length(); |
| if ((len1 + len2) > MAX_IDENTIFIER_LENGTH) { |
| name = name.substring(0, MAX_IDENTIFIER_LENGTH - len2); |
| } |
| uniqueName = name.toUpperCase() + m_allGeneratedTypeNamesMagicNumber; |
| } |
| while (m_allTypeNames.contains(uniqueName) |
| || (toBeDistinguished && m_allGeneratedTypeNames.contains(uniqueName))); |
| } |
| m_allGeneratedTypeNames.add(uniqueName); |
| if (valueType != null) { |
| m_allDefaultArgsHolderTypeNames.put(origName, uniqueName); |
| } |
| |
| return uniqueName; |
| } |
| |
| void addAllGeneratedTypeNames(String name) { |
| m_allGeneratedTypeNames.add(name.toUpperCase()); |
| } |
| |
| public boolean isReused(String name) { |
| return m_isReused.contains(name); |
| } |
| |
| /* |
| * public String determineDefaultArgsHolderSqlName(String name) throws SQLException { String |
| * unique = (String) m_allDefaultArgsHolderTypeNames.get(name); if (unique == null) { unique = |
| * determineSqlName( name, true, null , null , name); m_allDefaultArgsHolderTypeNames.put(name, |
| * unique); } return unique; } |
| */ |
| public void setViewCachePoolCapacity(int size) { |
| m_viewCacheManager.setViewCachePoolCapacity(size); |
| } |
| |
| private void sqlTypeInit() { |
| if (!m_allTypes.isEmpty()) { |
| m_allTypes = new HashMap<Name, TypeClass>(); |
| } |
| m_allTypes.put(BFILE_TYPE.m_name, BFILE_TYPE); |
| m_allTypes.put(BINARY_INTEGER_TYPE.m_name, BINARY_INTEGER_TYPE); |
| m_allTypes.put(BLOB_TYPE.m_name, BLOB_TYPE); |
| // m_allTypes.put(BOOLEAN_TYPE.m_name, BOOLEAN_TYPE); |
| m_allTypes.put(CHAR_TYPE.m_name, CHAR_TYPE); |
| m_allTypes.put(CLOB_TYPE.m_name, CLOB_TYPE); |
| m_allTypes.put(DATE_TYPE.m_name, DATE_TYPE); |
| m_allTypes.put(NCHAR_TYPE.m_name, NCHAR_TYPE); |
| m_allTypes.put(NCLOB_TYPE.m_name, NCLOB_TYPE); |
| /* |
| * m_allTypes.put(TIME_TYPE.m_name, TIME_TYPE); |
| * m_allTypes.put(TIME_WTZ_TYPE.m_name,TIME_WTZ_TYPE); |
| * m_allTypes.put(TIMETZ_TYPE.m_name, TIMETZ_TYPE); |
| */ |
| m_allTypes.put(TIMESTAMP_TYPE.m_name, TIMESTAMP_TYPE); |
| m_allTypes.put(TIMESTAMP_WTZ_TYPE.m_name, TIMESTAMP_WTZ_TYPE); |
| m_allTypes.put(TIMESTAMPTZ_TYPE.m_name, TIMESTAMPTZ_TYPE); |
| m_allTypes.put(TIMESTAMPTZ_TYPE0.m_name, TIMESTAMPTZ_TYPE0); |
| m_allTypes.put(TIMESTAMP_WLTZ_TYPE.m_name, TIMESTAMP_WLTZ_TYPE); |
| m_allTypes.put(TIMESTAMPLTZ_TYPE.m_name, TIMESTAMPLTZ_TYPE); |
| m_allTypes.put(TIMESTAMPLTZ_TYPE0.m_name, TIMESTAMPLTZ_TYPE0); |
| /* |
| * m_allTypes.put(INTERVAL_YM_TYPE.m_name, INTERVAL_YM_TYPE); |
| * m_allTypes.put(INTERVAL_YM_TYPE.m_name, INTERVAL_YM_TYPE); |
| */ |
| m_allTypes.put(DECIMAL_TYPE.m_name, DECIMAL_TYPE); |
| m_allTypes.put(BINARY_DOUBLE_TYPE.m_name, BINARY_DOUBLE_TYPE); |
| m_allTypes.put(DOUBLE_PRECISION_TYPE.m_name, DOUBLE_PRECISION_TYPE); |
| m_allTypes.put(FLOAT_TYPE.m_name, FLOAT_TYPE); |
| m_allTypes.put(FLOAT38TYPE.m_name, FLOAT38TYPE); |
| m_allTypes.put(BINARY_FLOAT_TYPE.m_name, BINARY_FLOAT_TYPE); |
| m_allTypes.put(INTEGER_TYPE.m_name, INTEGER_TYPE); |
| m_allTypes.put(INT_TYPE.m_name, INT_TYPE); |
| m_allTypes.put(LONG_TYPE.m_name, LONG_TYPE); |
| m_allTypes.put(LONG_RAW_TYPE.m_name, LONG_RAW_TYPE); |
| m_allTypes.put(NUMBER_TYPE.m_name, NUMBER_TYPE); |
| m_allTypes.put(NUMERIC_TYPE.m_name, NUMERIC_TYPE); |
| m_allTypes.put(NVARCHAR2_TYPE.m_name, NVARCHAR2_TYPE); |
| m_allTypes.put(PLS_INTEGER_TYPE.m_name, PLS_INTEGER_TYPE); |
| m_allTypes.put(RAW_TYPE.m_name, RAW_TYPE); |
| m_allTypes.put(REAL_TYPE.m_name, REAL_TYPE); |
| m_allTypes.put(REF_CURSOR_TYPE.m_name, REF_CURSOR_TYPE); |
| m_allTypes.put(PLSQL_REF_CURSOR_TYPE.m_name, PLSQL_REF_CURSOR_TYPE); |
| m_allTypes.put(ROWID_TYPE.m_name, ROWID_TYPE); |
| m_allTypes.put(SMALLINT_TYPE.m_name, SMALLINT_TYPE); |
| m_allTypes.put(STRING_TYPE.m_name, STRING_TYPE); |
| m_allTypes.put(UROWID_TYPE.m_name, UROWID_TYPE); |
| m_allTypes.put(VARCHAR_TYPE.m_name, VARCHAR_TYPE); |
| m_allTypes.put(VARCHAR2_TYPE.m_name, VARCHAR2_TYPE); |
| |
| String name; |
| SqlName sqlName; |
| |
| name = "PLSQL_TABLE_DOUBLE"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "double[]", null, "double[]", null)); |
| plsqlTableDouble = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_FLOAT"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "float[]", null, "float[]", null)); |
| plsqlTableFloat = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_INT"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "int[]", null, "int[]", null)); |
| plsqlTableInt = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_SHORT"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "short[]", null, "short[]", null)); |
| plsqlTableShort = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_JLDOUBLE"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "Double[]", null, "Double[]", null)); |
| plsqlTableJldouble = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_JLFLOAT"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "Float[]", null, "Float[]", null)); |
| plsqlTableJlfloat = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_JLINTEGER"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "Integer[]", null, "Integer[]", null)); |
| plsqlTableJlinteger = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_JLSHORT"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "Short[]", null, "Short[]", null)); |
| plsqlTableJlshort = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_JLINTEGER"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "Integer[]", null, "Integer[]", null)); |
| plsqlTableJlinteger = new PlsqlIndexTableType(sqlName, true); |
| |
| name = "PLSQL_TABLE_JMBIGDECIMAL"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "java.math.BigDecimal[]", null, |
| "java.math.BigDecimal[]", null)); |
| plsqlTableJmbigdecimal = new PlsqlIndexTableType(sqlName, true); |
| name = "PLSQL_TABLE_STRING"; |
| sqlName = new SqlName(name, name); |
| sqlName.setAnnotation(new JavaName("", "String[]", null, "String[]", null)); |
| plsqlTableString = new PlsqlIndexTableType(sqlName, false); |
| |
| m_allTypes.put(plsqlTableDouble.m_name, plsqlTableDouble); |
| m_allTypes.put(plsqlTableFloat.m_name, plsqlTableFloat); |
| m_allTypes.put(plsqlTableInt.m_name, plsqlTableInt); |
| m_allTypes.put(plsqlTableShort.m_name, plsqlTableShort); |
| m_allTypes.put(plsqlTableJldouble.m_name, plsqlTableJldouble); |
| m_allTypes.put(plsqlTableJlfloat.m_name, plsqlTableJlfloat); |
| m_allTypes.put(plsqlTableJlshort.m_name, plsqlTableJlshort); |
| m_allTypes.put(plsqlTableJlinteger.m_name, plsqlTableJlinteger); |
| m_allTypes.put(plsqlTableJmbigdecimal.m_name, plsqlTableJmbigdecimal); |
| m_allTypes.put(plsqlTableString.m_name, plsqlTableString); |
| String hint = "/*[32767]*/"; |
| plsqlTableDouble.setHint(hint); |
| plsqlTableFloat.setHint(hint); |
| plsqlTableInt.setHint(hint); |
| plsqlTableShort.setHint(hint); |
| plsqlTableJldouble.setHint(hint); |
| plsqlTableJlfloat.setHint(hint); |
| plsqlTableJlinteger.setHint(hint); |
| plsqlTableJlshort.setHint(hint); |
| plsqlTableString.setHint(hint); |
| plsqlTableJmbigdecimal.setHint(hint); |
| |
| m_allTypes.put(UNKNOWN_TYPE.m_name, UNKNOWN_TYPE); |
| } |
| |
| public Connection getConnection() { |
| return m_conn; |
| } |
| |
| public SqlType addSqlType(SqlName sqlName, int whatIsIt, boolean mustBeNew, boolean generateMe, |
| SqlType parentType, String modifier) throws SQLException, PublisherException { |
| return addSqlType(sqlName, whatIsIt, mustBeNew, generateMe, parentType, modifier, false, |
| null); |
| } |
| |
| public SqlType addSqlType(SqlName sqlName, int whatIsIt, boolean mustBeNew, boolean generateMe, |
| SqlType parentType, String modifier, boolean ncharFormOfUse, MethodFilter signatureFilter) |
| throws SQLException, PublisherException { |
| SqlType result = null; |
| try { |
| if (ncharFormOfUse) { |
| if (sqlName.getTypeName().equals("CHAR")) { |
| sqlName = NCHAR_TYPE.getSqlName(); |
| } |
| else if (sqlName.getTypeName().equals("VARCHAR2")) { |
| sqlName = NVARCHAR2_TYPE.getSqlName(); |
| } |
| else if (sqlName.getTypeName().equals("CLOB")) { |
| sqlName = NCLOB_TYPE.getSqlName(); |
| } |
| } |
| |
| SqlType t = findType(sqlName); |
| boolean predefined = false; |
| if (t == null) { |
| t = findPredefType(sqlName); |
| predefined = true; |
| } |
| |
| if (t != null) { |
| result = t; |
| if (mustBeNew && predefined) { |
| throw new PublisherException("duplicate type " + result.toString()); |
| } |
| else if (mustBeNew) { |
| t.setNameObject(sqlName); |
| } |
| return result; |
| } |
| |
| String schema = sqlName.getSchemaName(); |
| String type = sqlName.getTypeName(); |
| if (whatIsIt == IS_TOPLEVEL) { |
| return new SqlToplevelType(sqlName, parentType, signatureFilter, this); |
| } |
| else if ((whatIsIt & IS_PACKAGE) != 0) { |
| Iterator<ViewRow> rowIter = m_viewCache.getRows(ALL_OBJECTS, |
| new String[]{"'PACKAGE' AS TYPECODE"}, new String[]{OWNER, OBJECT_NAME, |
| "OBJECT_TYPE", "STATUS"}, new Object[]{schema, type, "PACKAGE", "VALID"}, |
| new String[0]); |
| |
| if (rowIter.hasNext()) { |
| m_viewCache.fetch(type, signatureFilter); |
| result = new SqlPackageType(sqlName, parentType, signatureFilter, this); |
| } |
| if (result != null) { |
| return result; |
| } |
| if (whatIsIt == IS_PACKAGE) { |
| throw new PublisherException("package not found " + sqlName.toString()); |
| } |
| } |
| |
| /* determine whether this is an opaque type */ |
| Iterator<ViewRow> iter = m_viewCache.getRows(ALL_TYPES, new String[0], new String[]{ |
| OWNER, TYPE_NAME, "PREDEFINED"}, new Object[]{schema, type, "NO"}, |
| new String[0]); |
| if (iter.hasNext()) { |
| AllTypes allTypes = (AllTypes)iter.next(); |
| String typeCode = allTypes.typeCode; |
| byte[] typeOID = allTypes.typeOid; |
| int dbTypeCode = 0; |
| int kind = 0; |
| if (!m_getTypeCodeWarning) { |
| try { |
| Object[] outParams = m_viewCache.getOutParameters( |
| "BEGIN sys.sqljutl.get_typecode(:1, :2, :3, :4); END;", |
| new Object[]{typeOID}, new int[]{OracleTypes.INTEGER, |
| OracleTypes.VARCHAR, OracleTypes.INTEGER}); |
| if (outParams == null) { |
| throw new SQLException("no data from sqljutl.get_typecode call"); |
| } |
| dbTypeCode = (Integer) outParams[0]; |
| kind = (Integer) outParams[2]; |
| } |
| catch (SQLException exn) { |
| String msg = exn.getMessage(); |
| if (isPre920()) { |
| // an older database version will not have GET_TYPECODE |
| msg = null; |
| m_getTypeCodeWarning = true; |
| } |
| else if (msg.indexOf("PLS-00201") > 0) { |
| msg = "cannot determine type " + type; |
| m_getTypeCodeWarning = true; |
| } |
| else { |
| msg = "error determining type " + type; |
| } |
| if (msg != null) { |
| throw new PublisherException(msg); |
| } |
| } |
| } |
| |
| if (dbTypeCode == SqlType.CODE_OPAQUE) { |
| result = new SqlObjectType(sqlName, OracleTypes.OPAQUE, generateMe, parentType, |
| this); |
| } |
| else if (dbTypeCode == SqlType.CODE_SQLJTYPE && kind != 0) { |
| result = new SqlSqljType(sqlName, kind, parentType, this); |
| } |
| else if (typeCode.equals("OBJECT")) { |
| result = new SqlObjectType(sqlName, generateMe, parentType, this); |
| if (SqlName.langIsOtt()) { |
| /* |
| * For C only. Maura - I'm not sure about this, but |
| * it seems that a ref typedef has to be generated |
| * for every struct. That's the purpose of the |
| * following SqlRefType |
| */ |
| new SqlRefType(sqlName, result, parentType, generateMe, this); |
| } |
| if (PublisherModifier.isIncomplete(result.getModifiers())) { |
| /* |
| * give warning |
| * about incomplete |
| * type and |
| * continue |
| */ |
| int line = sqlName.getLine(); |
| int column = sqlName.getColumn(); |
| String mesg = "incomplete type " + sqlName.toString(); |
| |
| if (line > 0 || column > 0) { |
| mesg = "" + line + "." + column + ": " + mesg; |
| } |
| System.err.println(mesg); |
| } |
| } |
| else if (typeCode.equals("COLLECTION")) { |
| if ((whatIsIt & IS_COLLECTION) == 0) { |
| throw new PublisherException("collection found " + sqlName.toString()); |
| } |
| Iterator<ViewRow> iter3 = m_viewCache.getRows(ALL_COLL_TYPES, new String[0], |
| new String[]{OWNER, TYPE_NAME}, new Object[]{schema, type}, |
| new String[0]); |
| if (iter3.hasNext()) { |
| AllCollTypes act = (AllCollTypes)iter3.next(); |
| String collTypeCode = act.collType; |
| if (collTypeCode.equals("TABLE")) { |
| result = new SqlTableType(sqlName, generateMe, parentType, this); |
| } |
| else { |
| // "VARYING ARRAY" |
| result = new SqlArrayType(sqlName, generateMe, parentType, this); |
| } |
| } |
| } |
| } |
| if (result != null) { |
| return result; |
| } |
| else { |
| // Note that this type is not supported, so we will not look |
| // it up again. |
| result = addPredefType(sqlName, OracleTypes.UNSUPPORTED); |
| // Now add an error message |
| if ((whatIsIt & IS_PACKAGE) != 0) { |
| throw new PublisherException("type not found " + sqlName.toString()); |
| } |
| } |
| } |
| catch (SQLException e) { |
| System.err.println(e.getMessage()); |
| } |
| catch (PublisherException ex) { |
| System.err.println(ex.getMessage()); |
| } |
| return result; |
| } |
| |
| public SqlType addSqlUserType(String schema, String type, int whatIsIt, boolean mustBeNew, |
| int line, int col, MethodFilter signatureFilter) throws SQLException, PublisherException { |
| SqlName sn = new SqlName(schema, type, false, line, col, this); |
| return addSqlType(sn, whatIsIt, mustBeNew, true, null, null, false, signatureFilter); |
| } |
| |
| private SqlType addSqlDBType(SqlName sqlName, String modifier, boolean ncharFormOfUse, |
| SqlType parentType) throws SQLException, PublisherException { |
| |
| /* constructed types are not hashed */ |
| if (modifier != null && modifier.equals("REF")) { |
| SqlType t = addSqlType(sqlName, IS_TYPE, false, m_transitive, parentType, modifier); |
| SqlType rt = new SqlRefType(sqlName, t, parentType, m_transitive, this); |
| return rt; |
| } |
| else { |
| return addSqlType(sqlName, IS_TYPE, false, m_transitive, parentType, modifier, |
| ncharFormOfUse, null); |
| } |
| } |
| |
| // Used for adding types other than PL/SQL types |
| public SqlType addSqlDBType(String schema, String type, String subtype, String modifier, |
| boolean ncharFormOfUse, SqlType parentType) throws SQLException, PublisherException { |
| int line = 0; |
| int col = 0; |
| |
| if (parentType != null) { |
| SqlName psqlname = (SqlName)parentType.getNameObject(); |
| line = psqlname.getLine(); |
| col = psqlname.getColumn(); |
| } |
| if (subtype != null) { |
| if (type != null) { |
| type = type + "." + subtype; |
| } |
| else { |
| type = subtype; |
| } |
| if (schema != null) { |
| type = schema + "." + type; |
| schema = null; |
| } |
| } |
| SqlName sn = new SqlName(schema, type, true, line, col, this); |
| sn.setAnnotation(genPattern((LangName)sn.getAnnotation(), sn.getSimpleName(), true)); |
| |
| return addSqlDBType(sn, modifier, ncharFormOfUse, parentType); |
| } |
| |
| /* |
| * Add SQL or PL/SQL types from PL/SQL packages (1) For SQL types, delegate to addSqlDBType (2) |
| * For PL/SQL, create PlsqlRecorType or PlsqlTableType |
| */ |
| public SqlType addPlsqlDBType(String schema, String type, String subtype, String modifier, |
| boolean ncharFormOfUse, String packageName, String methodName, String methodNo, |
| int sequence, SqlType parentType) throws SQLException, PublisherException { |
| return addPlsqlDBType(schema, type, subtype, modifier, ncharFormOfUse, packageName, |
| methodName, methodNo, sequence, parentType, false); |
| } |
| |
| public SqlType addPlsqlDBType(String schema, String type, String subtype, String modifier, |
| boolean ncharFormOfUse, String packageName, String methodName, String methodNo, |
| int sequence, SqlType parentType, boolean isGrandparent) throws SQLException, |
| PublisherException { |
| int line = 0; |
| int col = 0; |
| |
| if (parentType != null) { |
| SqlName psqlname = (SqlName)parentType.getNameObject(); |
| line = psqlname.getLine(); |
| col = psqlname.getColumn(); |
| } |
| if (subtype != null) { |
| if (type != null) { |
| type = type + "." + subtype; |
| } |
| else { |
| type = subtype; |
| } |
| if (schema != null) { |
| type = schema + "." + type; |
| schema = null; |
| } |
| } |
| // For SQL types, delegate to addSqlDBType |
| if (modifier == null |
| || (!modifier.equals("PL/SQL RECORD") && !modifier.equals("PL/SQL TABLE") |
| && (!modifier.equals("TABLE") || (subtype == null && type.indexOf(".") == -1)) && (!modifier |
| .equals("VARRAY") || (subtype == null && type.indexOf(".") == -1)))) { |
| SqlName sn = new SqlName(schema, type, true, line, col, this); |
| sn.setAnnotation(genPattern((LangName)sn.getAnnotation(), sn.getSimpleName(), true)); |
| return addSqlDBType(sn, modifier, ncharFormOfUse, parentType); |
| } |
| |
| // Start processing PL/SQL types |
| // Check wether the ROWTYPE has bee published |
| List<RowtypeInfo> rowtypeInfoA = null; |
| if (modifier != null && type != null && modifier.equals("PL/SQL RECORD") |
| && type.equals("PL/SQL RECORD")) { |
| rowtypeInfoA = reflectRowtypeInfo(packageName, methodName, methodNo, sequence); |
| for (int i = 0; i < m_userTypes.size(); i++) { |
| boolean found = true; |
| TypeClass p = m_userTypes.get(i); |
| if (!(p instanceof PlsqlRecordType)) { |
| continue; |
| } |
| List<RowtypeInfo> rowtypeInfoB = ((PlsqlRecordType)p).getRowtypeInfo(); |
| if (rowtypeInfoB == null || rowtypeInfoA == null |
| || rowtypeInfoA.size() != rowtypeInfoB.size()) { |
| found = false; |
| continue; |
| } |
| for (int ja = 0; ja < rowtypeInfoA.size(); ja++) { |
| boolean microFound = false; |
| for (int jb = 0; jb < rowtypeInfoB.size(); jb++) { |
| if (rowtypeInfoA.get(ja).equals(rowtypeInfoB.get(jb))) { |
| microFound = true; |
| break; |
| } |
| } |
| if (!microFound) { |
| found = false; |
| break; |
| } |
| } |
| if (found) { |
| return (SqlType)p; |
| } |
| } |
| } |
| |
| // Create the PL/SQL type |
| SqlType result = findPredefType(schema, type); |
| boolean predefined = (result != null); |
| if (!predefined) { |
| result = findType(schema, type); |
| } |
| if (result != null) { |
| if (result.getTypecode() != SqlType.ORACLE_TYPES_TBD) { |
| return result; |
| } |
| else if (!predefined) { |
| // Deals with PL/SQL types: Augument the predefined type |
| // by -typemap with more specific PL/SQL type information. |
| if (modifier != null && modifier.equals("PL/SQL RECORD")) { |
| result.setTypecode(OracleTypes.PLSQL_RECORD); |
| } |
| else if (modifier != null && modifier.equals("PL/SQL TABLE")) { |
| result.setTypecode(OracleTypes.PLSQL_INDEX_TABLE); |
| } |
| else if (modifier != null && modifier.equals("TABLE")) { |
| result.setTypecode(OracleTypes.PLSQL_NESTED_TABLE); |
| } |
| else if (modifier != null && modifier.equals("VARRAY")) { |
| result.setTypecode(OracleTypes.PLSQL_VARRAY_TABLE); |
| } |
| return result; |
| } |
| } |
| |
| // Case 1: result is null |
| // Case 2: result not null, predefined, OracleTypes_TBD |
| if (modifier != null && modifier.equals("PL/SQL RECORD")) { |
| List<FieldInfo> fieldInfo = PlsqlRecordType.getFieldInfo(Util.getSchema(schema, type), packageName, methodName, methodNo, |
| sequence, this); |
| List<AttributeField> fields = SqlTypeWithFields.reflectFields(false, fieldInfo, this, |
| parentType, true /* isGrandparent */); |
| // if predefined, overwrite entry already in m_predefiendTypes |
| if (predefined) { |
| String hint = result.getHint(); |
| result = new PlsqlRecordType(result.getSqlName(), fieldInfo, fields, rowtypeInfoA, |
| false /* generateMe */, parentType, this); |
| result.setHint(hint); |
| } |
| else { |
| // Prepare SqlName for the PL/SQL type |
| SqlName sqlName = new PlsqlRecordName(schema, type, true, line, col, packageName, |
| parentType, fields, this); |
| sqlName.setAnnotation(genPattern((LangName)sqlName.getAnnotation(), sqlName |
| .getSimpleName(), true)); |
| |
| result = new PlsqlRecordType(sqlName, fieldInfo, fields, rowtypeInfoA, |
| true /* generateMe */, parentType, this); |
| } |
| } |
| else { |
| int oracleType = OracleTypes.UNSUPPORTED; |
| if (modifier != null && modifier.equals("PL/SQL TABLE")) { |
| oracleType = OracleTypes.PLSQL_INDEX_TABLE; |
| } |
| else if (modifier != null && modifier.equals("TABLE") && type.indexOf(".") > -1) { |
| oracleType = OracleTypes.PLSQL_NESTED_TABLE; |
| } |
| else if (modifier != null && modifier.equals("VARRAY") && type.indexOf(".") > -1) { |
| oracleType = OracleTypes.PLSQL_VARRAY_TABLE; |
| } |
| if (oracleType != OracleTypes.UNSUPPORTED) { |
| ElemInfo elemInfo = PlsqlTableType.getElemInfo(schema, type, packageName, |
| methodName, methodNo, m_viewCache); |
| int[] details = new int[PlsqlTableType.NUMBER_OF_DETAILS]; |
| |
| SqlType elemType = (SqlType)PlsqlTableType.getComponentType(elemInfo, this, |
| parentType, details); |
| |
| if (predefined) { |
| String hint = result.getHint(); |
| result = PlsqlTableType.newInstance(result.getSqlName(), oracleType, elemInfo, |
| elemType, details, false, parentType, isGrandparent, this); |
| result.setHint(hint); |
| } |
| else { |
| // Prepare SqlName for the PL/SQL type |
| SqlName sqlName = new PlsqlTableName(schema, type, true, line, col, |
| packageName, parentType, elemType, this); |
| sqlName.setAnnotation(genPattern((LangName)sqlName.getAnnotation(), sqlName |
| .getSimpleName(), true)); |
| |
| result = PlsqlTableType.newInstance(sqlName, oracleType, elemInfo, elemType, |
| details, true, parentType, isGrandparent, this); |
| } |
| } |
| } |
| if (result != null) { |
| return result; |
| } |
| throw new PublisherException("unsupported Type " + schema + "." + type); |
| } |
| |
| /* |
| * look up rowtype columns |
| */ |
| List<RowtypeInfo> reflectRowtypeInfo(String packageName, String methodName, String methodNo, |
| int sequence) throws SQLException { |
| // Although package_name and type_name derived from getPlsqlTypeName() |
| // can be used for the queries, we use method and sequence |
| // to be more general. If this type is defined via CURSOR%ROWTYPE, |
| // method, method_no and sequence has to be used to |
| // identify this type in ALL_ARGUMENTS. |
| Iterator<ViewRow> iter = m_viewCache.getRows(ALL_ARGUMENTS, new String[0], new String[]{ |
| PACKAGE_NAME, OBJECT_NAME, OVERLOAD}, new Object[]{packageName, methodName, |
| methodNo}, new String[0]); |
| ArrayList<ViewRow> viewRows = new ArrayList<ViewRow>(); |
| while (iter.hasNext()) { // DISTINCT not enforced |
| UserArguments item = (UserArguments)iter.next(); |
| viewRows.add(item); |
| } |
| List<RowtypeInfo> rowtypeInfoA = RowtypeInfo.getRowtypeInfo(viewRows); |
| int data_level = 0; |
| for (int i = 0; i < rowtypeInfoA.size(); i++) { |
| RowtypeInfo rti = rowtypeInfoA.get(i); |
| if (sequence == -1 || sequence == rti.sequence()) { |
| data_level = rti.data_level(); |
| break; |
| } |
| } |
| int next_rec_sequence = -1; |
| for (int i = 0; i < rowtypeInfoA.size(); i++) { |
| RowtypeInfo rti = rowtypeInfoA.get(i); |
| if (data_level == rti.data_level() && (sequence == -1 || sequence < rti.sequence())) { |
| next_rec_sequence = rti.sequence(); |
| break; |
| } |
| } |
| data_level++; |
| ArrayList<RowtypeInfo> rowtypeInfoW = new ArrayList<RowtypeInfo>(); |
| for (int i = 0; i < rowtypeInfoA.size(); i++) { |
| RowtypeInfo rti = rowtypeInfoA.get(i); |
| if ((sequence == -1 || sequence < rti.sequence()) && data_level == rti.data_level() |
| // && data_level <= rti.data_level() |
| && (next_rec_sequence == -1 || next_rec_sequence > rti.sequence())) { |
| rowtypeInfoW.add(rti); |
| } |
| } |
| return rowtypeInfoW; |
| } |
| |
| public SqlType addPredefType(String schema, String type, int typecode, String javaName, |
| String convertInto, String convertOutOf, String conversionTarget) throws PublisherException { |
| SqlName sn = new SqlName(schema, type, false, false, true, convertInto, convertOutOf, |
| conversionTarget, this); |
| |
| // Does the Java type contain a type annotation? |
| int pos; |
| String annotation; |
| boolean isPlsqlIndexTable = false; |
| int maxLen = Integer.parseInt(DEFAULT_VARCHAR_LEN); |
| int maxElemLen = -1; |
| boolean isNumeric = true; |
| if ((pos = javaName.indexOf("[")) >= 0) { |
| annotation = "/*" + javaName.substring(pos).trim() + "*/"; |
| isPlsqlIndexTable = true; |
| int pos1 = pos; |
| int pos2 = javaName.indexOf("]"); |
| if (pos2 > -1) { |
| try { |
| maxLen = Integer.parseInt(javaName.substring(pos1 + 1, pos2)); |
| } |
| catch (NumberFormatException e) { |
| System.err.println("ERROR: number format error: " + javaName); |
| } |
| } |
| pos1 = javaName.indexOf("("); |
| pos2 = javaName.indexOf(")"); |
| if (pos1 > -1 && pos2 > -1) { |
| try { |
| maxElemLen = Integer.parseInt(javaName.substring(pos1 + 1, pos2)); |
| } |
| catch (NumberFormatException e) { |
| System.err.println("ERROR: number format error: " + javaName); |
| } |
| } |
| javaName = javaName.substring(0, pos); |
| if ("String".equals(javaName) || "java.lang.String".equals(javaName)) { |
| isNumeric = false; |
| } |
| javaName = javaName + "[]"; |
| if (maxElemLen == -1) { |
| maxElemLen = Integer.parseInt(getDefaultTypeLen("VARCHAR2")); |
| } |
| } |
| else { |
| annotation = ""; |
| } |
| |
| sn.setLangName(null, javaName, null, null, null, null, null, null, true); |
| |
| SqlType st = null; |
| if (isPlsqlIndexTable) { |
| st = new PlsqlIndexTableType(sn, isNumeric, maxLen, maxElemLen); |
| m_predefTypes.put(sn, st); |
| } |
| else { |
| st = addPredefType(sn, typecode); |
| } |
| st.setHint(annotation); |
| return st; |
| } |
| |
| public SqlType addPredefType(SqlName name, int typecode) throws PublisherException { |
| // To determine whether the Java type implements ORAData or CustomData |
| boolean isPrimitive = true; |
| String declClassName = ((name.getDeclPackage() != null && name.getDeclPackage().length() > 0) ? (name |
| .getDeclPackage() + ".") |
| : "") |
| + name.getDeclClass(); |
| Class<?> declClass = null; |
| try { |
| declClass = Class.forName(declClassName); |
| } |
| catch (Throwable t) { |
| try { |
| declClass = Class.forName("java.lang." + declClassName); |
| } |
| catch (Throwable t2) { |
| // class did not check out as a primitive class |
| } |
| } |
| if (declClass == null) { |
| // Cannot load that class. Assume it is not primitive. |
| isPrimitive = false; |
| } |
| else if (declClass != null) { |
| try { |
| declClass.getField("_SQL_TYPENAME"); |
| isPrimitive = false; |
| } |
| catch (Throwable t) { |
| try { |
| declClass.getField("_SQL_NAME"); |
| isPrimitive = false; |
| } |
| catch (Throwable t2) { |
| // class has neither _SQL_TYPENAME nor _SQL_NAME |
| } |
| } |
| } |
| if (declClassName.equals("boolean") || declClassName.equals("int") |
| || declClassName.equals("short") || declClassName.equals("double") |
| || declClassName.equals("float") || declClassName.equals("long") |
| || declClassName.equals("byte") || declClassName.equals("char") |
| || declClassName.endsWith("[]")) { |
| isPrimitive = true; |
| } |
| |
| if (typecode == OracleTypes.UNSUPPORTED || typecode == SqlType.ORACLE_TYPES_TBD) { |
| if ("INTEGER".equals(name.getTargetTypeName())) { |
| typecode = OracleTypes.INTEGER; |
| } |
| if ("CHAR".equals(name.getTargetTypeName())) { |
| typecode = OracleTypes.CHAR; |
| } |
| } |
| |
| SqlType st = null; |
| if (!isPrimitive) { // for sure this is a SQL Object Type |
| try { |
| st = new SqlObjectType(name, typecode, false, null, this); |
| } |
| catch (Exception e) { |
| System.err.println(e.getMessage()); |
| } |
| } |
| else { |
| st = new SqlType(name, typecode, false, isPrimitive, null, this); |
| } |
| if (st != null) { |
| m_predefTypes.put(name, st); |
| } |
| else { |
| throw new PublisherException("Cannot find Type " + name); |
| } |
| return st; |
| } |
| |
| public void addType(Name name, TypeClass type, boolean generateMe) { |
| if (generateMe) { |
| m_userTypes.add(type); |
| } |
| if (name instanceof SqlName && type instanceof SqlType) { |
| m_allTypes.put(name, type); |
| } |
| } |
| |
| /* |
| * Add default parameter holder types for PL/SQL methods |
| */ |
| public SqlType addDefaultArgsHolderType(SqlType valueType, String packageName, |
| SqlType parentType, boolean ncharFormOfUse) throws SQLException, PublisherException { |
| String typeName = ""; |
| if (valueType.getSqlName().getTypeName().indexOf(".") > -1) { |
| typeName += valueType.getSqlName().getSimpleName(); |
| } |
| else { |
| typeName += valueType.getSqlName().getTypeName(); |
| } |
| SqlName sqlName = new DefaultArgsHolderName(null, typeName, true, 0, 0, packageName, |
| parentType, valueType, this); |
| SqlType result = findType(sqlName); |
| if (result == null) { |
| sqlName.setAnnotation(genPattern((LangName)sqlName.getAnnotation(), sqlName |
| .getSimpleName(), true)); |
| result = new DefaultArgsHolderType(sqlName, valueType, ncharFormOfUse, this); |
| } |
| return result; |
| } |
| |
| public boolean isUserType(TypeClass t) { |
| if (t instanceof SqlType) { |
| for (int i = 0; i < m_userTypes.size(); i++) { |
| if (m_userTypes.get(i).equals(t)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private SqlType findType(SqlName sqlName) { |
| return _findType(m_allTypes, sqlName); |
| } |
| |
| private SqlType findType(String schema, String type) { |
| SqlName sqlName = new SqlName(Util.getSchema(schema, type), Util.getType(schema, type), |
| false, this); |
| return _findType(m_allTypes, sqlName); |
| } |
| |
| private SqlType findPredefType(SqlName sqlName) { |
| return _findType(m_predefTypes, sqlName); |
| } |
| |
| private SqlType findPredefType(String schema, String type) { |
| SqlName sqlName = new SqlName(schema, type, false, this); |
| return _findType(m_predefTypes, sqlName); |
| } |
| |
| private SqlType _findType(Map<Name, TypeClass> ht, SqlName sqlName) { |
| return (SqlType)ht.get(sqlName); |
| /* |
| * Enumeration keys = ht.keys(); while (keys.hasMoreElements()) { Object key = |
| * keys.nextElement(); if (sqlName.equals(key)) { return (SqlType) ht.get(key); } } return |
| * null; |
| */ |
| } |
| |
| public SqlType findType(String name) { |
| String schema = Util.getSchema(null, name); |
| name = Util.getType(null, name); |
| SqlName sqlName = new SqlName(schema, name, false, this); |
| SqlType sqlType = findType(sqlName); |
| if (sqlType == null) { |
| sqlType = findPredefType(sqlName); |
| } |
| return sqlType; |
| } |
| |
| /** |
| * Add all types declared in the given schema to the set of types to be translated. |
| */ |
| public void addAllTypes(String schema) throws SQLException, PublisherException { |
| if (m_conn != null) { |
| Iterator<ViewRow> iter = m_viewCache |
| .getRows(ALL_TYPES, new String[0], new String[]{OWNER, "PREDEFINED"}, |
| new Object[]{schema, "NO"}, new String[]{TYPE_NAME}); |
| while (iter.hasNext()) { |
| AllTypes allTypes = (AllTypes)iter.next(); |
| addSqlDBType(schema, allTypes.typeName, null, "", false, null); |
| } |
| } |
| } |
| |
| public void addAllPackages(String schema) throws SQLException, PublisherException { |
| if (m_conn != null) { |
| PreparedStatement stmt = m_conn.prepareStatement("SELECT OBJECT_NAME AS TYPE_NAME FROM " + |
| "ALL_OBJECTS WHERE OWNER = :1 AND OBJECT_TYPE = 'PACKAGE' AND STATUS='VALID'"); |
| stmt.setString(1, schema); |
| ResultSet rs = stmt.executeQuery(); |
| while (rs.next()) { |
| SqlName sn = new SqlName(schema, rs.getString(1), true, this); |
| // determine generated Java names using -genPattern setting. |
| sn |
| .setAnnotation(genPattern((LangName)sn.getAnnotation(), sn.getSimpleName(), |
| true)); |
| addSqlType(sn, Util.IS_PACKAGE, false, true, null, null); |
| } |
| rs.close(); |
| stmt.close(); |
| |
| stmt = m_conn |
| .prepareStatement("SELECT COUNT(OBJECT_NAME) AS ARG_COUNT FROM ALL_ARGUMENTS WHERE OWNER = :1 AND PACKAGE_NAME IS NULL AND DATA_LEVEL = 0"); |
| stmt.setString(1, schema); |
| rs = stmt.executeQuery(); |
| if (rs.next()) { |
| int count = rs.getInt(1); |
| if (count > 0) { |
| SqlType t = addSqlUserType(schema, "", Util.IS_TOPLEVEL, true, 0, 0, null); |
| String userName = SqlName.sqlIdToJavaId(TOPLEVEL, true); |
| t.getSqlName().setLangName("", userName, null, null, null, null, null, null, |
| true); |
| } |
| } |
| rs.close(); |
| stmt.close(); |
| } |
| } |
| |
| /* Add Java non-array types for publishing */ |
| public JavaType addJavaType(String typeName, List<AttributeField> fields, |
| List<ProcedureMethod> methods, boolean genPattern, TypeClass sqlType) throws SQLException { |
| if (typeName == null) { |
| return null; |
| } |
| JavaType jt = null; |
| for (int i = 0; i < m_userTypes.size(); i++) { |
| if (m_userTypes.get(i) instanceof JavaType) { |
| JavaType jTypeTmp = (JavaType)m_userTypes.get(i); |
| JavaName jNameTmp = new JavaName(null, typeName, null, null, null); |
| if (jTypeTmp.getJavaName().equals(jNameTmp)) { |
| jt = jTypeTmp; |
| } |
| } |
| } |
| if (jt != null) { |
| return jt; |
| } |
| JavaName javaName = new JavaName(null, typeName, null, null, null); |
| if (genPattern) { |
| javaName = (JavaName)genPattern(javaName, typeName, false); |
| } |
| jt = new JavaBaseType(javaName, fields, methods, sqlType); |
| m_userTypes.add(jt); |
| return jt; |
| } |
| |
| /** |
| * Search among userTypes, and return a boolean indicating whether a subclass of the receiver |
| * type has methods |
| */ |
| public boolean hasMethodsInSubclasses(TypeClass who) throws SQLException, PublisherException { |
| Iterator<TypeClass> iter = m_userTypes.iterator(); |
| while (iter.hasNext()) { |
| TypeClass t = iter.next(); |
| if (!(t instanceof SqlType)) { |
| continue; |
| } |
| SqlType st = (SqlType)t; |
| boolean stHasMethods = st.hasMethods(); |
| while ((st = (SqlType)st.getSupertype()) != null) { |
| if (who.getName() != null && who.getName().equals(st.getName())) { |
| if (stHasMethods) { |
| return true; |
| } |
| break; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public void createSqlStmtType(SqlName sqlName) throws SQLException { |
| if (m_sqlStmtType == null) { |
| m_sqlStmtType = new SqlStmtType(sqlName, this); |
| } |
| } |
| |
| public boolean isPre920() { |
| if (m_isPre920 == null) { |
| try { |
| String v = m_conn.getMetaData().getDatabaseProductVersion().toUpperCase(); |
| if (v.startsWith("ORACLE DATABASE 10G") || v.startsWith("ORACLE DATABASE 11G")) { |
| m_isPre920 = Boolean.FALSE; |
| return false; |
| } |
| int pos = v.indexOf("ORACLE"); |
| if (0 < pos) { |
| v = v.substring(pos); |
| } |
| String vp = v.substring(0, "ORACLExx".length()).toUpperCase(); |
| if (vp.equals("ORACLE12") |
| || vp.equals("ORACLE11") |
| || vp.equals("ORACLE10") |
| || (vp.equals("ORACLE9I") && (v.indexOf("9.2.") > 0 || v.indexOf("9.3.") > 0 || v |
| .indexOf("9.4.") > 0))) { |
| m_isPre920 = Boolean.FALSE; |
| } |
| } |
| catch (Exception e) { |
| // Connection is pre 9.2.0 |
| } |
| if (m_isPre920 == null) { |
| m_isPre920 = Boolean.TRUE; |
| } |
| } |
| return m_isPre920; |
| } |
| |
| public boolean geqOracle9() { |
| return m_geq9i; |
| } |
| |
| public static boolean geqOracle9(Connection conn) { |
| boolean geq9i = false; |
| try { |
| String dv = conn.getMetaData().getDatabaseProductVersion().toUpperCase(); |
| |
| int pos = dv.indexOf("ORACLE"); |
| if (0 < pos) { |
| dv = dv.substring(pos); |
| } |
| geq9i = dv.startsWith("ORACLE9") || dv.startsWith("ORACLE DATABASE 10G") |
| || dv.startsWith("ORACLE DATABASE 11G") || dv.startsWith("ORACLE1") |
| || dv.startsWith("ORACLE2") || dv.startsWith("ORACLE3"); |
| } |
| catch (SQLException se) { |
| /* assume the database is old */ |
| } |
| return geq9i; |
| } |
| |
| public void setTransitive(boolean transitive) { |
| m_transitive = transitive; |
| } |
| |
| // Return true if a column is null |
| public static boolean isNull(String col) { |
| return (col == null || col.length() == 0); |
| } |
| |
| public boolean noUserTypes() { |
| return m_userTypes.isEmpty(); |
| } |
| |
| public Map<String, SqlType> getTypeMap() { |
| return m_typeMap; |
| } |
| |
| /** |
| * Used for implicitly publishing SQL types Based on the -genPattern setting, sets the LangName |
| * associated with this SqlName. |
| */ |
| private LangName genPattern(LangName langName, String simpleName, boolean sql2Java) { |
| String genPattern = null; |
| if (genPattern == null) { |
| return langName; |
| } |
| @SuppressWarnings("unused") String useName = simpleName; |
| if (sql2Java) { |
| useName = SqlName.sqlIdToJavaId(simpleName, true); |
| } |
| // return sqlstring entry (e.g., a:b:c#d) according to m_genPattern. |
| // Entries in itemTokens are used to replace %? |
| // in m_genPattern. For instance, itemTokens can be |
| // - {sqlTypeName, JavaTypeName, JavaSubclassName, JavaItf} |
| // - {sqlTypeName} |
| String sql = genPattern; |
| for (int i = genPattern.length() - 1; i >= 0; i--) { |
| if (genPattern.charAt(i) == '%' && i != genPattern.length() - 1) { |
| int j = genPattern.charAt(i + 1) - '0'; |
| sql = sql.substring(0, i) + ((j == 1) ? simpleName : useName) |
| + sql.substring(i + 2); |
| // System.out.println("SqlName: sql = "+sql); //D+ |
| } |
| } |
| // Derive useName, subclassName, interface name |
| String useItf = null; |
| String generatedName = null; |
| String generatedItf = null; |
| StringTokenizer st = new StringTokenizer(sql, "#"); |
| String[] tokens = new String[st.countTokens()]; |
| for (int i = 0; 0 < st.countTokens(); i++) { |
| tokens[i] = st.nextToken(); |
| if (i == 1) { |
| generatedItf = tokens[1]; |
| useItf = tokens[1]; |
| } |
| } |
| st = new StringTokenizer(tokens[0], ":"); |
| tokens = new String[st.countTokens()]; |
| for (int i = 0; 0 < st.countTokens(); i++) { |
| tokens[i] = st.nextToken(); |
| if (i == 0) { |
| useName = tokens[0]; |
| } |
| else if (i == 1) { |
| useName = tokens[1]; |
| generatedName = tokens[0]; |
| } |
| } |
| if (useName == null) { |
| useItf = null; |
| } |
| if (generatedName == null) { |
| generatedItf = null; |
| } |
| if (useItf != null) { |
| generatedItf = null; |
| } |
| JavaName retJavaName = new JavaName("", useName, useItf, generatedName, generatedItf); |
| return retJavaName; |
| } |
| |
| public String determineSqlName(String name, boolean toBeDistinguished) { |
| // toBeDistinguished is a marker indicating that the name is chopped |
| // or post-fixed due to length over-limit or name conflicts. If true, |
| // the new name needs to go through another round of conflicts checking. |
| if (name.length() > MAX_IDENTIFIER_LENGTH) { |
| name = name.substring(0, MAX_IDENTIFIER_LENGTH); |
| toBeDistinguished = true; |
| } |
| String uniqueName = name.toUpperCase(); |
| if (m_allTypeNames.contains(uniqueName) |
| || (toBeDistinguished && m_allGeneratedTypeNames.contains(uniqueName))) { |
| do { |
| int len1 = name.length(); |
| int len2 = Integer.toString(m_allGeneratedTypeNamesMagicNumber++).length(); |
| if ((len1 + len2) > MAX_IDENTIFIER_LENGTH) { |
| name = name.substring(0, MAX_IDENTIFIER_LENGTH - len2); |
| } |
| uniqueName = name.toUpperCase() + m_allGeneratedTypeNamesMagicNumber; |
| } |
| while (m_allTypeNames.contains(uniqueName) |
| || (toBeDistinguished && m_allGeneratedTypeNames.contains(uniqueName))); |
| } |
| m_allGeneratedTypeNames.add(uniqueName); |
| return uniqueName; |
| } |
| |
| |
| public ViewCache getViewCache() { |
| return m_viewCache; |
| } |
| |
| public void getViewCache(ViewCache vc) { |
| m_viewCache = vc; |
| } |
| |
| public void close() { |
| try { |
| if (m_conn != null) { |
| m_conn.close(); |
| } |
| if (m_viewCache != null) { |
| m_viewCache.close(); |
| } |
| } |
| catch (Exception e) { |
| // Closing resources. OK to ignore exception. |
| } |
| } |
| |
| /** |
| * Returns all the user-defined SqlTypes. New types may be appended to the end of the |
| * enumeration as the existing types are published or navigated. |
| */ |
| public Iterator<TypeClass> getUserTypes() { |
| return m_userTypes.iterator(); |
| } |
| |
| /** |
| * @return metadata for generated PL/SQL wrapper package |
| */ |
| |
| public WrapperPackageMetadata getWrapperPackageMetadata() { |
| return m_wrapperPackageMetadata; |
| } |
| |
| public void addWrapperMethodMetadata(String name, String[] paramTypes, String[] paramNames, |
| String returnType) { |
| if (m_wrapperPackageMetadata == null) { |
| m_wrapperPackageMetadata = new WrapperPackageMetadata(""); |
| } |
| m_wrapperPackageMetadata.addMethod(new WrapperMethodMetadata(name, paramTypes, paramNames, |
| returnType)); |
| } |
| |
| } |