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

com.datastax.driver.core.querybuilder.Delete Maven / Gradle / Ivy

There is a newer version: 3.6.0-1
Show newest version
/*
 *      Copyright (C) 2012-2015 DataStax Inc.
 *
 *   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 com.datastax.driver.core.querybuilder;

import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.TableMetadata;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * A built DELETE statement.
 */
public class Delete extends BuiltStatement {

    private final String table;
    private final List columns;
    private final Where where;
    private final Options usings;
    private final Conditions conditions;
    private boolean ifExists;

    Delete(String keyspace, String table, List columns) {
        this(keyspace, table, null, null, columns);
    }

    Delete(TableMetadata table, List columns) {
        this(escapeId(table.getKeyspace().getName()),
                escapeId(table.getName()),
                Arrays.asList(new Object[table.getPartitionKey().size()]),
                table.getPartitionKey(),
                columns);
    }

    Delete(String keyspace,
           String table,
           List routingKeyValues,
           List partitionKey,
           List columns) {
        super(keyspace, partitionKey, routingKeyValues);
        this.table = table;
        this.columns = columns;
        this.where = new Where(this);
        this.usings = new Options(this);
        this.conditions = new Conditions(this);

        // This is for JAVA-1089, if the query deletes an element in a list, the statement should be non-idempotent.
        if (!areIdempotent(columns)) {
            setNonIdempotentOps();
        }
    }

    @Override
    StringBuilder buildQueryString(List variables, CodecRegistry codecRegistry) {
        StringBuilder builder = new StringBuilder();

        builder.append("DELETE");
        if (!columns.isEmpty())
            Utils.joinAndAppend(builder.append(" "), codecRegistry, ",", columns, variables);

        builder.append(" FROM ");
        if (keyspace != null)
            Utils.appendName(keyspace, builder).append('.');
        Utils.appendName(table, builder);
        if (!usings.usings.isEmpty()) {
            builder.append(" USING ");
            Utils.joinAndAppend(builder, codecRegistry, " AND ", usings.usings, variables);
        }

        if (!where.clauses.isEmpty()) {
            builder.append(" WHERE ");
            Utils.joinAndAppend(builder, codecRegistry, " AND ", where.clauses, variables);
        }

        if (ifExists) {
            builder.append(" IF EXISTS ");
        }

        if (!conditions.conditions.isEmpty()) {
            builder.append(" IF ");
            Utils.joinAndAppend(builder, codecRegistry, " AND ", conditions.conditions, variables);
        }

        return builder;
    }

