/* * DBeaver - Universal Database Manager * Copyright (C) 2013-2017 Denis Forveille (titou10.titou10@gmail.com) * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jkiss.dbeaver.ext.db2; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.ext.db2.info.DB2Parameter; import org.jkiss.dbeaver.ext.db2.info.DB2XMLString; import org.jkiss.dbeaver.ext.db2.model.DB2Bufferpool; import org.jkiss.dbeaver.ext.db2.model.DB2DataSource; import org.jkiss.dbeaver.ext.db2.model.DB2Index; import org.jkiss.dbeaver.ext.db2.model.DB2MaterializedQueryTable; import org.jkiss.dbeaver.ext.db2.model.DB2Package; import org.jkiss.dbeaver.ext.db2.model.DB2Routine; import org.jkiss.dbeaver.ext.db2.model.DB2Schema; import org.jkiss.dbeaver.ext.db2.model.DB2Sequence; import org.jkiss.dbeaver.ext.db2.model.DB2Table; import org.jkiss.dbeaver.ext.db2.model.DB2TableColumn; import org.jkiss.dbeaver.ext.db2.model.DB2Tablespace; import org.jkiss.dbeaver.ext.db2.model.DB2Trigger; import org.jkiss.dbeaver.ext.db2.model.DB2View; import org.jkiss.dbeaver.ext.db2.model.DB2XMLSchema; import org.jkiss.dbeaver.ext.db2.model.app.DB2ServerApplication; import org.jkiss.dbeaver.ext.db2.model.dict.DB2TablespaceDataType; import org.jkiss.dbeaver.ext.db2.model.fed.DB2Nickname; import org.jkiss.dbeaver.ext.db2.model.module.DB2Module; import org.jkiss.dbeaver.model.DBPEvaluationContext; import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.dbeaver.model.exec.jdbc.JDBCCallableStatement; import org.jkiss.dbeaver.model.exec.jdbc.JDBCPreparedStatement; import org.jkiss.dbeaver.model.exec.jdbc.JDBCResultSet; import org.jkiss.dbeaver.model.exec.jdbc.JDBCSession; import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; import org.jkiss.dbeaver.model.sql.SQLUtils; import org.jkiss.utils.CommonUtils; import java.sql.Clob; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * DB2 Utils * * @author Denis Forveille */ public class DB2Utils { private static final Log LOG = Log.getLog(DB2Utils.class); private static final String LINE_SEP = ";\n"; // TODO DF: many things in this class could probably be factorized or generic-field // DB2LOOK private static final String CALL_DB2LK_GEN = "CALL SYSPROC.DB2LK_GENERATE_DDL(?,?)"; private static final String CALL_DB2LK_CLEAN = "CALL SYSPROC.DB2LK_CLEAN_TABLE(?)"; private static final String SEL_DB2LK = "SELECT SQL_STMT FROM SYSTOOLS.DB2LOOK_INFO WHERE OP_TOKEN = ? ORDER BY OP_SEQUENCE WITH UR"; private static final String DB2LK_COMMAND = "-e -x -xd -td %s -t %s"; // EXPLAIN private static final String CALL_INST_OBJ = "CALL SYSPROC.SYSINSTALLOBJECTS(?,?,?,?)"; private static final int CALL_INST_OBJ_BAD_RC = -438; private static final String SEL_LIST_TS_EXPLAIN; static { StringBuilder sb = new StringBuilder(512); sb.append("SELECT TA.TBSPACE"); sb.append(" FROM SYSCAT.TBSPACEAUTH TA"); sb.append(" , SYSCAT.TABLESPACES T"); sb.append(" WHERE TA.GRANTEE IN ('PUBLIC',SESSION_USER)"); sb.append(" AND TA.USEAUTH In ('Y','G')"); sb.append(" AND T.TBSPACE = TA.TBSPACE"); sb.append(" AND T.DATATYPE IN ("); sb.append(" '").append(DB2TablespaceDataType.A.name()).append("'"); sb.append(" ,'").append(DB2TablespaceDataType.L.name()).append("'"); sb.append(" )"); sb.append(" ORDER BY TA.TBSPACE"); sb.append(" WITH UR"); SEL_LIST_TS_EXPLAIN = sb.toString(); } // ADMIN ACTIONS private static final String CALL_ADMIN_CMD = "CALL SYSPROC.ADMIN_CMD('%s')"; // DBCFG/DBMCFG/XMLStrings private static final String SEL_DBCFG = "SELECT * FROM SYSIBMADM.DBCFG ORDER BY NAME WITH UR"; private static final String SEL_DBMCFG = "SELECT * FROM SYSIBMADM.DBMCFG ORDER BY NAME WITH UR"; private static final String SEL_XMLSTRINGS = "SELECT * FROM SYSCAT.XMLSTRINGS ORDER BY STRINGID WITH UR"; // APPLICATIONS private static final String SEL_APP = "SELECT * FROM SYSIBMADM.APPLICATIONS WITH UR"; private static final String GET_MSG = "VALUES (SYSPROC.SQLERRM(?))"; // ------------------------ // Admin Command // ------------------------ public static void callAdminCmd(DBRProgressMonitor monitor, DB2DataSource dataSource, String command) throws SQLException { LOG.debug("Call admin_cmd with '" + command + "'"); String sql = String.format(CALL_ADMIN_CMD, command); monitor.beginTask("Executing command " + command, 1); try (JDBCSession session = DBUtils.openUtilSession(monitor, dataSource, "ADMIN_CMD")) { JDBCUtils.executeProcedure(session, sql); } finally { monitor.done(); } } // ------------------------ // Generate DDL // ------------------------ // DF: Use "Undocumented" SYSPROC.DB2LK_GENERATE_DDL stored proc // Ref to db2look : // http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2.luw.admin.cmd.doc/doc/r0002051.html // // Options of db2look that do not seem to work: -dp . "-a" seems to work on v10.1+, "-l" seems OK in all versions // // TODO DF: Tables in SYSTOOLS tables must exist first public static String generateDDLforTable(DBRProgressMonitor monitor, String statementDelimiter, DB2DataSource dataSource, DB2Table db2Table) throws DBException { LOG.debug("Generate DDL for " + db2Table.getFullyQualifiedName(DBPEvaluationContext.DDL)); // The DB2LK_GENERATE_DDL SP does not generate DDL for System Tables for some reason... // As a workaround, display a message to the end-user if (db2Table.getSchema().isSystem()) { return DB2Messages.no_ddl_for_system_tables; } // The DB2LK_GENERATE_DDL SP does not work when the name contains a space, even after trying to apply what is said in the // doc: // As a workaround, display a message to the end-user // Enclose multiword table names with the backslash and two sets of double quotation marks (for example, "\"My Table\"") // to prevent the pairing from being evaluated word-by-word by the command line processor (CLP). // If you use only one set of double quotation marks (for example, "My Table"), all words are converted into uppercase, // and the db2look command looks for an uppercase table name (for example, MY TABLE). if (db2Table.getFullyQualifiedName(DBPEvaluationContext.DDL).contains(" ")) { return DB2Messages.no_ddl_for_spaces_in_name; } monitor.beginTask("Generating DDL", 3); int token; StringBuilder sb = new StringBuilder(2048); String command = String.format(DB2LK_COMMAND, statementDelimiter, db2Table.getFullyQualifiedName(DBPEvaluationContext.DDL)); try (JDBCSession session = DBUtils.openMetaSession(monitor, dataSource, "Generate DDL")) { LOG.debug("Calling DB2LK_GENERATE_DDL with command : " + command); try (JDBCCallableStatement stmtSP = session.prepareCall(CALL_DB2LK_GEN)) { stmtSP.registerOutParameter(2, java.sql.Types.INTEGER); stmtSP.setString(1, command); stmtSP.executeUpdate(); token = stmtSP.getInt(2); } LOG.debug("Token = " + token); monitor.worked(1); // Read result try (JDBCPreparedStatement stmtSel = session.prepareStatement(SEL_DB2LK)) { stmtSel.setInt(1, token); try (JDBCResultSet dbResult = stmtSel.executeQuery()) { Clob ddlStmt; Long ddlLength; Long ddlStart = 1L; while (dbResult.next()) { ddlStmt = dbResult.getClob(1); try { ddlLength = ddlStmt.length() + 1L; sb.append(ddlStmt.getSubString(ddlStart, ddlLength.intValue())); sb.append(LINE_SEP); } finally { try { ddlStmt.free(); } catch (Throwable e) { LOG.debug("Error freeing CLOB: " + e.getMessage()); } } } } } monitor.worked(2); // Clean try (JDBCCallableStatement stmtSPClean = session.prepareCall(CALL_DB2LK_CLEAN)) { stmtSPClean.setInt(1, token); stmtSPClean.executeUpdate(); } monitor.worked(3); LOG.debug("Terminated OK"); return sb.toString(); } catch (SQLException e) { throw new DBException(e, dataSource); } finally { monitor.done(); } } // ------------------------ // Error Message // ------------------------ public static String getMessageFromCode(DB2DataSource db2DataSource, Integer sqlErrorCode) throws SQLException { try (JDBCSession session = DBUtils.openUtilSession(new VoidProgressMonitor(), db2DataSource, "Get Error Code")) { return JDBCUtils.queryString(session, GET_MSG, sqlErrorCode); } } // ------------------------ // EXPLAIN // ------------------------ public static List<String> getListOfUsableTsForExplain(DBRProgressMonitor monitor, JDBCSession session) throws SQLException { LOG.debug("Get List Of Usable Tablespaces For Explain Tables"); List<String> listTablespaces = new ArrayList<>(); try (JDBCPreparedStatement dbStat = session.prepareStatement(SEL_LIST_TS_EXPLAIN)) { try (JDBCResultSet dbResult = dbStat.executeQuery()) { while (dbResult.next()) { listTablespaces.add(dbResult.getString(1)); } } } return listTablespaces; } public static Boolean checkExplainTables(DBRProgressMonitor monitor, DB2DataSource dataSource, String explainTableSchemaName) throws DBCException { LOG.debug("Check EXPLAIN tables in '" + explainTableSchemaName + "'"); monitor.beginTask("Check EXPLAIN tables", 1); try (JDBCSession session = DBUtils.openMetaSession(monitor, dataSource, "Verify EXPLAIN tables")) { // First Check with given schema try (JDBCCallableStatement stmtSP = session.prepareCall(CALL_INST_OBJ)) { stmtSP.setString(1, "EXPLAIN"); // EXPLAIN stmtSP.setString(2, "V"); // Verify stmtSP.setString(3, ""); // Tablespace stmtSP.setString(4, explainTableSchemaName); // Schema stmtSP.execute(); LOG.debug("EXPLAIN tables with schema " + explainTableSchemaName + " found."); return true; } catch (SQLException e) { LOG.debug("RC:" + e.getErrorCode() + " SQLState:" + e.getSQLState() + " " + e.getMessage()); if (e.getErrorCode() == CALL_INST_OBJ_BAD_RC) { LOG.debug("No valid EXPLAIN tables found in schema '" + explainTableSchemaName + "'."); return false; } throw new DBCException(e, dataSource); } } finally { monitor.done(); } } public static void createExplainTables(DBRProgressMonitor monitor, DB2DataSource dataSource, String explainTableSchemaName, String tablespaceName) throws DBCException { LOG.debug("Create EXPLAIN tables in " + explainTableSchemaName); monitor.beginTask("Create EXPLAIN Tables", 1); try (JDBCSession session = DBUtils.openMetaSession(monitor, dataSource, "Create EXPLAIN tables")) { try (JDBCCallableStatement stmtSP = session.prepareCall(CALL_INST_OBJ)) { stmtSP.setString(1, "EXPLAIN"); // EXPLAIN stmtSP.setString(2, "C"); // Create stmtSP.setString(3, tablespaceName); // Tablespace stmtSP.setString(4, explainTableSchemaName); // Schema stmtSP.executeUpdate(); LOG.debug("Creation EXPLAIN Tables : OK"); } catch (SQLException e) { LOG.error("SQLException occured during EXPLAIN tables creation in schema " + explainTableSchemaName, e); throw new DBCException(e, dataSource); } } finally { monitor.done(); } } // --------------------- // DBA Data and Actions // --------------------- public static List<DB2ServerApplication> readApplications(DBRProgressMonitor monitor, JDBCSession session) throws SQLException { LOG.debug("readApplications"); List<DB2ServerApplication> listApplications = new ArrayList<>(); try (JDBCPreparedStatement dbStat = session.prepareStatement(SEL_APP)) { try (JDBCResultSet dbResult = dbStat.executeQuery()) { while (dbResult.next()) { listApplications.add(new DB2ServerApplication(dbResult)); } } } return listApplications; } public static List<DB2Parameter> readDBCfg(DBRProgressMonitor monitor, JDBCSession session) throws SQLException { LOG.debug("readDBCfg"); List<DB2Parameter> listDBParameters = new ArrayList<>(); try (JDBCPreparedStatement dbStat = session.prepareStatement(SEL_DBCFG)) { try (JDBCResultSet dbResult = dbStat.executeQuery()) { while (dbResult.next()) { listDBParameters.add(new DB2Parameter((DB2DataSource) session.getDataSource(), dbResult)); } } } return listDBParameters; } public static List<DB2Parameter> readDBMCfg(DBRProgressMonitor monitor, JDBCSession session) throws SQLException { LOG.debug("readDBMCfg"); List<DB2Parameter> listDBMParameters = new ArrayList<>(); try (JDBCPreparedStatement dbStat = session.prepareStatement(SEL_DBMCFG)) { try (JDBCResultSet dbResult = dbStat.executeQuery()) { while (dbResult.next()) { listDBMParameters.add(new DB2Parameter((DB2DataSource) session.getDataSource(), dbResult)); } } } return listDBMParameters; } public static List<DB2XMLString> readXMLStrings(DBRProgressMonitor monitor, JDBCSession session) throws SQLException { LOG.debug("readXMLStrings"); List<DB2XMLString> listXMLStrings = new ArrayList<>(); try (JDBCPreparedStatement dbStat = session.prepareStatement(SEL_XMLSTRINGS)) { try (JDBCResultSet dbResult = dbStat.executeQuery()) { while (dbResult.next()) { listXMLStrings.add(new DB2XMLString((DB2DataSource) session.getDataSource(), dbResult)); } } } return listXMLStrings; } // --------------- // Objects Finders // --------------- public static DB2Tablespace findTablespaceById(DBRProgressMonitor monitor, DB2DataSource db2DataSource, Integer tablespaceId) throws DBException { if (db2DataSource == null) { return null; } for (DB2Tablespace db2Tablespace : db2DataSource.getTablespaces(monitor)) { if (CommonUtils.equalObjects(db2Tablespace.getTbspaceId(), tablespaceId)) { return db2Tablespace; } } return null; } public static DB2Bufferpool findBufferpoolById(DBRProgressMonitor monitor, DB2DataSource db2DataSource, Integer bufferpoolId) throws DBException { if (db2DataSource == null) { return null; } for (DB2Bufferpool db2Bufferpool : db2DataSource.getBufferpools(monitor)) { if (CommonUtils.equalObjects(db2Bufferpool.getId(), bufferpoolId)) { return db2Bufferpool; } } return null; } public static DB2TableColumn findColumnBySchemaNameAndTableNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2TableName, String db2ColumnName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } DB2Table db2Table = db2Schema.getTable(monitor, db2TableName); if (db2Table == null) { return null; } return db2Table.getAttribute(monitor, db2ColumnName); } public static DB2Index findIndexBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2IndexName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getIndex(monitor, db2IndexName); } public static DB2Module findModuleBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2ModuleName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getModule(monitor, db2ModuleName); } public static DB2Nickname findNicknameBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2NicknameName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getNickname(monitor, db2NicknameName); } public static DB2MaterializedQueryTable findMaterializedQueryTableBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2NicknameName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getMaterializedQueryTable(monitor, db2NicknameName); } public static DB2Package findPackageBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2PackageName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getPackage(monitor, db2PackageName); } public static DB2Routine findMethodBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2MethodName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getMethod(monitor, db2MethodName); } public static DB2Routine findProcedureBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2ProcedureName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getProcedure(monitor, db2ProcedureName); } public static DB2Sequence findSequenceBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2SequenceName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getSequence(monitor, db2SequenceName); } public static DB2Table findTableBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2TableName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getTable(monitor, db2TableName); } public static DB2Trigger findTriggerBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2TriggerName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getTrigger(monitor, db2TriggerName); } public static DB2Routine findUDFBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2FunctionName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getUDF(monitor, db2FunctionName); } public static DB2View findViewBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2ViewName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getView(monitor, db2ViewName); } public static DB2MaterializedQueryTable findMQTBySchemaNameAndName(DBRProgressMonitor monitor, DB2DataSource db2DataSource, String db2SchemaName, String db2MQTName) throws DBException { DB2Schema db2Schema = db2DataSource.getSchema(monitor, db2SchemaName); if (db2Schema == null) { return null; } return db2Schema.getMaterializedQueryTable(monitor, db2MQTName); } public static DB2XMLSchema findXMLSchemaByById(DBRProgressMonitor monitor, DB2DataSource db2DataSource, Long xmlSchemaId) throws DBException { if (db2DataSource == null) { return null; } // We have to iterate all Schemas... for (DB2Schema db2Schema : db2DataSource.getSchemas(monitor)) { for (DB2XMLSchema db2XMLSchema : db2Schema.getXMLSchemas(monitor)) { if (db2XMLSchema.getId().equals(xmlSchemaId)) { return db2XMLSchema; } } } return null; } public static String formatSQLProcedureDDL(DB2DataSource db2DataSource, String rawText) { // First let the defaut SQL formater operate String result = SQLUtils.formatSQL(db2DataSource, rawText); // Put some kwywords on the same line result = result.replace("CREATE\r\n PROCEDURE", "CREATE PROCEDURE"); result = result.replace("\r\nOR REPLACE", " OR REPLACE"); // Put each definition keywords on one line result = result.replace(" LANGUAGE ", "\r\nLANGUAGE "); result = result.replace(" SPECIFIC ", "\r\nSPECIFIC "); result = result.replace(" DYNAMIC RESULT ", "\r\nDYNAMIC RESULT "); result = result.replace(" MODIFIES SQL ", "\r\nMODIFIES SQL "); result = result.replace(" CONTAINS SQL ", "\r\nCONTAINS SQL "); result = result.replace(" READS SQL DATA ", "\r\nREADS SQL DATA "); result = result.replace(" NOT DETERMINISTIC ", "\r\nNOT DETERMINISTIC "); result = result.replace(" DETERMINISTIC ", "\r\nDETERMINISTIC "); result = result.replace(" CALLED ON NULL INPUT ", "\r\nCALLED ON NULL INPUT "); result = result.replace(" COMMIT ON RETURN ", "\r\nCOMMIT ON RETURN "); result = result.replace(" AUTONOMOUS ", "\r\nAUTONOMOUS "); result = result.replace(" INHERIT SPECIAL ", "\r\nINHERIT SPECIAL "); result = result.replace(" OLD SAVEPOINT ", "\r\nOLD SAVEPOINT "); result = result.replace(" NEW SAVEPOINT ", "\r\nNEW SAVEPOINT "); result = result.replace(" EXTERNAL ACTION ", "\r\nEXTERNAL ACTION "); result = result.replace(" NO EXTERNAL ", "\r\nNO EXTERNAL "); result = result.replace(" PARAMETER CCSID ", "\r\nPARAMETER CCSID "); result = result.replace(" BEGIN ", "\r\nBEGIN\r\n"); // Put a CR after ";" result = result.replaceAll(";", ";\r\n"); // Suppress the CRs before ";" result = result.replaceAll("\\r\\n;", ";"); // Remove CR space result = result.replaceAll("\\r\\n ", "\r\n"); // Remove some CRs result = result.replaceAll("SET\\r\\n", "SET "); result = result.replaceAll("INTO\\r\\n", "INTO "); result = result.replaceAll("FROM\\r\\n", "FROM "); result = result.replaceAll("FETCH\\r\\n", "FETCH "); result = result.replaceAll("WHERE\\r\\n", "WHERE "); return result; } private DB2Utils() { // Pure utility class, no instanciation allowed } }