All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.flywaydb.community.database.db2z.DB2ZSchema Maven / Gradle / Ivy

/*-
 * ========================LICENSE_START=================================
 * flyway-database-db2zos
 * ========================================================================
 * Copyright (C) 2010 - 2024 Red Gate Software Ltd
 * ========================================================================
 * 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.
 * =========================LICENSE_END==================================
 */
/*
 * Copyright (C) Red Gate Software Ltd 2010-2022
 *
 * 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.flywaydb.community.database.db2z;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import lombok.CustomLog;
import org.flywaydb.core.internal.database.base.Function;
import org.flywaydb.core.internal.database.base.Schema;
import org.flywaydb.core.internal.database.base.Table;
import org.flywaydb.core.internal.database.base.Type;
import org.flywaydb.core.internal.jdbc.JdbcTemplate;

/**
 * DB2 implementation of Schema.
 */
@CustomLog
public class DB2ZSchema extends Schema {
    /**
     * Creates a new DB2 schema.
     *
     * @param jdbcTemplate The Jdbc Template for communicating with the DB.
     * @param database     The database-specific support.
     * @param name         The name of the schema.
     */
    DB2ZSchema(JdbcTemplate jdbcTemplate, DB2ZDatabase database, String name) {
        super(jdbcTemplate, database, name);
    }

    @Override
    protected boolean doExists() throws SQLException {
		/**
		* For DB2 on z/OS, a schema is not an object that can be created or dropped and is not listed in the catalog. 
		* Instead, we do need to check whether the database exists (which is a container for tablespaces and other storage related objects)
		*/
        return jdbcTemplate.queryForInt("SELECT COUNT(*) FROM sysibm.sysdatabase WHERE name=?", database.getName()) > 0;
    }

    @Override
    protected boolean doEmpty() throws SQLException {
        int objectCount = jdbcTemplate.queryForInt("select count(*) from sysibm.systables where dbname = ? AND creator = ?", database.getName(), name);
        objectCount += jdbcTemplate.queryForInt("select count(*) from sysibm.syssequences where schema = ?", name);
        objectCount += jdbcTemplate.queryForInt("select count(*) from sysibm.sysindexes where creator = ?", name);
        objectCount += jdbcTemplate.queryForInt("select count(*) from sysibm.sysroutines where schema = ?", name);
        objectCount += jdbcTemplate.queryForInt("select count(*) from sysibm.systriggers where schema = ?", name);
        return objectCount == 0;
    }

    @Override
    protected void doCreate() throws SQLException {
        throw new UnsupportedOperationException("Create Schema - is not supported in db2 on zOS");
    }

    @Override
    protected void doDrop() throws SQLException {
        throw new UnsupportedOperationException("Drop Schema - is not supported in db2 on zOS");
    }

    @Override
    protected void doClean() throws SQLException {
        // MQTs are dropped when the backing views or tables are dropped
        // Indexes in DB2 are dropped when the corresponding table is dropped

             // drop versioned table link -> not supported for DB2 9.x
            List dropVersioningStatements = generateDropVersioningStatement();
            if (!dropVersioningStatements.isEmpty()) {
                // Do a explicit drop of MQTs in order to be able to drop the Versioning
                for (String dropTableStatement : generateDropStatements("M", "TABLE")) {
                    jdbcTemplate.execute(dropTableStatement);
                }
            }

            for (String dropVersioningStatement : dropVersioningStatements) {
                jdbcTemplate.execute(dropVersioningStatement);
            }
        
            // diable archiving on table
            List disableArchivingStatements = generateDisableArchivingStatement();
            if (!disableArchivingStatements.isEmpty()) {
                // Do a explicit drop of MQTs in order to be able to drop the Versioning
                for (String dropTableStatement : generateDropStatements("M", "TABLE")) {
                    jdbcTemplate.execute(dropTableStatement);
                }
            }

            for (String disableArchivingStatement : disableArchivingStatements) {
                jdbcTemplate.execute(disableArchivingStatement);
            }

        // views
        /* We need to query for all views in schema after each DROP because of a
         * specific property in z/OS to DROP dependent nested views (views depending on other views).
         */
        List dropStatements = generateDropStatements("V", "VIEW");
        while(dropStatements.size() != 0) {
           	String dropStatement = dropStatements.get(0);
           	jdbcTemplate.execute(dropStatement);
         	dropStatements = generateDropStatements("V", "VIEW");
        }

        // aliases
        for (String dropStatement : generateDropStatements("A", "ALIAS")) {
            jdbcTemplate.execute(dropStatement);
        }

        for (Table table : allTables()) {
            table.drop();
        }

        // temporary Tables
        for (String dropStatement : generateDropStatements("G", "TABLE")) {
            jdbcTemplate.execute(dropStatement);
        }

        // explicit tablespace
        for (String dropStatement : generateDropStatementsForRegularTablespace()) {
            jdbcTemplate.execute(dropStatement);
        }
        
        for (String dropStatement : generateDropStatementsForLobTablespace()) {
            jdbcTemplate.execute(dropStatement);
        }

        // sequences
        for (String dropStatement : generateDropStatementsForSequences()) {
            jdbcTemplate.execute(dropStatement);
        }

        // procedures
        for (String dropStatement : generateDropStatementsForProcedures()) {
            jdbcTemplate.execute(dropStatement);
        }

        // triggers
        for (String dropStatement : generateDropStatementsForTriggers()) {
            jdbcTemplate.execute(dropStatement);
        }
  
        for (Function function : allFunctions()) {
            function.drop();
        }

        // types. TODO: find out, why generic drop type function is not working at all times with Db2Z
        // For now, call the one that is working for sure
        for (String dropStatement : generateDropStatementsForTypes()) {
            jdbcTemplate.execute(dropStatement);
        }

        for (Type type : allTypes()) {
            type.drop();
        }
    }

