com.tesora.dve.sql.statement.ddl.PEDropTableStatement.java Source code

Java tutorial

Introduction

Here is the source code for com.tesora.dve.sql.statement.ddl.PEDropTableStatement.java

Source

package com.tesora.dve.sql.statement.ddl;

/*
 * #%L
 * Tesora Inc.
 * Database Virtualization Engine
 * %%
 * Copyright (C) 2011 - 2014 Tesora Inc.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;

import com.tesora.dve.common.MultiMap;
import com.tesora.dve.common.catalog.CatalogDAO;
import com.tesora.dve.common.catalog.CatalogEntity;
import com.tesora.dve.common.catalog.UserTable;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.queryplan.QueryStepDDLGeneralOperation.DDLCallback;
import com.tesora.dve.server.connectionmanager.SSConnection;
import com.tesora.dve.server.messaging.SQLCommand;
import com.tesora.dve.sql.ParserException.Pass;
import com.tesora.dve.sql.SchemaException;
import com.tesora.dve.sql.expression.TableKey;
import com.tesora.dve.sql.schema.ComplexPETable;
import com.tesora.dve.sql.schema.Name;
import com.tesora.dve.sql.schema.PEAbstractTable;
import com.tesora.dve.sql.schema.PEContainer;
import com.tesora.dve.sql.schema.PEDatabase;
import com.tesora.dve.sql.schema.PEForeignKey;
import com.tesora.dve.sql.schema.PETable;
import com.tesora.dve.sql.schema.PETrigger;
import com.tesora.dve.sql.schema.QualifiedName;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.schema.cache.CacheInvalidationRecord;
import com.tesora.dve.sql.schema.cache.InvalidationScope;
import com.tesora.dve.sql.schema.cache.SchemaCacheKey;
import com.tesora.dve.sql.statement.StatementType;
import com.tesora.dve.sql.transform.behaviors.BehaviorConfiguration;
import com.tesora.dve.sql.transform.execution.ComplexDDLExecutionStep;
import com.tesora.dve.sql.transform.execution.EmptyExecutionStep;
import com.tesora.dve.sql.transform.execution.ExecutionSequence;
import com.tesora.dve.sql.transform.execution.ExecutionStep;
import com.tesora.dve.sql.util.ListOfPairs;
import com.tesora.dve.worker.WorkerGroup;

public class PEDropTableStatement extends PEDropStatement<PETable, UserTable> {

    protected Set<CatalogEntity> deletes = null;
    protected Set<CatalogEntity> updates = null;
    protected Set<PEAbstractTable<?>> referenced = null;

    private List<TableKey> tableKeys;
    private List<Name> unknownTables;

    private CacheInvalidationRecord cacheInvalidationRecord = null;

    private boolean temporary;

    public PEDropTableStatement(SchemaContext sc, List<TableKey> tks, List<Name> unknownTbls, Boolean ifExists,
            boolean temporary) {
        super(PETable.class, ifExists, false,
                (tks.isEmpty() ? unknownTbls.get(0) : tks.get(0).getAbstractTable().getName(sc, sc.getValues())),
                "TABLE");
        this.tableKeys = new ArrayList<TableKey>(tks);
        this.unknownTables = unknownTbls;
        this.temporary = temporary;
    }

    public PEDropTableStatement(PEDropTableStatement base) {
        super(PETable.class, base.isIfExists(), false, base.getTarget(), "TABLE");
        this.tableKeys = new ArrayList<TableKey>(base.tableKeys);
        this.unknownTables = new ArrayList<Name>(base.unknownTables);
        this.temporary = base.temporary;
    }

    @Override
    public PETable getTarget() {
        if (tableKeys.isEmpty())
            return null;
        return tableKeys.get(0).getAbstractTable().asTable();
    }

    public List<TableKey> getDroppedTableKeys() {
        return tableKeys;
    }

    public boolean isTemporary() {
        return temporary;
    }

    @Override
    public PEDatabase getDatabase(SchemaContext pc) {
        if (getRoot() != null)
            return ((PETable) getRoot()).getPEDatabase(pc);
        return super.getDatabase(pc);
    }

    @Override
    public List<CatalogEntity> getCatalogObjects(SchemaContext pc) throws PEException {
        compute(pc, false);
        return new ArrayList<CatalogEntity>(updates);
    }

    @Override
    public List<CatalogEntity> getDeleteObjects(SchemaContext pc) throws PEException {
        compute(pc, false);
        return new ArrayList<CatalogEntity>(deletes);
    }

    @Override
    protected void preplan(SchemaContext pc, ExecutionSequence es, boolean explain) throws PEException {
        if (temporary)
            return;
        super.preplan(pc, es, explain);
    }

    protected static void compute(SchemaContext pc, Set<CatalogEntity> deletes, Set<CatalogEntity> updates,
            List<TableKey> keys, Set<PEAbstractTable<?>> referencedTables, boolean ignoreFKChecks)
            throws PEException {
        if (keys.isEmpty())
            return;
        if (!deletes.isEmpty())
            return;
        for (TableKey tk : keys) {
            PETable tab = tk.getAbstractTable().asTable();
            appendChildTriggersToCollection(pc, tab, deletes);
            List<PEForeignKey> effectedForeignKeys = new ArrayList<PEForeignKey>();
            if (!tk.isUserlandTemporaryTable())
                checkForeignKeys(pc, tab, effectedForeignKeys, referencedTables, ignoreFKChecks);
            pc.beginSaveContext();
            try {
                if (tk.isUserlandTemporaryTable()) {
                    // for userland temp tables, we only need to toss out the temporary table record
                    deletes.addAll(pc.getCatalog().findUserlandTemporaryTable(pc.getConnection().getConnectionId(),
                            tab.getDatabaseName(pc).getUnquotedName().get(),
                            tab.getName().getUnquotedName().get()));
                } else {
                    deletes.add(tab.persistTree(pc));
                    if (tab.isContainerBaseTable(pc)) {
                        PEContainer cont = tab.getDistributionVector(pc).getContainer(pc);
                        List<UserTable> anyTabs = pc.getCatalog().findContainerMembers(cont.getName().get());
                        if (anyTabs.size() == 1) {
                            // last table - this is ok
                            cont.setBaseTable(pc, null);
                            updates.add(cont.persistTree(pc));
                            // we're also going to delete all of the container tenants
                            try {
                                deletes.addAll(
                                        pc.getCatalog().getDAO().findContainerTenants(cont.getPersistent(pc)));
                            } catch (Exception e) {
                                throw new PEException(
                                        "Unable to find container tenants of container " + cont.getName(), e);
                            }
                        } else {
                            // more than one table left - not ok
                            throw new SchemaException(Pass.PLANNER,
                                    "Unable to drop table " + tab.getName().getSQL()
                                            + " because it is the base table to container "
                                            + cont.getName().getSQL() + " which is not empty");
                        }
                    }
                    for (PEForeignKey pefk : effectedForeignKeys) {
                        // this should be persisting the whole table, not the key
                        updates.add(pefk.persistTree(pc));
                    }
                }
            } finally {
                pc.endSaveContext();
            }
        }

    }

    private static void appendChildTriggersToCollection(final SchemaContext pc, final PETable parent,
            final Set<CatalogEntity> collection) {
        for (final PETrigger trigger : parent.getAllTriggers(pc)) {
            collection.add(trigger.getPersistent(pc));
        }
    }

    protected void compute(SchemaContext pc, boolean ignoreFKChecks) throws PEException {
        if (tableKeys.isEmpty())
            return;
        if (deletes != null)
            return;
        deletes = new HashSet<CatalogEntity>();
        updates = new HashSet<CatalogEntity>();
        referenced = new HashSet<PEAbstractTable<?>>();
        compute(pc, deletes, updates, tableKeys, referenced, ignoreFKChecks);
    }

    protected static void checkForeignKeys(SchemaContext pc, PETable targetTable, List<PEForeignKey> updatedKeys,
            Set<PEAbstractTable<?>> referencedTables, boolean ignoreFKChecks) {
        emitReferencedTables(pc, targetTable, referencedTables);
        MultiMap<PETable, PEForeignKey> referencing = pc.findFKSReferencing(targetTable);
        if (!referencing.isEmpty()) {
            // otherwise we have to fix up the pertinent fks to use strings instead of fk refs
            for (PEForeignKey pefk : referencing.values()) {
                pefk.revertToForward(pc);
                updatedKeys.add(pefk);
            }
        }
    }

    private static void emitReferencedTables(final SchemaContext pc, final PETable targetTable,
            final Set<PEAbstractTable<?>> container) {
        for (final PEForeignKey fk : targetTable.getForeignKeys(pc)) {
            final PEAbstractTable<?> child = fk.getTargetTable(pc);
            // The child may have already been dropped in a multi-table statement. 
            if (child != null) {
                container.add(child);
            }
        }
    }

    @Override
    public StatementType getStatementType() {
        return StatementType.DROP_TABLE;
    }

    @Override
    public boolean filterStatement(SchemaContext pc) {
        if (tableKeys.isEmpty())
            return false;

        for (TableKey tk : tableKeys) {
            PEAbstractTable<?> tbl = tk.getAbstractTable();
            if (tbl != null) {
                if (pc.getConnection().isFilteredTable(
                        new QualifiedName(tbl.getDatabase(pc).getName().getUnquotedName().getUnqualified(),
                                tbl.getName().getUnquotedName().getUnqualified()))) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public void plan(SchemaContext pc, ExecutionSequence es, BehaviorConfiguration config) throws PEException {
        if (!tableKeys.isEmpty())
            es.append(buildStep(pc));
        else
            es.append(new EmptyExecutionStep(0, "already dropped: " + getSQL(pc)));
    }

    @Override
    protected ExecutionStep buildStep(SchemaContext pc) throws PEException {
        final List<CatalogEntity> updates = getCatalogObjects(pc);
        final List<CatalogEntity> deletes = getDeleteObjects(pc);
        return new ComplexDDLExecutionStep(getDatabase(pc), getStorageGroup(pc), getRoot(), getAction(),
                new DropTableCallback(pc, getSQLCommand(pc), getInvalidationRecord(pc), updates, deletes));
    }

    @Override
    public CacheInvalidationRecord getInvalidationRecord(SchemaContext sc) {
        if (tableKeys.isEmpty())
            return null;

        if (cacheInvalidationRecord != null)
            return cacheInvalidationRecord;

        ListOfPairs<SchemaCacheKey<?>, InvalidationScope> actions = new ListOfPairs<SchemaCacheKey<?>, InvalidationScope>();
        for (TableKey tk : tableKeys) {
            if (!tk.isUserlandTemporaryTable()) {
                actions.add(tk.getAbstractTable().getCacheKey(), InvalidationScope.CASCADE);
            }
        }

        for (final PEAbstractTable<?> child : referenced) {
            actions.add(child.getCacheKey(), InvalidationScope.LOCAL);
        }
        cacheInvalidationRecord = new CacheInvalidationRecord(actions);

        return cacheInvalidationRecord;
    }

    private class DropTableCallback extends DDLCallback {

        private final SQLCommand command;
        private final CacheInvalidationRecord record;
        private final List<CatalogEntity> updates;
        private final List<CatalogEntity> deletes;
        private final SchemaContext context;

        public DropTableCallback(SchemaContext ctxt, SQLCommand theCommand, CacheInvalidationRecord theRecord,
                List<CatalogEntity> updates, List<CatalogEntity> deletes) {
            this.command = theCommand;
            this.record = theRecord;
            this.updates = updates;
            this.deletes = deletes;
            this.context = ctxt;
        }

        @Override
        public SQLCommand getCommand(CatalogDAO c) {
            return command;
        }

        @Override
        public String description() {
            return command.getRawSQL();
        }

        @Override
        public CacheInvalidationRecord getInvalidationRecord() {
            return record;
        }

        public void postCommitAction(CatalogDAO c) throws PEException {
            if (unknownTables != null && unknownTables.size() > 0) {
                if (!isIfExists()) {
                    throw new PEException("Unknown table '" + StringUtils.join(unknownTables, ",") + "'");
                } else {
                    // set warning count to number of unknown tables if we only could...
                }
            }

        }

        public List<CatalogEntity> getUpdatedObjects() throws PEException {
            return updates;
        }

        public List<CatalogEntity> getDeletedObjects() throws PEException {
            return deletes;
        }

        public void onCommit(SSConnection conn, CatalogDAO c, WorkerGroup wg) {
            for (TableKey tk : tableKeys) {
                if (tk.isUserlandTemporaryTable()) {
                    wg.clearPinned();
                    context.getTemporaryTableSchema().removeTable(context, (ComplexPETable) tk.getAbstractTable());
                    if (context.getTemporaryTableSchema().isEmpty())
                        conn.releaseInflightTemporaryTablesLock();
                }
            }
        }

    }

}