getExpectedValues() {
+ return expected;
+ }
+
+ public Row getRow() {
+ return row;
+ }
+}
diff --git a/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/errorhandling/exceptions/UnexpectedPagingException.java b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/errorhandling/exceptions/UnexpectedPagingException.java
new file mode 100644
index 000000000..73156c8f0
--- /dev/null
+++ b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/errorhandling/exceptions/UnexpectedPagingException.java
@@ -0,0 +1,55 @@
+package io.nosqlbench.activitytype.cql.ebdrivers.cql.errorhandling.exceptions;
+
+import com.datastax.driver.core.ResultSet;
+
+/**
+ * This is not a core exception. It was added to the CQL activity type
+ * driver for nosqlbench specifically to catch the following unexpected
+ * condition:
+ * Paging would be needed to read all the results from a read query, but the user
+ * is not expecting to intentionally check and iterate the result sets for paging.
+ *
+ * This should only be thrown if a result set would need paging, but configuration
+ * options specific that it should not expect to. Rather than assume paging is completely
+ * expected or unexpected, we simply assume that only 1 page is allowed, being the
+ * first page, or what is thought of as "not paging".
+ *
If this error is thrown, and paging is expected, then the user can adjust
+ * fetchsize or maxpages in order to open up paging to the degree that is allowable or
+ * expected.
+ */
+public class UnexpectedPagingException extends CqlCycleException {
+
+ private final ResultSet resultSet;
+ private final String queryString;
+ private final int fetchSize;
+ private int fetchedPages;
+ private int maxpages;
+
+ public UnexpectedPagingException(
+ long cycle,
+ ResultSet resultSet,
+ String queryString,
+ int fetchedPages,
+ int maxpages,
+ int fetchSize) {
+ super(cycle);
+ this.resultSet = resultSet;
+ this.queryString = queryString;
+ this.fetchedPages = fetchedPages;
+ this.maxpages = maxpages;
+ this.fetchSize = fetchSize;
+ }
+
+ public ResultSet getResultSet() {
+ return resultSet;
+ }
+
+ public String getMessage() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Additional paging would be required to read the results from this query fully" +
+ ", but the user has not explicitly indicated that paging was expected.")
+ .append(" fetched/allowed: ").append(fetchedPages).append("/").append(maxpages)
+ .append(" fetchSize(").append(fetchSize).append("): ").append(queryString);
+ return sb.toString();
+ }
+}
diff --git a/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/filtering/CQLResultFilterType.java b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/filtering/CQLResultFilterType.java
new file mode 100644
index 000000000..c23faf86b
--- /dev/null
+++ b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/filtering/CQLResultFilterType.java
@@ -0,0 +1,65 @@
+package io.nosqlbench.activitytype.cql.ebdrivers.cql.filtering;
+
+import io.nosqlbench.activitytype.cql.ebdrivers.cql.errorhandling.CQLExceptionEnum;
+import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.ResultReadable;
+import io.nosqlbench.engine.api.activityapi.cyclelog.filters.ResultFilterDispenser;
+import io.nosqlbench.engine.api.activityapi.cyclelog.filters.ResultValueFilterType;
+import io.nosqlbench.engine.api.activityapi.cyclelog.filters.tristate.EnumReadableMappingFilter;
+import io.nosqlbench.engine.api.activityapi.cyclelog.filters.tristate.TristateFilter;
+import io.nosqlbench.engine.api.util.ConfigTuples;
+import io.nosqlbench.virtdata.annotations.Service;
+
+import java.util.function.Predicate;
+
+@Service(ResultValueFilterType.class)
+public class CQLResultFilterType implements ResultValueFilterType {
+
+ @Override
+ public String getName() {
+ return "cql";
+ }
+
+ @Override
+ public ResultFilterDispenser getDispenser(String config) {
+ return new Dispenser(config);
+ }
+
+ private class Dispenser implements ResultFilterDispenser {
+ private final ConfigTuples conf;
+ private final EnumReadableMappingFilter enumFilter;
+ private final Predicate filter;
+
+ public Dispenser(String config) {
+ this.conf = new ConfigTuples(config);
+ ConfigTuples inout = conf.getAllMatching("in.*", "ex.*");
+
+ // Default policy is opposite of leading rule
+ TristateFilter.Policy defaultPolicy = TristateFilter.Policy.Discard;
+ if (conf.get(0).get(0).startsWith("ex")) {
+ defaultPolicy = TristateFilter.Policy.Keep;
+ }
+
+ this.enumFilter =
+ new EnumReadableMappingFilter<>(CQLExceptionEnum.values(), TristateFilter.Policy.Ignore);
+
+ for (ConfigTuples.Section section : inout) {
+ if (section.get(0).startsWith("in")) {
+ this.enumFilter.addPolicy(section.get(1), TristateFilter.Policy.Keep);
+ } else if (section.get(0).startsWith("ex")) {
+ this.enumFilter.addPolicy(section.get(1), TristateFilter.Policy.Discard);
+ } else {
+ throw new RuntimeException("Section must start with in(clude) or ex(clude), but instead it is " + section);
+ }
+
+ }
+
+ this.filter = this.enumFilter.toDefaultingPredicate(defaultPolicy);
+ }
+
+ @Override
+ public Predicate getResultFilter() {
+ return filter;
+ }
+ }
+
+}
diff --git a/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/CqlBinderTypes.java b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/CqlBinderTypes.java
new file mode 100644
index 000000000..43a033c7e
--- /dev/null
+++ b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/CqlBinderTypes.java
@@ -0,0 +1,27 @@
+package io.nosqlbench.activitytype.cql.ebdrivers.cql.statements.binders;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.Statement;
+import io.nosqlbench.virtdata.api.ValuesArrayBinder;
+
+public enum CqlBinderTypes {
+ direct_array,
+ unset_aware,
+ diagnostic;
+
+ public final static CqlBinderTypes DEFAULT = unset_aware;
+
+ public ValuesArrayBinder get(Session session) {
+ if (this==direct_array) {
+ return new DirectArrayValuesBinder();
+ } else if (this== unset_aware) {
+ return new UnsettableValuesBinder(session);
+ } else if (this==diagnostic) {
+ return new DiagnosticPreparedBinder();
+ } else {
+ throw new RuntimeException("Impossible-ish statement branch");
+ }
+ }
+
+}
diff --git a/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/DiagnosticPreparedBinder.java b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/DiagnosticPreparedBinder.java
new file mode 100644
index 000000000..80df09c15
--- /dev/null
+++ b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/DiagnosticPreparedBinder.java
@@ -0,0 +1,48 @@
+package io.nosqlbench.activitytype.cql.ebdrivers.cql.statements.binders;
+
+import com.datastax.driver.core.*;
+import io.nosqlbench.activitytype.cql.ebdrivers.cql.core.CQLBindHelper;
+import io.nosqlbench.virtdata.api.ValuesArrayBinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * This binder is not meant to be used primarily by default. It gives detailed
+ * diagnostics, but in order to do so by default it does lots of processing.
+ * Other binders will call to this one in an exception handler when needed in
+ * order to explain in more detail what is happening for users.
+ */
+public class DiagnosticPreparedBinder implements ValuesArrayBinder {
+ public static final Logger logger = LoggerFactory.getLogger(DiagnosticPreparedBinder.class);
+ @Override
+ public Statement bindValues(PreparedStatement prepared, Object[] values) {
+ ColumnDefinitions columnDefinitions = prepared.getVariables();
+ BoundStatement bound = prepared.bind();
+ List columnDefList;
+ if (columnDefinitions.asList().size() == values.length) {
+ columnDefList = columnDefinitions.asList();
+ } else {
+ throw new RuntimeException("The number of named anchors in your statement does not match the number of bindings provided.");
+ }
+
+ int i = 0;
+ for (Object value : values) {
+ if (columnDefList.size() <= i) {
+ logger.error("what gives?");
+ }
+ ColumnDefinitions.Definition columnDef = columnDefList.get(i);
+ String colName = columnDef.getName();
+ DataType.Name type = columnDef.getType().getName();
+ try {
+ bound = CQLBindHelper.bindStatement(bound, colName, value, type);
+ } catch (ClassCastException e) {
+ logger.error(String.format("Unable to bind column %s to cql type %s with value %s", colName, type, value));
+ throw e;
+ }
+ i++;
+ }
+ return bound;
+ }
+}
diff --git a/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/DirectArrayValuesBinder.java b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/DirectArrayValuesBinder.java
new file mode 100644
index 000000000..1f0f547c0
--- /dev/null
+++ b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/DirectArrayValuesBinder.java
@@ -0,0 +1,37 @@
+package io.nosqlbench.activitytype.cql.ebdrivers.cql.statements.binders;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Statement;
+import io.nosqlbench.virtdata.api.ValuesArrayBinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+
+/**
+ * This is now the main binder again, but if there are any exceptions, it delegates to the diagnostic
+ * one in order to explain what happened. This is to allow for higher performance in the general
+ * case, but with better user support when something goes wrong.
+ *
+ * If you want to force the client to use the array passing method of initializing a statement,
+ * use this one, known as 'directarray'. This does give up the benefit of allowing unset values
+ * to be modeled, and at no clear benefit. Thus the {@link CqlBinderTypes#unset_aware} one
+ * will become the default.
+ */
+public class DirectArrayValuesBinder implements ValuesArrayBinder {
+ public final static Logger logger = LoggerFactory.getLogger(DirectArrayValuesBinder.class);
+
+ @Override
+ public Statement bindValues(PreparedStatement preparedStatement, Object[] objects) {
+ try {
+ return preparedStatement.bind(objects);
+ } catch (Exception e) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Error binding objects to prepared statement directly, falling back to diagnostic binding layer:");
+ sb.append(Arrays.toString(objects));
+ logger.warn(sb.toString(),e);
+ DiagnosticPreparedBinder diag = new DiagnosticPreparedBinder();
+ return diag.bindValues(preparedStatement, objects);
+ }
+ }
+}
diff --git a/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/SimpleStatementValuesBinder.java b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/SimpleStatementValuesBinder.java
new file mode 100644
index 000000000..f514672c6
--- /dev/null
+++ b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/SimpleStatementValuesBinder.java
@@ -0,0 +1,19 @@
+package io.nosqlbench.activitytype.cql.ebdrivers.cql.statements.binders;
+
+import com.datastax.driver.core.SimpleStatement;
+import com.datastax.driver.core.Statement;
+import io.nosqlbench.virtdata.api.ValuesArrayBinder;
+
+/**
+ * This binder is not meant to be used with anything but DDL or statements
+ * which should not be trying to parameterize values in general. If this changes,
+ * support will be added for parameterized values here.
+ */
+public class SimpleStatementValuesBinder
+ implements ValuesArrayBinder {
+
+ @Override
+ public Statement bindValues(SimpleStatement context, Object[] values) {
+ return new SimpleStatement(context.getQueryString(), values);
+ }
+}
diff --git a/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/UnsettableValuesBinder.java b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/UnsettableValuesBinder.java
new file mode 100644
index 000000000..d3b3e03c0
--- /dev/null
+++ b/activitytype-cql/src/main/java/io/nosqlbench/activitytype/cql/ebdrivers/cql/statements/binders/UnsettableValuesBinder.java
@@ -0,0 +1,73 @@
+package io.nosqlbench.activitytype.cql.ebdrivers.cql.statements.binders;
+
+import com.datastax.driver.core.*;
+import io.nosqlbench.virtdata.api.VALUE;
+import io.nosqlbench.virtdata.api.ValuesArrayBinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class UnsettableValuesBinder implements ValuesArrayBinder {
+ private final static Logger logger = LoggerFactory.getLogger(UnsettableValuesBinder.class);
+
+ private final Session session;
+ private final CodecRegistry codecRegistry;
+ private final ProtocolVersion protocolVersion;
+
+ public UnsettableValuesBinder(Session session) {
+ this.session = session;
+ this.codecRegistry = session.getCluster().getConfiguration().getCodecRegistry();
+ this.protocolVersion = this.session.getCluster().getConfiguration().getProtocolOptions().getProtocolVersion();
+ }
+
+
+ // TODO: Allow for warning when nulls are passed and they aren't expected
+ @Override
+ public Statement bindValues(PreparedStatement preparedStatement, Object[] objects) {
+ int i=-1;
+ try {
+ BoundStatement boundStmt = preparedStatement.bind();
+ List defs = preparedStatement.getVariables().asList();
+ for (i = 0; i < objects.length; i++) {
+ Object value = objects[i];
+ if (VALUE.unset != value) {
+ if (null==value) {
+ boundStmt.setToNull(i);
+ } else {
+ DataType cqlType = defs.get(i).getType();
+ TypeCodec