    /**
     * Adds a WHERE clause to this statement.
     * 

* This is a shorter/more readable version for {@code where().and(clause)}. * * @param clause the clause to add. * @return the where clause of this query to which more clause can be added. */ public Where where(Clause clause) { return where.and(clause); } /** * Returns a Where statement for this query without adding clause. * * @return the where clause of this query to which more clause can be added. */ public Where where() { return where; } /** * Adds a conditions clause (IF) to this statement. *

* This is a shorter/more readable version for {@code onlyIf().and(condition)}. *

* This will configure the statement as non-idempotent, see {@link com.datastax.driver.core.Statement#isIdempotent()} * for more information. * * @param condition the condition to add. * @return the conditions of this query to which more conditions can be added. */ public Conditions onlyIf(Clause condition) { return conditions.and(condition); } /** * Adds a conditions clause (IF) to this statement. *

* This will configure the statement as non-idempotent, see {@link com.datastax.driver.core.Statement#isIdempotent()} * for more information. * * @return the conditions of this query to which more conditions can be added. */ public Conditions onlyIf() { return conditions; } /** * Adds a new options for this DELETE statement. * * @param using the option to add. * @return the options of this DELETE statement. */ public Options using(Using using) { return usings.and(using); } /** * Returns the options for this DELETE statement. *

* Chain this with {@link Options#and(Using)} to add options. * * @return the options of this DELETE statement. */ public Options using() { return usings; } /** * Sets the 'IF EXISTS' option for this DELETE statement. *

*

* A delete with that option will report whether the statement actually * resulted in data being deleted. The existence check and deletion are * done transactionally in the sense that if multiple clients attempt to * delete a given row with this option, then at most one may succeed. *

*

* Please keep in mind that using this option has a non negligible * performance impact and should be avoided when possible. *

* This will configure the statement as non-idempotent, see {@link com.datastax.driver.core.Statement#isIdempotent()} * for more information. * * @return this DELETE statement. */ public Delete ifExists() { this.ifExists = true; setNonIdempotentOps(); return this; } /** * The WHERE clause of a DELETE statement. */ public static class Where extends BuiltStatement.ForwardingStatement { private final List clauses = new ArrayList(); Where(Delete statement) { super(statement); } /** * Adds the provided clause to this WHERE clause. * * @param clause the clause to add. * @return this WHERE clause. */ public Where and(Clause clause) { clauses.add(clause); statement.maybeAddRoutingKey(clause.name(), clause.firstValue()); if (!hasNonIdempotentOps() && !Utils.isIdempotent(clause)) { statement.setNonIdempotentOps(); } checkForBindMarkers(clause); return this; } /** * Adds an option to the DELETE statement this WHERE clause is part of. * * @param using the using clause to add. * @return the options of the DELETE statement this WHERE clause is part of. */ public Options using(Using using) { return statement.using(using); } /** * Sets the 'IF EXISTS' option for the DELETE statement this WHERE clause * is part of. *

*

* A delete with that option will report whether the statement actually * resulted in data being deleted. The existence check and deletion are * done transactionally in the sense that if multiple clients attempt to * delete a given row with this option, then at most one may succeed. *

*

* Please keep in mind that using this option has a non negligible * performance impact and should be avoided when possible. *

* * @return the DELETE statement this WHERE clause is part of. */ public Delete ifExists() { return statement.ifExists(); } /** * Adds a condition to the DELETE statement this WHERE clause is part of. * * @param condition the condition to add. * @return the conditions for the DELETE statement this WHERE clause is part of. */ public Conditions onlyIf(Clause condition) { return statement.onlyIf(condition); } } /** * The options of a DELETE statement. */ public static class Options extends BuiltStatement.ForwardingStatement { private final List usings = new ArrayList(); Options(Delete statement) { super(statement); } /** * Adds the provided option. * * @param using a DELETE option. * @return this {@code Options} object. */ public Options and(Using using) { usings.add(using); checkForBindMarkers(using); return this; } /** * Adds a where clause to the DELETE statement these options are part of. * * @param clause clause to add. * @return the WHERE clause of the DELETE statement these options are part of. */ public Where where(Clause clause) { return statement.where(clause); } } /** * An in-construction DELETE statement. */ public static class Builder { List columns = new ArrayList(); Builder() { } Builder(String... columnNames) { for (String columnName : columnNames) { this.columns.add(new Selector(columnName)); } } /** * Adds the table to delete from. * * @param table the name of the table to delete from. * @return a newly built DELETE statement that deletes from {@code table}. */ public Delete from(String table) { return from(null, table); } /** * Adds the table to delete from. * * @param keyspace the name of the keyspace to delete from. * @param table the name of the table to delete from. * @return a newly built DELETE statement that deletes from {@code keyspace.table}. */ public Delete from(String keyspace, String table) { return new Delete(keyspace, table, columns); } /** * Adds the table to delete from. * * @param table the table to delete from. * @return a newly built DELETE statement that deletes from {@code table}. */ public Delete from(TableMetadata table) { return new Delete(table, columns); } } /** * An column selection clause for an in-construction DELETE statement. */ public static class Selection extends Builder { /** * Deletes all columns (i.e. "DELETE FROM ...") * * @return an in-build DELETE statement. * @throws IllegalStateException if some columns had already been selected for this builder. */ public Builder all() { if (!columns.isEmpty()) throw new IllegalStateException(String.format("Some columns (%s) have already been selected.", columns)); return this; } /** * Deletes the provided column. * * @param columnName the column to select for deletion. * @return this in-build DELETE Selection */ public Selection column(String columnName) { columns.add(new Selector(columnName)); return this; } /** * Deletes the provided list element. * * @param columnName the name of the list column. * @param idx the index of the element to delete. * @return this in-build DELETE Selection */ public Selection listElt(String columnName, int idx) { columns.add(new ListElementSelector(columnName, idx)); return this; } /** * Deletes the provided list element, * specified as a bind marker. * * @param columnName the name of the list column. * @param idx the index of the element to delete. * @return this in-build DELETE Selection */ public Selection listElt(String columnName, BindMarker idx) { columns.add(new ListElementSelector(columnName, idx)); return this; } /** * Deletes the provided set element. * * @param columnName the name of the set column. * @param element the element to delete. * @return this in-build DELETE Selection */ public Selection setElt(String columnName, Object element) { columns.add(new SetElementSelector(columnName, element)); return this; } /** * Deletes the provided set element, * specified as a bind marker. * * @param columnName the name of the set column. * @param element the element to delete. * @return this in-build DELETE Selection */ public Selection setElt(String columnName, BindMarker element) { columns.add(new SetElementSelector(columnName, element)); return this; } /** * Deletes a map element given a key. * * @param columnName the name of the map column. * @param key the key for the element to delete. * @return this in-build DELETE Selection */ public Selection mapElt(String columnName, Object key) { columns.add(new MapElementSelector(columnName, key)); return this; } } /** * A selector in a DELETE selection clause. * A selector can be either a column name, * a list element, a set element or a map entry. */ private static class Selector extends Utils.Appendeable { private final String columnName; Selector(String columnName) { this.columnName = columnName; } @Override void appendTo(StringBuilder sb, List values, CodecRegistry codecRegistry) { Utils.appendName(columnName, sb); } @Override boolean containsBindMarker() { return false; } @Override public String toString() { return columnName; } } /** * A selector representing a list index, a set element or a map key in a DELETE selection clause. */ private static class CollectionElementSelector extends Selector { protected final Object key; CollectionElementSelector(String columnName, Object key) { super(columnName); this.key = key; } @Override void appendTo(StringBuilder sb, List values, CodecRegistry codecRegistry) { super.appendTo(sb, values, codecRegistry); sb.append('['); Utils.appendValue(key, codecRegistry, sb, values); sb.append(']'); } @Override boolean containsBindMarker() { return Utils.containsBindMarker(key); } } private static class ListElementSelector extends CollectionElementSelector { ListElementSelector(String columnName, Object key) { super(columnName, key); } } private static class SetElementSelector extends CollectionElementSelector { SetElementSelector(String columnName, Object key) { super(columnName, key); } } private static class MapElementSelector extends CollectionElementSelector { MapElementSelector(String columnName, Object key) { super(columnName, key); } } private boolean areIdempotent(List selectors) { for (Selector sel : selectors) { if (sel instanceof ListElementSelector) { return false; } } return true; } /** * Conditions for a DELETE statement. *

* When provided some conditions, a deletion will not apply unless the * provided conditions applies. *

*

* Please keep in mind that provided conditions have a non negligible * performance impact and should be avoided when possible. *

*/ public static class Conditions extends BuiltStatement.ForwardingStatement { private final List conditions = new ArrayList(); Conditions(Delete statement) { super(statement); } /** * Adds the provided condition for the deletion. *

* Note that while the query builder accept any type of {@code Clause} * as conditions, Cassandra currently only allows equality ones. * * @param condition the condition to add. * @return this {@code Conditions} clause. */ public Conditions and(Clause condition) { statement.setNonIdempotentOps(); conditions.add(condition); checkForBindMarkers(condition); return this; } /** * Adds a where clause to the DELETE statement these conditions are part of. * * @param clause clause to add. * @return the WHERE clause of the DELETE statement these conditions are part of. */ public Where where(Clause clause) { return statement.where(clause); } /** * Adds an option to the DELETE statement these conditions are part of. * * @param using the using clause to add. * @return the options of the DELETE statement these conditions are part of. */ public Options using(Using using) { return statement.using(using); } } }