This commit is contained in:
john-mcpeek
2013-12-24 00:23:52 -05:00
11 changed files with 251 additions and 60 deletions

View File

@@ -23,6 +23,7 @@ public class CqlStringUtils {
protected static final String DOUBLE_SINGLE_QUOTE = "\'\'";
protected static final String DOUBLE_QUOTE = "\"";
protected static final String DOUBLE_DOUBLE_QUOTE = "\"\"";
protected static final String EMPTY_STRING = "";
public static StringBuilder noNull(StringBuilder sb) {
return sb == null ? new StringBuilder() : sb;
@@ -129,4 +130,11 @@ public class CqlStringUtils {
return thing == null ? (String) null : new StringBuilder().append(DOUBLE_QUOTE).append(thing).append(DOUBLE_QUOTE)
.toString();
}
/**
* Removed single quotes from quoted String option values
*/
public static String removeSingleQuotes(Object thing) {
return thing == null ? (String) null : ((String) thing).replaceAll(SINGLE_QUOTE, EMPTY_STRING);
}
}

View File

@@ -107,4 +107,12 @@ public class CreateIndexSpecification extends IndexNameSpecification<CreateIndex
return this;
}
/**
* Entry point into the {@link CreateIndexSpecification}'s fluent API to create a index. Convenient if imported
* statically.
*/
public static CreateIndexSpecification createIndex() {
return new CreateIndexSpecification();
}
}

View File

@@ -25,6 +25,14 @@ public class DropIndexSpecification extends IndexNameSpecification<DropIndexSpec
private boolean ifExists;
/**
* Entry point into the {@link DropIndexSpecification}'s fluent API to drop a table. Convenient if imported
* statically.
*/
public static DropIndexSpecification dropIndex() {
return new DropIndexSpecification();
}
/*
* In CQL 3.1 this is supported so we can uncomment the exposure then.
* In the meantime, it will always be false and tests will pass.

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2011-2013 the original author or authors.
*
* 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.springframework.cassandra.core.keyspace;
/**
* Class that offers static methods as entry points into the fluent API for building create, drop and alter index
* specifications. These methods are most convenient when imported statically.
*
* @author Matthew T. Adams
* @author David Webb
*/
public class IndexOperations {
/**
* Entry point into the {@link CreateIndexSpecification}'s fluent API to create a index. Convenient if imported
* statically.
*/
public static CreateIndexSpecification createIndex() {
return new CreateIndexSpecification();
}
/**
* Entry point into the {@link DropIndexSpecification}'s fluent API to drop a table. Convenient if imported
* statically.
*/
public static DropIndexSpecification dropIndex() {
return new DropIndexSpecification();
}
}

View File

