now have create/alter/drop table builders
This commit is contained in:
@@ -9,29 +9,28 @@ public class CqlStringUtils {
|
||||
protected static final String DOUBLE_QUOTE = "\"";
|
||||
protected static final String DOUBLE_DOUBLE_QUOTE = "\"\"";
|
||||
|
||||
/**
|
||||
* Helper {@link StringBuilder} factory method. If given a non-<code>null</code> argument, returns that, else returns
|
||||
* a new {@link StringBuilder}. Intended to be imported statically by other classes in the builder's fluent API
|
||||
* implementation.
|
||||
*
|
||||
* @param sb
|
||||
* @return The given {@link StringBuilder} if not null, else a new one.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
public static StringBuilder ensureNotNull(StringBuilder sb) {
|
||||
public static StringBuilder noNull(StringBuilder sb) {
|
||||
return sb == null ? new StringBuilder() : sb;
|
||||
}
|
||||
|
||||
public static final String IDENTIFIER_REGEX = "[a-zA-Z0-9_]*";
|
||||
public static final Pattern IDENTIFIER_PATTERN = Pattern.compile(IDENTIFIER_REGEX);
|
||||
public static final String UNESCAPED_DOUBLE_QUOTE_REGEX = "TODO";
|
||||
public static final Pattern UNESCAPED_DOUBLE_QUOTE_PATTERN = Pattern.compile(UNESCAPED_DOUBLE_QUOTE_REGEX);
|
||||
|
||||
public static boolean isIdentifier(CharSequence chars) {
|
||||
return IDENTIFIER_PATTERN.matcher(chars).matches();
|
||||
public static final String UNQUOTED_IDENTIFIER_REGEX = "[a-zA-Z_][a-zA-Z0-9_]*";
|
||||
public static final Pattern UNQUOTED_IDENTIFIER_PATTERN = Pattern.compile(UNQUOTED_IDENTIFIER_REGEX);
|
||||
|
||||
public static boolean isUnquotedIdentifier(CharSequence chars) {
|
||||
return UNQUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
|
||||
}
|
||||
|
||||
public static final String QUOTED_IDENTIFIER_REGEX = "([a-zA-Z0-9_]|'{2}+|\"{2}+)*";
|
||||
public static final Pattern QUOTED_IDENTIFIER_PATTERN = Pattern.compile(IDENTIFIER_REGEX);
|
||||
public static void checkUnquotedIdentifier(CharSequence chars) {
|
||||
if (!CqlStringUtils.isUnquotedIdentifier(chars)) {
|
||||
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL identifier");
|
||||
}
|
||||
}
|
||||
|
||||
public static final String QUOTED_IDENTIFIER_REGEX = "[a-zA-Z_]([a-zA-Z0-9_]|\"{2}+)*";
|
||||
public static final Pattern QUOTED_IDENTIFIER_PATTERN = Pattern.compile(QUOTED_IDENTIFIER_REGEX);
|
||||
|
||||
public static boolean isQuotedIdentifier(CharSequence chars) {
|
||||
return QUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
|
||||
@@ -43,19 +42,45 @@ public class CqlStringUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trims then escapes the given {@link CharSequence}. Given <code>null</code>, returns <code>null</code>.
|
||||
*/
|
||||
public static String scrub(Object thing) {
|
||||
return thing == null ? (String) null : escape(thing.toString().trim());
|
||||
public static boolean isIdentifier(CharSequence chars) {
|
||||
return isUnquotedIdentifier(chars) || isQuotedIdentifier(chars);
|
||||
}
|
||||
|
||||
public static void checkIdentifier(CharSequence chars) {
|
||||
if (!CqlStringUtils.isIdentifier(chars)) {
|
||||
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL quoted or unquoted identifier");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Doubles single quote characters and doubles double quote characters (' -> '' and " -> ""). Given
|
||||
* <code>null</code>, returns <code>null</code>.
|
||||
* Renders the given string as a legal Cassandra identifier.
|
||||
* <ul>
|
||||
* <li>If the given identifier is a legal unquoted identifier, it is returned unchanged.</li>
|
||||
* <li>If the given identifier is a legal quoted identifier, it is returned encased in double quotes.</li>
|
||||
* <li>If the given identifier is illegal, an {@link IllegalArgumentException} is thrown.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static String escape(Object thing) {
|
||||
return escapeDouble(escapeSingle(thing));
|
||||
public static String identifize(String candidate) {
|
||||
|
||||
checkIdentifier(candidate);
|
||||
|
||||
if (isUnquotedIdentifier(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
// else it must be quoted
|
||||
return doubleQuote(candidate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the given string as a legal Cassandra string column or table option value, by escaping single quotes and
|
||||
* encasing the result in single quotes. Given <code>null</code>, returns <code>null</code>.
|
||||
*/
|
||||
public static String valuize(String candidate) {
|
||||
|
||||
if (candidate == null) {
|
||||
return null;
|
||||
}
|
||||
return singleQuote(escapeSingle(candidate));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +94,7 @@ public class CqlStringUtils {
|
||||
* Doubles double quote characters (" -> ""). Given <code>null</code>, returns <code>null</code>.
|
||||
*/
|
||||
public static String escapeDouble(Object things) {
|
||||
return things == null ? (String) null : things.toString().replace(DOUBLE_QUOTE, DOUBLE_SINGLE_QUOTE);
|
||||
return things == null ? (String) null : things.toString().replace(DOUBLE_QUOTE, DOUBLE_DOUBLE_QUOTE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.noNull;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.escapeSingle;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.identifize;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.singleQuote;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.cassandra.cql.CqlStringUtils;
|
||||
|
||||
/**
|
||||
* Base class that contains behavior common to table operations.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
* @param T The subtype of AbstractTableBuilder.
|
||||
*/
|
||||
public abstract class AbstractTableBuilder<T extends AbstractTableBuilder<T>> {
|
||||
|
||||
protected abstract StringBuilder toCql(StringBuilder cql);
|
||||
|
||||
private String name;
|
||||
|
||||
protected Map<String, Object> options = new LinkedHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* Sets the table name.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T name(String name) {
|
||||
CqlStringUtils.checkIdentifier(name);
|
||||
this.name = name;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getNameAsIdentifier() {
|
||||
return identifize(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that calls <code>with(option, null)</code>.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public T with(TableOption option) {
|
||||
return with(option, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given table option. This is a convenience method that calls
|
||||
* {@link #with(String, Object, boolean, boolean)} appropriately from the given {@link TableOption} and value for that
|
||||
* option.
|
||||
*
|
||||
* @param option The option to set.
|
||||
* @param value The value of the option. Must be type-compatible with the {@link TableOption}.
|
||||
* @return this
|
||||
* @see #with(String, Object, boolean, boolean)
|
||||
*/
|
||||
public T with(TableOption option, Object value) {
|
||||
option.checkValue(value);
|
||||
return (T) with(option.getName(), value, option.escapesValue(), option.quotesValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given option by name to this table's options.
|
||||
* <p/>
|
||||
* Options that have <code>null</code> values are considered single string options where the name of the option is the
|
||||
* string to be used. Otherwise, the result of {@link Object#toString()} is considered to be the value of the option
|
||||
* with the given name. The value, after conversion to string, may have embedded single quotes escaped according to
|
||||
* parameter <code>escape</code> and may be single-quoted according to parameter <code>quote</code>.
|
||||
*
|
||||
* @param name The name of the option
|
||||
* @param value The value of the option. If <code>null</code>, the value is ignored and the option is considered to be
|
||||
* composed of only the name, otherwise the value's {@link Object#toString()} value is used.
|
||||
* @param escape Whether to escape the value via {@link CqlStringUtils#escapeSingle(Object)}. Ignored if given value
|
||||
* is an instance of a {@link Map}.
|
||||
* @param quote Whether to quote the value via {@link CqlStringUtils#singleQuote(Object)}. Ignored if given value is
|
||||
* an instance of a {@link Map}.
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T with(String name, Object value, boolean escape, boolean quote) {
|
||||
if (!(value instanceof Map)) {
|
||||
if (escape) {
|
||||
value = escapeSingle(value);
|
||||
}
|
||||
if (quote) {
|
||||
value = singleQuote(value);
|
||||
}
|
||||
}
|
||||
options().put(name, value);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
protected Map<String, Object> options() {
|
||||
return options == null ? options = new LinkedHashMap<String, Object>() : options;
|
||||
}
|
||||
|
||||
protected StringBuilder optionValueMap(Map<Option, Object> valueMap, StringBuilder cql) {
|
||||
cql = noNull(cql);
|
||||
|
||||
if (valueMap == null || valueMap.isEmpty()) {
|
||||
return cql;
|
||||
}
|
||||
// else option value is a non-empty map
|
||||
|
||||
// append { 'name' : 'value', ... }
|
||||
cql.append("{ ");
|
||||
boolean mapFirst = true;
|
||||
for (Map.Entry<Option, Object> entry : valueMap.entrySet()) {
|
||||
if (mapFirst) {
|
||||
mapFirst = false;
|
||||
} else {
|
||||
cql.append(", ");
|
||||
}
|
||||
|
||||
Option option = entry.getKey();
|
||||
cql.append(singleQuote(option.getName())); // entries in map keys are always quoted
|
||||
cql.append(" : ");
|
||||
Object entryValue = entry.getValue();
|
||||
entryValue = entryValue == null ? "" : entryValue.toString();
|
||||
if (option.escapesValue()) {
|
||||
entryValue = escapeSingle(entryValue);
|
||||
}
|
||||
if (option.quotesValue()) {
|
||||
entryValue = singleQuote(entryValue);
|
||||
}
|
||||
cql.append(entryValue);
|
||||
}
|
||||
cql.append(" }");
|
||||
|
||||
return cql;
|
||||
}
|
||||
|
||||
public String toCql() {
|
||||
return toCql(null).toString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toCql(null).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.noNull;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
|
||||
public class AddColumn extends ColumnTypeChange {
|
||||
|
||||
public AddColumn(String name, DataType type) {
|
||||
super(name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuilder toCql(StringBuilder cql) {
|
||||
return noNull(cql).append("ADD ").append(getNameAsIdentifier()).append(" TYPE ").append(getType().getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.noNull;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
|
||||
public class AlterColumn extends ColumnTypeChange {
|
||||
|
||||
public AlterColumn(String name, DataType type) {
|
||||
super(name, type);
|
||||
}
|
||||
|
||||
public StringBuilder toCql(StringBuilder cql) {
|
||||
return noNull(cql).append("ALTER ").append(getNameAsIdentifier()).append(" TYPE ")
|
||||
.append(getType().getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.noNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
|
||||
public class AlterTableBuilder extends AbstractTableBuilder<AlterTableBuilder> {
|
||||
|
||||
private List<ColumnChange> changes = new ArrayList<ColumnChange>();
|
||||
|
||||
public AlterTableBuilder drop(String column) {
|
||||
changes.add(new DropColumn(column));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlterTableBuilder add(String column, DataType type) {
|
||||
changes.add(new AddColumn(column, type));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlterTableBuilder alter(String column, DataType type) {
|
||||
changes.add(new AlterColumn(column, type));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlterTableBuilder name(String name) {
|
||||
return (AlterTableBuilder) super.name(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuilder toCql(StringBuilder cql) {
|
||||
cql = noNull(cql);
|
||||
|
||||
preambleCql(cql);
|
||||
changesCql(cql);
|
||||
optionsCql(cql);
|
||||
|
||||
cql.append(";");
|
||||
|
||||
return cql;
|
||||
}
|
||||
|
||||
protected StringBuilder preambleCql(StringBuilder cql) {
|
||||
return noNull(cql).append("ALTER TABLE ").append(getNameAsIdentifier()).append(" ");
|
||||
}
|
||||
|
||||
protected StringBuilder changesCql(StringBuilder cql) {
|
||||
cql = noNull(cql);
|
||||
|
||||
boolean first = true;
|
||||
for (ColumnChange change : changes) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
cql.append(" ");
|
||||
}
|
||||
change.toCql(cql);
|
||||
}
|
||||
|
||||
return cql;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected StringBuilder optionsCql(StringBuilder cql) {
|
||||
cql = noNull(cql);
|
||||
|
||||
Map<String, Object> options = options();
|
||||
if (options == null || options.isEmpty()) {
|
||||
return cql;
|
||||
}
|
||||
|
||||
cql.append(" WITH ");
|
||||
boolean first = true;
|
||||
for (String key : options.keySet()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
cql.append(" AND ");
|
||||
}
|
||||
|
||||
cql.append(key);
|
||||
|
||||
Object value = options.get(key);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
cql.append(" = ");
|
||||
|
||||
if (value instanceof Map) {
|
||||
optionValueMap((Map<Option, Object>) value, cql);
|
||||
continue;
|
||||
}
|
||||
|
||||
// else just use value as string
|
||||
cql.append(value.toString());
|
||||
}
|
||||
return cql;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.checkQuotedIdentifier;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.ensureNotNull;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.checkIdentifier;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.identifize;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.noNull;
|
||||
import static org.springframework.data.cassandra.mapping.KeyType.PARTITION;
|
||||
import static org.springframework.data.cassandra.mapping.KeyType.PRIMARY;
|
||||
import static org.springframework.data.cassandra.mapping.Ordering.ASCENDING;
|
||||
|
||||
import org.springframework.data.cassandra.cql.CqlStringUtils;
|
||||
import org.springframework.data.cassandra.mapping.KeyType;
|
||||
import org.springframework.data.cassandra.mapping.Ordering;
|
||||
|
||||
@@ -35,15 +35,12 @@ public class ColumnBuilder {
|
||||
private Ordering ordering;
|
||||
|
||||
/**
|
||||
* Sets the column's name. Quotes are not escaped.
|
||||
*
|
||||
* @see CqlStringUtils#escape(CharSequence)
|
||||
* @see CqlStringUtils#scrub(CharSequence)
|
||||
* Sets the column's name.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public ColumnBuilder name(String name) {
|
||||
checkQuotedIdentifier(name);
|
||||
checkIdentifier(name);
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
@@ -138,6 +135,10 @@ public class ColumnBuilder {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getNameAsIdentifier() {
|
||||
return identifize(name);
|
||||
}
|
||||
|
||||
public DataType getType() {
|
||||
return type;
|
||||
}
|
||||
@@ -155,7 +156,7 @@ public class ColumnBuilder {
|
||||
}
|
||||
|
||||
public StringBuilder toCql(StringBuilder cql) {
|
||||
return (cql = ensureNotNull(cql)).append(name).append(" ").append(type);
|
||||
return (cql = noNull(cql)).append(name).append(" ").append(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.checkIdentifier;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.identifize;
|
||||
|
||||
public abstract class ColumnChange {
|
||||
|
||||
public abstract StringBuilder toCql(StringBuilder cql);
|
||||
|
||||
private String name;
|
||||
|
||||
public ColumnChange(String name) {
|
||||
setName(name);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toCql(null).toString();
|
||||
}
|
||||
|
||||
private void setName(String name) {
|
||||
checkIdentifier(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getNameAsIdentifier() {
|
||||
return identifize(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
|
||||
public abstract class ColumnTypeChange extends ColumnChange {
|
||||
|
||||
private DataType type;
|
||||
|
||||
public ColumnTypeChange(String name, DataType type) {
|
||||
super(name);
|
||||
setType(type);
|
||||
}
|
||||
|
||||
private void setType(DataType type) {
|
||||
Assert.notNull(type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public DataType getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,39 @@ public class CqlBuilder {
|
||||
public static CreateTableBuilder createTable() {
|
||||
return new CreateTableBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point into the {@link CqlBuilder}'s fluent API to create a table. Convenient if imported statically.
|
||||
*/
|
||||
public static CreateTableBuilder createTable(String name) {
|
||||
return new CreateTableBuilder().name(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point into the {@link CqlBuilder}'s fluent API to alter a table. Convenient if imported statically.
|
||||
*/
|
||||
public static AlterTableBuilder alterTable() {
|
||||
return new AlterTableBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point into the {@link CqlBuilder}'s fluent API to alter a table. Convenient if imported statically.
|
||||
*/
|
||||
public static AlterTableBuilder alterTable(String name) {
|
||||
return new AlterTableBuilder().name(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point into the {@link CqlBuilder}'s fluent API to drop a table. Convenient if imported statically.
|
||||
*/
|
||||
public static DropTableBuilder dropTable() {
|
||||
return new DropTableBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point into the {@link CqlBuilder}'s fluent API to drop a table. Convenient if imported statically.
|
||||
*/
|
||||
public static DropTableBuilder dropTable(String name) {
|
||||
return new DropTableBuilder().name(name);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,14 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.checkQuotedIdentifier;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.ensureNotNull;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.escapeSingle;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.singleQuote;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.noNull;
|
||||
import static org.springframework.data.cassandra.mapping.KeyType.PARTITION;
|
||||
import static org.springframework.data.cassandra.mapping.KeyType.PRIMARY;
|
||||
import static org.springframework.data.cassandra.mapping.Ordering.ASCENDING;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.cassandra.cql.CqlStringUtils;
|
||||
import org.springframework.data.cassandra.mapping.KeyType;
|
||||
import org.springframework.data.cassandra.mapping.Ordering;
|
||||
|
||||
@@ -24,12 +19,10 @@ import com.datastax.driver.core.DataType;
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
public class CreateTableBuilder {
|
||||
public class CreateTableBuilder extends AbstractTableBuilder<CreateTableBuilder> {
|
||||
|
||||
private boolean ifNotExists = false;
|
||||
private String name;
|
||||
private List<ColumnBuilder> columns = new ArrayList<ColumnBuilder>();
|
||||
private Map<String, Object> options = new LinkedHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* Causes the inclusion of an <code>IF NOT EXISTS</code> clause.
|
||||
@@ -50,74 +43,6 @@ public class CreateTableBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the table name. Quotes are not escaped.
|
||||
*
|
||||
* @see CqlStringUtils#escape(CharSequence)
|
||||
* @see CqlStringUtils#scrub(CharSequence)
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public CreateTableBuilder name(String name) {
|
||||
checkQuotedIdentifier(name);
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that calls <code>with(option, null)</code>.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public CreateTableBuilder with(TableOption option) {
|
||||
return with(option, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given table option. This is a convenience method that calls
|
||||
* {@link #with(String, Object, boolean, boolean)} appropriately from the given {@link TableOption} and value for that
|
||||
* option.
|
||||
*
|
||||
* @param option The option to set.
|
||||
* @param value The value of the option. Must be type-compatible with the {@link TableOption}.
|
||||
* @return this
|
||||
* @see #with(String, Object, boolean, boolean)
|
||||
*/
|
||||
public CreateTableBuilder with(TableOption option, Object value) {
|
||||
option.checkValue(value);
|
||||
return with(option.getName(), value, option.escapesValue(), option.quotesValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given option by name to this table's options.
|
||||
* <p/>
|
||||
* Options that have <code>null</code> values are considered single string options where the name of the option is the
|
||||
* string to be used. Otherwise, the result of {@link Object#toString()} is considered to be the value of the option
|
||||
* with the given name. The value, after conversion to string, may have embedded single quotes escaped according to
|
||||
* parameter <code>escape</code> and may be single-quoted according to parameter <code>quote</code>.
|
||||
*
|
||||
* @param name The name of the option
|
||||
* @param value The value of the option. If <code>null</code>, the value is ignored and the option is considered to be
|
||||
* composed of only the name, otherwise the value's {@link Object#toString()} value is used.
|
||||
* @param escape Whether to escape the value via {@link CqlStringUtils#escapeSingle(Object)}. Ignored if given value
|
||||
* is an instance of a {@link Map}.
|
||||
* @param quote Whether to quote the value via {@link CqlStringUtils#singleQuote(Object)}. Ignored if given value is
|
||||
* an instance of a {@link Map}.
|
||||
* @return this
|
||||
*/
|
||||
public CreateTableBuilder with(String name, Object value, boolean escape, boolean quote) {
|
||||
if (!(value instanceof Map)) {
|
||||
if (escape) {
|
||||
value = escapeSingle(value);
|
||||
}
|
||||
if (quote) {
|
||||
value = singleQuote(value);
|
||||
}
|
||||
}
|
||||
options().put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateTableBuilder column(String name, DataType type) {
|
||||
return column(name, type, null, null);
|
||||
}
|
||||
@@ -143,30 +68,27 @@ public class CreateTableBuilder {
|
||||
return columns == null ? columns = new ArrayList<ColumnBuilder>() : columns;
|
||||
}
|
||||
|
||||
protected Map<String, Object> options() {
|
||||
return options == null ? options = new LinkedHashMap<String, Object>() : options;
|
||||
}
|
||||
public StringBuilder toCql(StringBuilder cql) {
|
||||
|
||||
public String toCql() {
|
||||
|
||||
StringBuilder cql = new StringBuilder();
|
||||
cql = noNull(cql);
|
||||
|
||||
preambleCql(cql);
|
||||
columnsAndOptionsCql(cql);
|
||||
|
||||
cql.append(";");
|
||||
|
||||
return cql.toString();
|
||||
return cql;
|
||||
}
|
||||
|
||||
protected StringBuilder preambleCql(StringBuilder cql) {
|
||||
return (cql = ensureNotNull(cql)).append("CREATE TABLE ").append(ifNotExists ? "IF NOT EXISTS " : "").append(name);
|
||||
return noNull(cql).append("CREATE TABLE ").append(ifNotExists ? "IF NOT EXISTS " : "")
|
||||
.append(getNameAsIdentifier());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected StringBuilder columnsAndOptionsCql(StringBuilder cql) {
|
||||
|
||||
cql = ensureNotNull(cql);
|
||||
cql = noNull(cql);
|
||||
|
||||
// begin columns
|
||||
cql.append(" (");
|
||||
@@ -275,35 +197,8 @@ public class CreateTableBuilder {
|
||||
|
||||
cql.append(" = ");
|
||||
|
||||
Map<Option, Object> valueMap = null;
|
||||
if ((value instanceof Map) && !(valueMap = (Map<Option, Object>) value).isEmpty()) {
|
||||
// then option value is a non-empty map
|
||||
|
||||
// append { 'name' : 'value', ... }
|
||||
cql.append("{ ");
|
||||
boolean mapFirst = true;
|
||||
for (Map.Entry<Option, Object> entry : valueMap.entrySet()) {
|
||||
if (mapFirst) {
|
||||
mapFirst = false;
|
||||
} else {
|
||||
cql.append(", ");
|
||||
}
|
||||
|
||||
Option option = entry.getKey();
|
||||
cql.append(singleQuote(option.getName())); // entries in map keys are always quoted
|
||||
cql.append(" : ");
|
||||
Object entryValue = entry.getValue();
|
||||
entryValue = entryValue == null ? "" : entryValue.toString();
|
||||
if (option.escapesValue()) {
|
||||
entryValue = escapeSingle(value);
|
||||
}
|
||||
if (option.quotesValue()) {
|
||||
entryValue = singleQuote(value);
|
||||
}
|
||||
cql.append(entryValue);
|
||||
}
|
||||
cql.append(" }");
|
||||
|
||||
if (value instanceof Map) {
|
||||
optionValueMap((Map<Option, Object>) value, cql);
|
||||
continue; // end non-empty value map
|
||||
}
|
||||
|
||||
@@ -316,9 +211,4 @@ public class CreateTableBuilder {
|
||||
|
||||
return cql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toCql();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.noNull;
|
||||
|
||||
public class DropColumn extends ColumnChange {
|
||||
|
||||
public DropColumn(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuilder toCql(StringBuilder cql) {
|
||||
return noNull(cql).append("DROP ").append(getNameAsIdentifier());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.checkIdentifier;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.noNull;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.identifize;
|
||||
|
||||
public class DropTableBuilder {
|
||||
|
||||
private String name;
|
||||
private boolean ifExists;
|
||||
|
||||
public DropTableBuilder name(String name) {
|
||||
checkIdentifier(name);
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DropTableBuilder ifExists() {
|
||||
return ifExists(true);
|
||||
}
|
||||
|
||||
public DropTableBuilder ifExists(boolean ifExists) {
|
||||
this.ifExists = ifExists;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StringBuilder toCql(StringBuilder cql) {
|
||||
return noNull(cql).append("DROP TABLE ").append(ifExists ? "IF EXISTS " : "").append(identifize(name));
|
||||
}
|
||||
|
||||
public String toCql() {
|
||||
return toCql(null).toString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toCql();
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* Builder for maps, which also conveniently implements {@link Map} via delegation for convenience so you don't have to
|
||||
* actually {@link #build()} it if you don't want to (or forget).
|
||||
* actually {@link #build()} it (or forget to).
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
* @param <K> The key type of the map.
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.springframework.data.cassandra.cql;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.isQuotedIdentifier;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.isUnquotedIdentifier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class CqlStringUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testIsQuotedIdentifier() throws Exception {
|
||||
assertFalse(isQuotedIdentifier("my\"id"));
|
||||
assertTrue(isQuotedIdentifier("my\"\"id"));
|
||||
assertFalse(isUnquotedIdentifier("my\"id"));
|
||||
assertTrue(isUnquotedIdentifier("myid"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.builder.CqlBuilder.alterTable;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
|
||||
public class AlterTableBuilderTest {
|
||||
|
||||
@Test
|
||||
public void testAlterTableBuilder() throws Exception {
|
||||
String name = "mytable";
|
||||
DataType addedType = DataType.timeuuid();
|
||||
String addedName = "added_column";
|
||||
DataType alteredType = DataType.text();
|
||||
String alteredName = "altered_column";
|
||||
String droppedName = "dropped";
|
||||
|
||||
String cql = alterTable().name(name).add(addedName, addedType).alter(alteredName, alteredType).drop(droppedName)
|
||||
.toCql();
|
||||
System.out.println(cql);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ public class CreateTableBuilderTest {
|
||||
|
||||
@Test
|
||||
public void createTableTest() {
|
||||
String name = "mytable";
|
||||
String name = "my\"\"table";
|
||||
DataType type0 = DataType.text();
|
||||
String partKey0 = "partitionKey0";
|
||||
String partition1 = "partitionKey1";
|
||||
@@ -38,7 +38,7 @@ public class CreateTableBuilderTest {
|
||||
|
||||
String cql = builder.toCql();
|
||||
assertEquals(
|
||||
"CREATE TABLE IF NOT EXISTS mytable (partitionKey0 text, partitionKey1 text, primary0 text, column1 text, column2 bigint, PRIMARY KEY ((partitionKey0, partitionKey1), primary0) WITH CLUSTERING ORDER BY (primary0 ASC) AND comment = 'this is a comment' AND bloom_filter_fp_chance = 0.00075 AND compaction = { 'tombstone_threshold' : 0.15 } AND caching = keys_only;",
|
||||
"CREATE TABLE IF NOT EXISTS \"my\"\"table\" (partitionKey0 text, partitionKey1 text, primary0 text, column1 text, column2 bigint, PRIMARY KEY ((partitionKey0, partitionKey1), primary0) WITH CLUSTERING ORDER BY (primary0 ASC) AND comment = 'this is a comment' AND bloom_filter_fp_chance = 0.00075 AND compaction = { 'tombstone_threshold' : 0.15 } AND caching = keys_only;",
|
||||
cql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.builder.CqlBuilder.dropTable;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class DropTableBuilderTest {
|
||||
|
||||
@Test
|
||||
public void testDropTableBuilder() throws Exception {
|
||||
String cql = dropTable().name("mytable").ifExists().toCql();
|
||||
|
||||
System.out.println(cql);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user