    private String getSqlId() {
        /**
         * Get SQLID.
         * When sqlid not set, implicitly use schema name for sqlid
         */
        String sqlId = (database.getSqlId() == "") ?name : database.getSqlId();
        return sqlId;
    }

    /**
     * Generates DROP statements for the procedures in this schema.
     *
     * @return The drop statements.
     * @throws SQLException when the statements could not be generated.
     */
    private List generateDropStatementsForProcedures() throws SQLException {
        String dropProcGenQuery = "select rtrim(NAME) from SYSIBM.SYSROUTINES where CAST_FUNCTION = 'N' " +
                " and ROUTINETYPE  = 'P' and SCHEMA = '" + name + "' and OWNER = '" + this.getSqlId() + "'";
        return buildDropStatements("DROP PROCEDURE", dropProcGenQuery);
    }

    /**
     * Generates DROP statements for the sequences in this schema.
     *
     * @return The drop statements.
     * @throws SQLException when the statements could not be generated.
     */
    private List generateDropStatementsForSequences() throws SQLException {
        String dropSeqGenQuery = "select rtrim(NAME) from SYSIBM.SYSSEQUENCES where SCHEMA = '" + name
                + "' and SEQTYPE='S' and OWNER = '" + this.getSqlId() + "'";
        return buildDropStatements("DROP SEQUENCE", dropSeqGenQuery);
    }

    /**
     * Generates DROP statements for the explicitly-created tablespaces in this database with the schema user as creator.
     *
     * @return The drop statements.
     * @throws SQLException when the statements could not be generated.
     */
    private List generateDropStatementsForRegularTablespace() throws SQLException {
		//Only drop explicitly created tablespaces for current database and created under this specific schema authorization ID
		//Note that this also drops the related table for partitioned tablespaces.
        String dropTablespaceGenQuery = "select rtrim(NAME) FROM SYSIBM.SYSTABLESPACE where IMPLICIT = 'N' AND DBNAME = '" + database.getName() + "' AND CREATOR = '" + this.getSqlId() + "' AND TYPE <> 'O'";

        List dropStatements = new ArrayList<>();
        List dbObjects = jdbcTemplate.queryForStringList(dropTablespaceGenQuery);
        for (String dbObject : dbObjects) {
            LOG.debug("DROP TABLESPACE " + database.quote(database.getName(), dbObject));
            dropStatements.add("DROP TABLESPACE " + database.quote(database.getName(), dbObject));
        }
        return dropStatements;
    }
    
    private List generateDropStatementsForLobTablespace() throws SQLException {
     	String dropTablespaceGenQuery = "select rtrim(NAME) FROM SYSIBM.SYSTABLESPACE where IMPLICIT = 'N' AND DBNAME = '" + database.getName() + "' AND CREATOR = '" + this.getSqlId() + "' AND TYPE = 'O'";

        List dropStatements = new ArrayList<>();
        List dbObjects = jdbcTemplate.queryForStringList(dropTablespaceGenQuery);
        for (String dbObject : dbObjects) {
            LOG.debug("DROP TABLESPACE " + database.quote(database.getName(), dbObject));
            dropStatements.add("DROP TABLESPACE " + database.quote(database.getName(), dbObject));
        }
        return dropStatements;
    }

    /**
     * Generates DROP statements for this type of table, representing this type of object in this schema.
     *
     * @param tableType  The type of table (Can be T, V, S, ...).
     * @param objectType The type of object.
     * @return The drop statements.
     * @throws SQLException when the statements could not be generated.
     */
    private List generateDropStatements(String tableType, String objectType) throws SQLException {
        String dropTablesGenQuery = "select rtrim(NAME) from SYSIBM.SYSTABLES where TYPE='" + tableType + "' and OWNER = '" + this.getSqlId() + "' AND CREATOR = '" + name + "'";
        return buildDropStatements("DROP " + objectType, dropTablesGenQuery);
    }