@@ -53,13 +53,13 @@ public enum TableOption implements Option {
/**
* <code>replicate_on_write</code>
*/
REPLICATE_ON_WRITE("replicate_on_write", Boolean.class, true, false, false),
REPLICATE_ON_WRITE("replicate_on_write", Boolean.class, true, false, true),
/**
* <code>caching</code>
*
* @see CachingOption
*/
CACHING("caching", CachingOption.class, true, false, false),
CACHING("caching", CachingOption.class, true, false, true),
/**
* <code>bloom_filter_fp_chance</code>
*/
@@ -152,6 +152,10 @@ public enum TableOption implements Option {
* @author Matthew T. Adams
*/
public enum CompactionOption implements Option {
/**
* <code>tombstone_threshold</code>
*/
CLASS("class", String.class, true, false, true),
/**
* <code>tombstone_threshold</code>
*/
@@ -242,7 +246,7 @@ public enum TableOption implements Option {
/**
* <code>sstable_compression</code>
*/
STABLE_COMPRESSION("sstable_compression", String.class, true, false, false),
SSTABLE_COMPRESSION("sstable_compression", String.class, true, false, true),
/**
* <code>chunk_length_kb</code>
*/

View File

@@ -20,10 +20,12 @@ import static org.junit.Assert.assertEquals;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.cql.CqlStringUtils;
import org.springframework.cassandra.core.keyspace.ColumnSpecification;
import org.springframework.cassandra.core.keyspace.TableDescriptor;
import org.springframework.cassandra.core.keyspace.TableOption;
import org.springframework.cassandra.core.keyspace.TableOption.CachingOption;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.Session;
@@ -32,6 +34,8 @@ import com.datastax.driver.core.TableMetadata.Options;
public class CqlTableSpecificationAssertions {
private final static Logger log = LoggerFactory.getLogger(CqlTableSpecificationAssertions.class);
public static double DELTA = 1e-6; // delta for comparisons of doubles
public static void assertTable(TableDescriptor expected, String keyspace, Session session) {
@@ -57,8 +61,10 @@ public class CqlTableSpecificationAssertions {
for (String key : expected.keySet()) {
log.info(key + " -> " + expected.get(key));
Object value = expected.get(key);
TableOption tableOption = getTableOptionFor(key);
TableOption tableOption = getTableOptionFor(key.toUpperCase());
if (tableOption == null && key.equalsIgnoreCase(TableOption.COMPACT_STORAGE.getName())) {
// TODO: figure out how to tell if COMPACT STORAGE was used
@@ -85,7 +91,7 @@ public class CqlTableSpecificationAssertions {
return;
case CACHING:
assertEquals(CachingOption.valueOf((String) expected).getValue(), actual);
assertEquals(((String) expected).toUpperCase(), ((String) actual).toUpperCase());
return;
case COMPACTION:
@@ -97,7 +103,10 @@ public class CqlTableSpecificationAssertions {
return;
}
assertEquals(expected, actual);
log.info(actual.getClass().getName());
assertEquals(expected,
tableOption.quotesValue() && !(actual instanceof CharSequence) ? CqlStringUtils.singleQuote(actual) : actual);
}
public static void assertCompaction(Map<String, Object> expected, Map<String, String> actual) {
@@ -122,9 +131,9 @@ public class CqlTableSpecificationAssertions {
case BLOOM_FILTER_FP_CHANCE:
return (T) (Double) options.getBloomFilterFalsePositiveChance();
case CACHING:
return (T) options.getCaching();
return (T) CqlStringUtils.singleQuote(options.getCaching());
case COMMENT:
return (T) options.getComment();
return (T) CqlStringUtils.singleQuote(options.getComment());
case COMPACTION:
return (T) options.getCompaction();
case COMPACT_STORAGE:
@@ -132,7 +141,7 @@ public class CqlTableSpecificationAssertions {
case COMPRESSION:
return (T) options.getCompression();
case DCLOCAL_READ_REPAIR_CHANCE:
return (T) (Double) options.getReadRepairChance();
return (T) (Double) options.getLocalReadRepairChance();
case GC_GRACE_SECONDS:
return (T) new Long(options.getGcGraceInSeconds());
case READ_REPAIR_CHANCE:

View File

@@ -3,6 +3,8 @@ package org.springframework.cassandra.test.integration.core.cql.generator;
import static org.springframework.cassandra.test.integration.core.cql.generator.CqlTableSpecificationAssertions.assertTable;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest;
import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.BasicTest;
import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.CompositePartitionKeyTest;
@@ -15,6 +17,8 @@ import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCql
*/
public class CreateTableCqlGeneratorIntegrationTests {
private final static Logger log = LoggerFactory.getLogger(CreateTableCqlGeneratorIntegrationTests.class);
/**
* Integration test base class that knows how to do everything except instantiate the concrete unit test type T.
*

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2011-2013 the original author or authors.
*
* 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.springframework.cassandra.test.integration.core.cql.generator;
import static org.springframework.cassandra.test.integration.core.cql.generator.CqlTableSpecificationAssertions.assertTable;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest;
import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests;
/**
* Test CREATE TABLE for all Options and assert against C* TableMetaData
*
* @author David Webb
*/
public class TableOptionsIntegrationTest extends AbstractEmbeddedCassandraIntegrationTest {
private final static Logger log = LoggerFactory.getLogger(TableOptionsIntegrationTest.class);
@Test
public void test() {
CreateTableCqlGeneratorTests.MultipleOptionsTest optionsTest = new CreateTableCqlGeneratorTests.MultipleOptionsTest();
optionsTest.prepare();
log.info(optionsTest.cql);
session.execute(optionsTest.cql);
assertTable(optionsTest.specification, keyspace, session);
}
}

View File

@@ -1,7 +1,6 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static org.junit.Assert.assertTrue;
import static org.springframework.cassandra.core.keyspace.IndexOperations.createIndex;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.CreateIndexCqlGenerator;
@@ -44,7 +43,7 @@ public class CreateIndexCqlGeneratorTests {
public String column1 = "column1";
public CreateIndexSpecification specification() {
return createIndex().name(name).tableName(tableName).columnName(column1);
return CreateIndexSpecification.createIndex().name(name).tableName(tableName).columnName(column1);
}
@Test

View File

@@ -1,15 +1,27 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.cassandra.core.keyspace.Option;
import org.springframework.cassandra.core.keyspace.TableOption;
import org.springframework.cassandra.core.keyspace.TableOption.CachingOption;
import org.springframework.cassandra.core.keyspace.TableOption.CompactionOption;
import org.springframework.cassandra.core.keyspace.TableOption.CompressionOption;
import com.datastax.driver.core.DataType;
public class CreateTableCqlGeneratorTests {
private static final Logger log = LoggerFactory.getLogger(CreateTableCqlGeneratorTests.class);
/**
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
@@ -35,6 +47,35 @@ public class CreateTableCqlGeneratorTests {
assertTrue(cql.contains("(" + columnSpec + ","));
}
/**
* Asserts that the read repair change is set properly
*/
public static void assertStringOption(String name, String value, String cql) {
log.info(name + " -> " + value);
assertTrue(cql.contains(name + " = '" + value + "'"));
}
/**
* Asserts that the option is set
*/
public static void assertDoubleOption(String name, Double value, String cql) {
log.info(name + " -> " + value);
assertTrue(cql.contains(name + " = " + value));
}
public static void assertLongOption(String name, Long value, String cql) {
log.info(name + " -> " + value);
assertTrue(cql.contains(name + " = " + value));
}
/**
* Asserts that the read repair change is set properly
*/
public static void assertNullOption(String name, String cql) {
log.info(name);
assertTrue(cql.contains(" " + name + " "));
}
/**
* Convenient base class that other test classes can use so as not to repeat the generics declarations or
* {@link #generator()} method.
@@ -56,7 +97,8 @@ public class CreateTableCqlGeneratorTests {
public String column1 = "column1";
public CreateTableSpecification specification() {
return CreateTableSpecification.createTable().name(name).partitionKeyColumn(partitionKey0, partitionKeyType0).column(column1, columnType1);
return CreateTableSpecification.createTable().name(name).partitionKeyColumn(partitionKey0, partitionKeyType0)
.column(column1, columnType1);
}
@Test
@@ -96,4 +138,109 @@ public class CreateTableCqlGeneratorTests {
assertPrimaryKey(String.format("(%s, %s)", partKey0, partKey1), cql);
}
}
/**
* Test just the Read Repair Chance
*
* @author David Webb
*
*/
public static class ReadRepairChanceTest extends CreateTableTest {
public String name = "mytable";
public DataType partitionKeyType0 = DataType.text();
public String partitionKey0 = "partitionKey0";
public DataType partitionKeyType1 = DataType.timestamp();
public String partitionKey1 = "create_timestamp";
public DataType columnType1 = DataType.text();
public String column1 = "column1";
public Double readRepairChance = 0.5;
public CreateTableSpecification specification() {
return (CreateTableSpecification) CreateTableSpecification.createTable().name(name)
.partitionKeyColumn(partitionKey0, partitionKeyType0).partitionKeyColumn(partitionKey1, partitionKeyType1)
.column(column1, columnType1).with(TableOption.READ_REPAIR_CHANCE, readRepairChance);
}
@Test
public void test() {
prepare();
assertPreamble(name, cql);
assertColumns(String.format("%s %s, %s %s, %s %s", partitionKey0, partitionKeyType0, partitionKey1,
partitionKeyType1, column1, columnType1), cql);
assertPrimaryKey(String.format("(%s, %s)", partitionKey0, partitionKey1), cql);
assertDoubleOption(TableOption.READ_REPAIR_CHANCE.getName(), readRepairChance, cql);
}
}
/**
* Fully test all available create table options
*
* TODO - Determine how to assert the options with map values
*
* @author David Webb
*
*/
public static class MultipleOptionsTest extends CreateTableTest {
public String name = "timeseries_table";
public DataType partitionKeyType0 = DataType.timeuuid();
public String partitionKey0 = "tid";
public DataType partitionKeyType1 = DataType.timestamp();
public String partitionKey1 = "create_timestamp";
public DataType columnType1 = DataType.text();
public String column1 = "data_point";
public Double readRepairChance = 0.5;
public Double dcLocalReadRepairChance = 0.7;
public Double bloomFilterFpChance = 0.001;
public Boolean replcateOnWrite = Boolean.FALSE;
public Long gcGraceSeconds = 600l;
public String comment = "This is My Table";
public Map<Option, Object> compactionMap = new LinkedHashMap<Option, Object>();
public Map<Option, Object> compressionMap = new LinkedHashMap<Option, Object>();
public CreateTableSpecification specification() {
// Compaction
compactionMap.put(CompactionOption.CLASS, "SizeTieredCompactionStrategy");
compactionMap.put(CompactionOption.MIN_THRESHOLD, "4");
// Compression
compressionMap.put(CompressionOption.SSTABLE_COMPRESSION, "SnappyCompressor");
compressionMap.put(CompressionOption.CHUNK_LENGTH_KB, 128);
compressionMap.put(CompressionOption.CRC_CHECK_CHANCE, 0.75);
return (CreateTableSpecification) CreateTableSpecification.createTable().name(name)
.partitionKeyColumn(partitionKey0, partitionKeyType0).partitionKeyColumn(partitionKey1, partitionKeyType1)
.column(column1, columnType1).with(TableOption.COMPACT_STORAGE)
.with(TableOption.READ_REPAIR_CHANCE, readRepairChance).with(TableOption.COMPACTION, compactionMap)
.with(TableOption.COMPRESSION, compressionMap).with(TableOption.BLOOM_FILTER_FP_CHANCE, bloomFilterFpChance)
.with(TableOption.CACHING, CachingOption.KEYS_ONLY).with(TableOption.REPLICATE_ON_WRITE, replcateOnWrite)
.with(TableOption.COMMENT, comment).with(TableOption.DCLOCAL_READ_REPAIR_CHANCE, dcLocalReadRepairChance)
.with(TableOption.GC_GRACE_SECONDS, gcGraceSeconds);
}
@Test
public void test() {
prepare();
log.info(cql);
assertPreamble(name, cql);
assertColumns(String.format("%s %s, %s %s, %s %s", partitionKey0, partitionKeyType0, partitionKey1,
partitionKeyType1, column1, columnType1), cql);
assertPrimaryKey(String.format("(%s, %s)", partitionKey0, partitionKey1), cql);
assertNullOption(TableOption.COMPACT_STORAGE.getName(), cql);
assertDoubleOption(TableOption.READ_REPAIR_CHANCE.getName(), readRepairChance, cql);
assertDoubleOption(TableOption.DCLOCAL_READ_REPAIR_CHANCE.getName(), dcLocalReadRepairChance, cql);
assertDoubleOption(TableOption.BLOOM_FILTER_FP_CHANCE.getName(), bloomFilterFpChance, cql);
assertStringOption(TableOption.CACHING.getName(), CachingOption.KEYS_ONLY.getValue(), cql);
assertStringOption(TableOption.REPLICATE_ON_WRITE.getName(), replcateOnWrite.toString(), cql);
assertStringOption(TableOption.COMMENT.getName(), comment, cql);
assertLongOption(TableOption.GC_GRACE_SECONDS.getName(), gcGraceSeconds, cql);
}
}
}

View File

@@ -1,7 +1,6 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static org.junit.Assert.assertTrue;
import static org.springframework.cassandra.core.keyspace.IndexOperations.dropIndex;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.DropIndexCqlGenerator;
@@ -28,7 +27,7 @@ public class DropIndexCqlGeneratorTests {
public String name = "myindex";
public DropIndexSpecification specification() {
return dropIndex().name(name);
return DropIndexSpecification.dropIndex().name(name);
}
public DropIndexCqlGenerator generator() {
@@ -49,7 +48,7 @@ public class DropIndexCqlGeneratorTests {
public String name = "myindex";
public DropIndexSpecification specification() {
return dropIndex().name(name)
return DropIndexSpecification.dropIndex().name(name)
// .ifExists()
;
}