com.torodb.torod.db.postgresql.meta.routines.DeleteDocuments.java Source code

Java tutorial

Introduction

Here is the source code for com.torodb.torod.db.postgresql.meta.routines.DeleteDocuments.java

Source

/*
 *     This file is part of ToroDB.
 *
 *     ToroDB is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     ToroDB 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 ToroDB. If not, see <http://www.gnu.org/licenses/>.
 *
 *     Copyright (c) 2014, 8Kdata Technology
 *     
 */
package com.torodb.torod.db.postgresql.meta.routines;

import com.google.common.collect.Iterators;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.Sets;
import com.torodb.torod.core.subdocument.structure.ArrayStructure;
import com.torodb.torod.core.subdocument.structure.DocStructure;
import com.torodb.torod.core.subdocument.structure.StructureElement;
import com.torodb.torod.core.subdocument.structure.StructureElementVisitor;
import com.torodb.torod.db.postgresql.meta.CollectionSchema;
import com.torodb.torod.db.postgresql.meta.tables.SubDocTable;
import com.torodb.torod.db.sql.AutoCloser;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.sql.*;
import java.util.*;
import org.jooq.*;
import org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;

/**
 *
 */
public class DeleteDocuments {

    public static int execute(Configuration configuration, CollectionSchema colSchema,
            Multimap<DocStructure, Integer> didsByStructure, boolean justOne) {
        Multimap<DocStructure, Integer> didsByStructureToDelete;
        if (didsByStructure.isEmpty()) {
            return 0;
        }

        if (justOne) {
            didsByStructureToDelete = MultimapBuilder.hashKeys(1).arrayListValues(1).build();

            Map.Entry<DocStructure, Integer> aEntry = didsByStructure.entries().iterator().next();

            didsByStructureToDelete.put(aEntry.getKey(), aEntry.getValue());
        } else {
            didsByStructureToDelete = didsByStructure;
        }

        try {
            return execute(configuration, colSchema, didsByStructureToDelete);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static int execute(Configuration configuration, CollectionSchema colSchema,
            Multimap<DocStructure, Integer> didsByStructure) throws SQLException {
        TableProvider tableProvider = new TableProvider(colSchema);

        DSLContext dsl = DSL.using(configuration);

        Set<SubDocTable> tables = Sets.newHashSet();
        for (DocStructure structure : didsByStructure.keySet()) {
            tables.clear();
            structure.accept(tableProvider, tables);

            executeDeleteSubDocuments(dsl, tables, didsByStructure.get(structure));
        }

        Set<Integer> dids = Sets.newHashSet(didsByStructure.values());
        return executeDeleteRoots(dsl, colSchema, dids);
    }

    private static void executeDeleteSubDocuments(DSLContext dsl, Set<SubDocTable> tables,
            Collection<Integer> dids) {

        ConnectionProvider connectionProvider = dsl.configuration().connectionProvider();
        Connection connection = connectionProvider.acquire();
        try {
            for (SubDocTable table : tables) {
                delete(connection, table.getSchema(), table, dids);
            }
        } catch (SQLException ex) {
            //TODO: change exceptions
            throw new RuntimeException(ex);
        } finally {
            connectionProvider.release(connection);
        }
    }

    private static int executeDeleteRoots(DSLContext dsl, CollectionSchema colSchema, Collection<Integer> dids)
            throws SQLException {
        ConnectionProvider connectionProvider = dsl.configuration().connectionProvider();
        Connection connection = connectionProvider.acquire();

        try {
            Table<Record> rootTable = DSL.tableByName(colSchema.getName(), "root");

            return delete(connection, colSchema, rootTable, dids);
        } finally {
            connectionProvider.release(connection);
        }
    }

    @SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
    private static int delete(Connection connection, Schema schema, Table table, Collection<Integer> dids)
            throws SQLException {
        final int maxInArray = (2 << 15) - 1; // = 2^16 -1 = 65535

        final String tableName = "\"" + schema.getName() + "\".\"" + table.getName() + "\"";
        PreparedStatement ps = null;
        try {
            ps = connection.prepareStatement("DELETE FROM " + tableName + " WHERE (" + tableName + "."
                    + SubDocTable.DID_COLUMN_NAME + " IN (SELECT unnest(?)))");
            Integer[] didsToDelete = dids.toArray(new Integer[dids.size()]);

            int i = 0;
            while (i < didsToDelete.length) {
                int toIndex = Math.min(i + maxInArray, didsToDelete.length);
                Integer[] subDids = Arrays.copyOfRange(didsToDelete, i, toIndex);

                Array arr = connection.createArrayOf("integer", subDids);
                ps.setArray(1, arr);
                ps.addBatch();

                i = toIndex;
            }
            int[] executeResult = ps.executeBatch();
            int result = 0;
            for (i = 0; i < executeResult.length; i++) {
                int iestResult = executeResult[i];
                if (iestResult >= 0) {
                    result += iestResult;
                }
            }
            return result;
        } finally {
            AutoCloser.close(ps);
        }
    }

    private static class TableProvider implements StructureElementVisitor<Void, Collection<SubDocTable>> {

        private final CollectionSchema colSchema;

        public TableProvider(CollectionSchema colSchema) {
            this.colSchema = colSchema;
        }

        @Override
        public Void visit(DocStructure structure, Collection<SubDocTable> arg) {
            arg.add(colSchema.getSubDocTable(structure.getType()));

            for (StructureElement child : structure.getElements().values()) {
                child.accept(this, arg);
            }

            return null;
        }

        @Override
        public Void visit(ArrayStructure structure, Collection<SubDocTable> arg) {
            for (StructureElement child : structure.getElements().values()) {
                child.accept(this, arg);
            }

            return null;
        }
    }
}