    /**
     * Generates DROP statements for the triggers in this schema.
     *
     * @return The drop statements.
     * @throws SQLException when the statements could not be generated.
     */
    private List generateDropStatementsForTriggers() throws SQLException {
        String dropTrigGenQuery = "select TRIGNAME from SYSIBM.SYSTRIGGERS where SCHEMA = '" + name + "' and OWNER = '" + this.getSqlId() + "'";
        LOG.debug(dropTrigGenQuery);
        return buildDropStatements("DROP TRIGGER", dropTrigGenQuery);
    }

    /**
     * Generates DROP statements for the types in this schema.
     *
     * @return The drop statements.
     * @throws SQLException when the statements could not be generated.
     */
    private List generateDropStatementsForTypes() throws SQLException {
        String dropProcGenQuery = "select rtrim(NAME) from SYSIBM.SYSROUTINES where CAST_FUNCTION = 'Y' " +
                " and ROUTINETYPE  = 'T' and SCHEMA = '" + name + "' and OWNER = '" + this.getSqlId() + "'";
        return buildDropStatements("DROP PROCEDURE", dropProcGenQuery);
    }

    /**
     * Builds the drop statements for database objects in this schema.
     *
     * @param dropPrefix The drop command for the database object (e.g. 'drop table').
     * @param query      The query to get all present database objects
     * @return The statements.
     * @throws SQLException when the drop statements could not be built.
     */
    private List buildDropStatements(final String dropPrefix, final String query) throws SQLException {
        List dropStatements = new ArrayList<>();
        List dbObjects = jdbcTemplate.queryForStringList(query);
        for (String dbObject : dbObjects) {
            LOG.debug(dropPrefix + " " + database.quote(name, dbObject));
            dropStatements.add(dropPrefix + " " + database.quote(name, dbObject));
        }
        return dropStatements;
    }

    /**
     * @return All tables that have versioning associated with them.
     */
    private List generateDropVersioningStatement() throws SQLException {
        List dropVersioningStatements = new ArrayList<>();
        Table[] versioningTables = findTables("select rtrim(NAME) from SYSIBM.SYSTABLES where VERSIONING_TABLE <> '' and CREATOR = '" + name + "' and OWNER = '" + this.getSqlId() + "'");
        for (Table table : versioningTables) {
            LOG.debug("ALTER TABLE " + table.toString() + " DROP VERSIONING");
            dropVersioningStatements.add("ALTER TABLE " + table.toString() + " DROP VERSIONING");
        }

        return dropVersioningStatements;
    }

    /**
     * @return All tables that have archiving associated with them.
     */
    private List generateDisableArchivingStatement() throws SQLException {
        List dropArchivingStatements = new ArrayList<>();
        Table[] archivingTables = findTables("select rtrim(NAME) from SYSIBM.SYSTABLES where ARCHIVING_TABLE <> '' and CREATOR = '" + name + "' and OWNER = '" + this.getSqlId() + "'");
        for (Table table : archivingTables) {
            LOG.debug("ALTER TABLE " + table.toString() + " DISABLE ARCHIVE");
            dropArchivingStatements.add("ALTER TABLE " + table.toString() + " DISABLE ARCHIVE");
        }
        return dropArchivingStatements;
    }

    private DB2ZTable[] findTables(String sqlQuery, String... params) throws SQLException {
        List tableNames = jdbcTemplate.queryForStringList(sqlQuery, params);
        DB2ZTable[] tables = new DB2ZTable[tableNames.size()];
        for (int i = 0; i < tableNames.size(); i++) {
            tables[i] = new DB2ZTable(jdbcTemplate, database, this, tableNames.get(i));
        }
        return tables;
    }

    @Override
    protected DB2ZTable[] doAllTables() throws SQLException {
        return findTables("select rtrim(NAME) from SYSIBM.SYSTABLES where TYPE='T' and CREATOR = '" + name + "' and OWNER = '" + this.getSqlId() + "'");
    }

    @Override
    protected Function[] doAllFunctions() throws SQLException {
        List functionNames = jdbcTemplate.queryForStringList(
                "select rtrim(SPECIFICNAME) from SYSIBM.SYSROUTINES where"
                        // Functions only
                        + " ROUTINETYPE='F'"
                        // That aren't system-generated or built-in
                        + " AND ORIGIN IN ("
                        + "'E', " // User-defined, external
                        + "'M', " // Template function
                        + "'Q', " // SQL-bodied
                        + "'U')"  // User-defined, based on a source
                        + " and SCHEMA = '" + name + "' and OWNER = '" + this.getSqlId() + "'");

        List functions = new ArrayList<>();
        for (String functionName : functionNames) {
            functions.add(getFunction(functionName));
        }

        return functions.toArray(new Function[0]);
    }

    @Override
    public Table getTable(String tableName) {
        return new DB2ZTable(jdbcTemplate, database, this, tableName);
    }

    @Override
    protected Type getType(String typeName) {
        return new DB2ZType(jdbcTemplate, database, this, typeName);
    }

    @Override
    public Function getFunction(String functionName, String... args) {
        return new DB2ZFunction(jdbcTemplate, database, this, functionName, args);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy