http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/cql3/selection/Selection.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/selection/Selection.java b/src/java/org/apache/cassandra/cql3/selection/Selection.java index eb796b0..cbca97d 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selection.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selection.java @@ -18,24 +18,24 @@ package org.apache.cassandra.cql3.selection; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; +import java.util.*; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.Json; import org.apache.cassandra.cql3.ResultSet; import org.apache.cassandra.db.Cell; import org.apache.cassandra.db.CounterCell; import org.apache.cassandra.db.ExpiringCell; import org.apache.cassandra.db.context.CounterContext; +import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.utils.ByteBufferUtil; import com.google.common.base.Predicate; +import com.google.common.base.Objects; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; @@ -147,9 +147,14 @@ public abstract class Selection }); } - public ResultSet.ResultMetadata getResultMetadata() + public ResultSet.ResultMetadata getResultMetadata(boolean isJson) { - return metadata; + if (!isJson) + return metadata; + + ColumnSpecification firstColumn = metadata.names.get(0); + ColumnSpecification jsonSpec = new ColumnSpecification(firstColumn.ksName, firstColumn.cfName, Json.JSON_COLUMN_ID, UTF8Type.instance); + return new ResultSet.ResultMetadata(Arrays.asList(jsonSpec)); } public static Selection wildcard(CFMetaData cfm) @@ -223,31 +228,22 @@ public abstract class Selection return columns; } - public ResultSetBuilder resultSetBuilder(long now) throws InvalidRequestException + public ResultSetBuilder resultSetBuilder(long now, boolean isJson) throws InvalidRequestException { - return new ResultSetBuilder(now); + return new ResultSetBuilder(now, isJson); } public abstract boolean isAggregate(); - /** - * Checks that selectors are either all aggregates or that none of them is. - * - * @param selectors the selectors to test. - * @param messageTemplate the error message template - * @param messageArgs the error message arguments - * @throws InvalidRequestException if some of the selectors are aggregate but not all of them - */ - static void validateSelectors(List<Selector> selectors, String messageTemplate, Object... messageArgs) - throws InvalidRequestException + @Override + public String toString() { - int aggregates = 0; - for (Selector s : selectors) - if (s.isAggregate()) - ++aggregates; - - if (aggregates != 0 && aggregates != selectors.size()) - throw new InvalidRequestException(String.format(messageTemplate, messageArgs)); + return Objects.toStringHelper(this) + .add("columns", columns) + .add("metadata", metadata) + .add("collectTimestamps", collectTimestamps) + .add("collectTTLs", collectTTLs) + .toString(); } public class ResultSetBuilder @@ -273,13 +269,16 @@ public abstract class Selection final int[] ttls; final long now; - private ResultSetBuilder(long now) throws InvalidRequestException + private final boolean isJson; + + private ResultSetBuilder(long now, boolean isJson) throws InvalidRequestException { - this.resultSet = new ResultSet(getResultMetadata().copy(), new ArrayList<List<ByteBuffer>>()); + this.resultSet = new ResultSet(getResultMetadata(isJson).copy(), new ArrayList<List<ByteBuffer>>()); this.selectors = newSelectors(); this.timestamps = collectTimestamps ? new long[columns.size()] : null; this.ttls = collectTTLs ? new int[columns.size()] : null; this.now = now; + this.isJson = isJson; } public void add(ByteBuffer v) @@ -315,11 +314,11 @@ public abstract class Selection selectors.addInputRow(protocolVersion, this); if (!selectors.isAggregate()) { - resultSet.addRow(selectors.getOutputRow(protocolVersion)); + resultSet.addRow(getOutputRow(protocolVersion)); selectors.reset(); } } - current = new ArrayList<ByteBuffer>(columns.size()); + current = new ArrayList<>(columns.size()); } public ResultSet build(int protocolVersion) throws InvalidRequestException @@ -327,16 +326,47 @@ public abstract class Selection if (current != null) { selectors.addInputRow(protocolVersion, this); - resultSet.addRow(selectors.getOutputRow(protocolVersion)); + resultSet.addRow(getOutputRow(protocolVersion)); selectors.reset(); current = null; } if (resultSet.isEmpty() && selectors.isAggregate()) + resultSet.addRow(getOutputRow(protocolVersion)); + return resultSet; + } + + private List<ByteBuffer> getOutputRow(int protocolVersion) + { + List<ByteBuffer> outputRow = selectors.getOutputRow(protocolVersion); + return isJson ? rowToJson(outputRow, protocolVersion) + : outputRow; + } + + private List<ByteBuffer> rowToJson(List<ByteBuffer> row, int protocolVersion) + { + StringBuilder sb = new StringBuilder("{"); + for (int i = 0; i < metadata.names.size(); i++) { - resultSet.addRow(selectors.getOutputRow(protocolVersion)); + if (i > 0) + sb.append(", "); + + ColumnSpecification spec = metadata.names.get(i); + String columnName = spec.name.toString(); + if (!columnName.equals(columnName.toLowerCase(Locale.US))) + columnName = "\"" + columnName + "\""; + + ByteBuffer buffer = row.get(i); + sb.append('"'); + sb.append(Json.JSON_STRING_ENCODER.quoteAsString(columnName)); + sb.append("\": "); + if (buffer == null) + sb.append("null"); + else + sb.append(spec.type.toJSONString(buffer, protocolVersion)); } - return resultSet; + sb.append("}"); + return Collections.singletonList(UTF8Type.instance.getSerializer().serialize(sb.toString())); } private ByteBuffer value(Cell c) @@ -476,10 +506,8 @@ public abstract class Selection public void reset() { - for (int i = 0, m = selectors.size(); i < m; i++) - { - selectors.get(i).reset(); - } + for (Selector selector : selectors) + selector.reset(); } public boolean isAggregate() @@ -491,19 +519,16 @@ public abstract class Selection { List<ByteBuffer> outputRow = new ArrayList<>(selectors.size()); - for (int i = 0, m = selectors.size(); i < m; i++) - { - outputRow.add(selectors.get(i).getOutput(protocolVersion)); - } + for (Selector selector: selectors) + outputRow.add(selector.getOutput(protocolVersion)); + return outputRow; } public void addInputRow(int protocolVersion, ResultSetBuilder rs) throws InvalidRequestException { - for (int i = 0, m = selectors.size(); i < m; i++) - { - selectors.get(i).addInput(protocolVersion, rs); - } + for (Selector selector : selectors) + selector.addInput(protocolVersion, rs); } }; }
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java b/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java index 3afd1ec..4e6970b 100644 --- a/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java +++ b/src/java/org/apache/cassandra/cql3/selection/SelectorFactories.java @@ -27,6 +27,7 @@ import com.google.common.collect.Lists; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.selection.Selector.Factory; +import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; /** @@ -186,4 +187,20 @@ final class SelectorFactories implements Iterable<Selector.Factory> } }); } + + /** + * Returns a list of the return types of the selector instances created by these factories. + * + * @return a list of types + */ + public List<AbstractType<?>> getReturnTypes() + { + return Lists.transform(factories, new Function<Selector.Factory, AbstractType<?>>() + { + public AbstractType<?> apply(Selector.Factory factory) + { + return factory.getReturnType(); + } + }); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index 8945d1d..af8947d 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@ -596,7 +596,7 @@ public abstract class ModificationStatement implements CQLStatement } long now = System.currentTimeMillis(); - Selection.ResultSetBuilder builder = selection.resultSetBuilder(now); + Selection.ResultSetBuilder builder = selection.resultSetBuilder(now, false); SelectStatement.forSelection(cfm, selection).processColumnFamily(key, cf, options, now, builder); return builder.build(options.getProtocolVersion()); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java index c73360c..66ad81d 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -89,7 +89,7 @@ public class SelectStatement implements CQLStatement private final Comparator<List<ByteBuffer>> orderingComparator; // Used by forSelection below - private static final Parameters defaultParameters = new Parameters(Collections.<ColumnIdentifier.Raw, Boolean>emptyMap(), false, false); + private static final Parameters defaultParameters = new Parameters(Collections.<ColumnIdentifier.Raw, Boolean>emptyMap(), false, false, false); public SelectStatement(CFMetaData cfm, int boundTerms, @@ -135,7 +135,7 @@ public class SelectStatement implements CQLStatement public ResultSet.ResultMetadata getResultMetadata() { - return selection.getResultMetadata(); + return selection.getResultMetadata(parameters.isJson); } public int getBoundTerms() @@ -231,7 +231,7 @@ public class SelectStatement implements CQLStatement private ResultMessage.Rows pageAggregateQuery(QueryPager pager, QueryOptions options, int pageSize, long now) throws RequestValidationException, RequestExecutionException { - Selection.ResultSetBuilder result = selection.resultSetBuilder(now); + Selection.ResultSetBuilder result = selection.resultSetBuilder(now, parameters.isJson); while (!pager.isExhausted()) { for (org.apache.cassandra.db.Row row : pager.fetchPage(pageSize)) @@ -575,7 +575,7 @@ public class SelectStatement implements CQLStatement private ResultSet process(List<Row> rows, QueryOptions options, int limit, long now) throws InvalidRequestException { - Selection.ResultSetBuilder result = selection.resultSetBuilder(now); + Selection.ResultSetBuilder result = selection.resultSetBuilder(now, parameters.isJson); for (org.apache.cassandra.db.Row row : rows) { // Not columns match the query, skip @@ -617,6 +617,7 @@ public class SelectStatement implements CQLStatement if (restrictions.isNonCompositeSliceWithExclusiveBounds()) cells = applySliceRestriction(cells, options); + int protocolVersion = options.getProtocolVersion(); CQL3Row.RowIterator iter = cfm.comparator.CQL3RowBuilder(cfm, now).group(cells); // If there is static columns but there is no non-static row, then provided the select was a full @@ -734,8 +735,8 @@ public class SelectStatement implements CQLStatement VariableSpecifications boundNames = getBoundVariables(); Selection selection = selectClause.isEmpty() - ? Selection.wildcard(cfm) - : Selection.fromSelectors(cfm, selectClause); + ? Selection.wildcard(cfm) + : Selection.fromSelectors(cfm, selectClause); StatementRestrictions restrictions = prepareRestrictions(cfm, boundNames, selection); @@ -1012,14 +1013,17 @@ public class SelectStatement implements CQLStatement private final Map<ColumnIdentifier.Raw, Boolean> orderings; private final boolean isDistinct; private final boolean allowFiltering; + public final boolean isJson; public Parameters(Map<ColumnIdentifier.Raw, Boolean> orderings, boolean isDistinct, - boolean allowFiltering) + boolean allowFiltering, + boolean isJson) { this.orderings = orderings; this.isDistinct = isDistinct; this.allowFiltering = allowFiltering; + this.isJson = isJson; } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java index c783d48..67958cf 100644 --- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java @@ -150,13 +150,15 @@ public class UpdateStatement extends ModificationStatement * A parsed <code>INSERT</code> statement. * * @param name column family being operated on + * @param attrs additional attributes for statement (CL, timestamp, timeToLive) * @param columnNames list of column names * @param columnValues list of column values (corresponds to names) - * @param attrs additional attributes for statement (CL, timestamp, timeToLive) + * @param ifNotExists true if an IF NOT EXISTS condition was specified, false otherwise */ public ParsedInsert(CFName name, Attributes.Raw attrs, - List<ColumnIdentifier.Raw> columnNames, List<Term.Raw> columnValues, + List<ColumnIdentifier.Raw> columnNames, + List<Term.Raw> columnValues, boolean ifNotExists) { super(name, attrs, null, ifNotExists, false); @@ -166,16 +168,20 @@ public class UpdateStatement extends ModificationStatement protected ModificationStatement prepareInternal(CFMetaData cfm, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException { - UpdateStatement stmt = new UpdateStatement(ModificationStatement.StatementType.INSERT,boundNames.size(), cfm, attrs); + UpdateStatement stmt = new UpdateStatement(ModificationStatement.StatementType.INSERT, boundNames.size(), cfm, attrs); // Created from an INSERT if (stmt.isCounter()) - throw new InvalidRequestException("INSERT statement are not allowed on counter tables, use UPDATE instead"); - if (columnNames.size() != columnValues.size()) - throw new InvalidRequestException("Unmatched column names/values"); + throw new InvalidRequestException("INSERT statements are not allowed on counter tables, use UPDATE instead"); + + if (columnNames == null) + throw new InvalidRequestException("Column names for INSERT must be provided when using VALUES"); if (columnNames.isEmpty()) throw new InvalidRequestException("No columns provided to INSERT"); + if (columnNames.size() != columnValues.size()) + throw new InvalidRequestException("Unmatched column names/values"); + String ks = keyspace(); for (int i = 0; i < columnNames.size(); i++) { ColumnIdentifier id = columnNames.get(i).prepare(cfm); @@ -191,22 +197,54 @@ public class UpdateStatement extends ModificationStatement } Term.Raw value = columnValues.get(i); - - switch (def.kind) + if (def.isPrimaryKeyColumn()) { - case PARTITION_KEY: - case CLUSTERING_COLUMN: - Term t = value.prepare(keyspace(), def); - t.collectMarkerSpecification(boundNames); - stmt.addKeyValue(def, t); - break; - default: - Operation operation = new Operation.SetValue(value).prepare(keyspace(), def); - operation.collectMarkerSpecification(boundNames); - stmt.addOperation(operation); - break; + Term t = value.prepare(ks, def); + t.collectMarkerSpecification(boundNames); + stmt.addKeyValue(def, t); } + else + { + Operation operation = new Operation.SetValue(value).prepare(ks, def); + operation.collectMarkerSpecification(boundNames); + stmt.addOperation(operation); + } + } + + return stmt; + } + } + + /** + * A parsed INSERT JSON statement. + */ + public static class ParsedInsertJson extends ModificationStatement.Parsed + { + private final Json.Raw jsonValue; + + public ParsedInsertJson(CFName name, Attributes.Raw attrs, Json.Raw jsonValue, boolean ifNotExists) + { + super(name, attrs, null, ifNotExists, false); + this.jsonValue = jsonValue; + } + + protected ModificationStatement prepareInternal(CFMetaData cfm, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException + { + UpdateStatement stmt = new UpdateStatement(ModificationStatement.StatementType.INSERT, boundNames.size(), cfm, attrs); + if (stmt.isCounter()) + throw new InvalidRequestException("INSERT statements are not allowed on counter tables, use UPDATE instead"); + + Collection<ColumnDefinition> defs = cfm.allColumns(); + Json.Prepared prepared = jsonValue.prepareAndCollectMarkers(cfm, defs, boundNames); + + for (ColumnDefinition def : defs) + { + if (def.isPrimaryKeyColumn()) + stmt.addKeyValue(def, prepared.getPrimaryKeyValueForColumn(def)); + else + stmt.addOperation(prepared.getSetOperationForColumn(def)); } + return stmt; } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java b/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java index 4e830ab..4baf6a3 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.BytesSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -266,6 +267,18 @@ public abstract class AbstractCompositeType extends AbstractType<ByteBuffer> } @Override + public Term fromJSONObject(Object parsed) + { + throw new UnsupportedOperationException(); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + throw new UnsupportedOperationException(); + } + + @Override public void validate(ByteBuffer bytes) throws MarshalException { ByteBuffer bb = bytes.duplicate(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/AbstractType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java index d3711df..6f37929 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java @@ -26,9 +26,11 @@ import java.util.List; import java.util.Map; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.MarshalException; + import org.github.jamm.Unmetered; /** @@ -94,6 +96,17 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer> /** get a byte representation of the given string. */ public abstract ByteBuffer fromString(String source) throws MarshalException; + /** Given a parsed JSON string, return a byte representation of the object. + * @param parsed the result of parsing a json string + **/ + public abstract Term fromJSONObject(Object parsed) throws MarshalException; + + /** Converts a value to a JSON string. */ + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return '"' + getSerializer().deserialize(buffer).toString() + '"'; + } + /* validate that the byte array is a valid sequence for the type we are supposed to be comparing */ public void validate(ByteBuffer bytes) throws MarshalException { http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/AsciiType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/AsciiType.java b/src/java/org/apache/cassandra/db/marshal/AsciiType.java index 891a8ed..953fc09 100644 --- a/src/java/org/apache/cassandra/db/marshal/AsciiType.java +++ b/src/java/org/apache/cassandra/db/marshal/AsciiType.java @@ -23,7 +23,11 @@ import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CharacterCodingException; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Json; + import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.AsciiSerializer; @@ -65,6 +69,33 @@ public class AsciiType extends AbstractType<String> } } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected an ascii string, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + try + { + return '"' + new String(Json.JSON_STRING_ENCODER.quoteAsString(ByteBufferUtil.string(buffer, Charset.forName("US-ASCII")))) + '"'; + } + catch (CharacterCodingException exc) + { + throw new AssertionError("ascii value contained non-ascii characters: ", exc); + } + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.ASCII; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/BooleanType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/BooleanType.java b/src/java/org/apache/cassandra/db/marshal/BooleanType.java index 70d7559..5c1bf42 100644 --- a/src/java/org/apache/cassandra/db/marshal/BooleanType.java +++ b/src/java/org/apache/cassandra/db/marshal/BooleanType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.BooleanSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -57,7 +59,25 @@ public class BooleanType extends AbstractType<Boolean> if (source.equalsIgnoreCase(Boolean.TRUE.toString())) return decompose(true); - throw new MarshalException(String.format("unable to make boolean from '%s'", source)); + throw new MarshalException(String.format("Unable to make boolean from '%s'", source)); + } + + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (parsed instanceof String) + return new Constants.Value(fromString((String) parsed)); + else if (!(parsed instanceof Boolean)) + throw new MarshalException(String.format( + "Expected a boolean value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + + return new Constants.Value(getSerializer().serialize((Boolean) parsed)); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return getSerializer().deserialize(buffer).toString(); } public CQL3Type asCQL3Type() http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/BytesType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/BytesType.java b/src/java/org/apache/cassandra/db/marshal/BytesType.java index a6a672c..eed3872 100644 --- a/src/java/org/apache/cassandra/db/marshal/BytesType.java +++ b/src/java/org/apache/cassandra/db/marshal/BytesType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.BytesSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -50,6 +52,29 @@ public class BytesType extends AbstractType<ByteBuffer> } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + String parsedString = (String) parsed; + if (!parsedString.startsWith("0x")) + throw new MarshalException(String.format("String representation of blob is missing 0x prefix: %s", parsedString)); + + return new Constants.Value(BytesType.instance.fromString(parsedString.substring(2))); + } + catch (ClassCastException | MarshalException exc) + { + throw new MarshalException(String.format("Value '%s' is not a valid blob representation: %s", parsed, exc.getMessage())); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return "\"0x" + ByteBufferUtil.bytesToHex(buffer) + '"'; + } + + @Override public boolean isCompatibleWith(AbstractType<?> previous) { // Both asciiType and utf8Type really use bytes comparison and http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java b/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java index 6fb32fb..1d2c88c 100644 --- a/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java +++ b/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java @@ -23,6 +23,7 @@ import java.util.Map; import com.google.common.collect.ImmutableMap; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.TypeSerializer; @@ -92,6 +93,18 @@ public class ColumnToCollectionType extends AbstractType<ByteBuffer> } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + throw new UnsupportedOperationException(); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + throw new UnsupportedOperationException(); + } + + @Override public void validate(ByteBuffer bytes) { throw new UnsupportedOperationException("ColumnToCollectionType should only be used in composite types, never alone"); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java index 2bcb4db..0fea87b 100644 --- a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java +++ b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java @@ -20,6 +20,7 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.db.context.CounterContext; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.CounterSerializer; @@ -68,6 +69,18 @@ public class CounterColumnType extends AbstractType<Long> return ByteBufferUtil.hexToBytes(source); } + @Override + public Term fromJSONObject(Object parsed) + { + throw new UnsupportedOperationException(); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return CounterSerializer.instance.deserialize(buffer).toString(); + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.COUNTER; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/DateType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/DateType.java b/src/java/org/apache/cassandra/db/marshal/DateType.java index 6413b79..806bbcf 100644 --- a/src/java/org/apache/cassandra/db/marshal/DateType.java +++ b/src/java/org/apache/cassandra/db/marshal/DateType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import java.util.Date; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +57,30 @@ public class DateType extends AbstractType<Date> } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (parsed instanceof Long) + return new Constants.Value(ByteBufferUtil.bytes((Long) parsed)); + + try + { + return new Constants.Value(TimestampType.instance.fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a long or a datestring representation of a date value, but got a %s: %s", + parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return '"' + TimestampSerializer.TO_JSON_FORMAT.format(TimestampSerializer.instance.deserialize(buffer)) + '"'; + } + + @Override public boolean isCompatibleWith(AbstractType<?> previous) { if (super.isCompatibleWith(previous)) http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/DecimalType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/DecimalType.java b/src/java/org/apache/cassandra/db/marshal/DecimalType.java index b7e481d..f1388ce 100644 --- a/src/java/org/apache/cassandra/db/marshal/DecimalType.java +++ b/src/java/org/apache/cassandra/db/marshal/DecimalType.java @@ -21,6 +21,8 @@ import java.math.BigDecimal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.DecimalSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -59,6 +61,25 @@ public class DecimalType extends AbstractType<BigDecimal> return decompose(decimal); } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(getSerializer().serialize(new BigDecimal(parsed.toString()))); + } + catch (NumberFormatException exc) + { + throw new MarshalException(String.format("Value '%s' is not a valid representation of a decimal value", parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return getSerializer().deserialize(buffer).toString(); + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.DECIMAL; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/DoubleType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/DoubleType.java b/src/java/org/apache/cassandra/db/marshal/DoubleType.java index af11a36..fdfd2d2 100644 --- a/src/java/org/apache/cassandra/db/marshal/DoubleType.java +++ b/src/java/org/apache/cassandra/db/marshal/DoubleType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.DoubleSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -52,12 +54,35 @@ public class DoubleType extends AbstractType<Double> } catch (NumberFormatException e1) { - throw new MarshalException(String.format("unable to coerce '%s' to a double", source), e1); + throw new MarshalException(String.format("Unable to make double from '%s'", source), e1); } return decompose(d); } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + if (parsed instanceof String) + return new Constants.Value(fromString((String) parsed)); + else + return new Constants.Value(getSerializer().serialize(((Number) parsed).doubleValue())); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a double value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return getSerializer().deserialize(buffer).toString(); + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.DOUBLE; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java b/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java index c4514d7..97d145d 100644 --- a/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java +++ b/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; +import org.apache.cassandra.cql3.Term; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -373,6 +374,18 @@ public class DynamicCompositeType extends AbstractCompositeType } @Override + public Term fromJSONObject(Object parsed) + { + throw new UnsupportedOperationException(); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + throw new UnsupportedOperationException(); + } + + @Override public void validate(ByteBuffer bytes) { throw new UnsupportedOperationException(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/EmptyType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/EmptyType.java b/src/java/org/apache/cassandra/db/marshal/EmptyType.java index 0ddb9ea..f82d767 100644 --- a/src/java/org/apache/cassandra/db/marshal/EmptyType.java +++ b/src/java/org/apache/cassandra/db/marshal/EmptyType.java @@ -19,6 +19,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.EmptySerializer; import org.apache.cassandra.serializers.MarshalException; @@ -52,6 +54,17 @@ public class EmptyType extends AbstractType<Void> return ByteBufferUtil.EMPTY_BYTE_BUFFER; } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (!(parsed instanceof String)) + throw new MarshalException(String.format("Expected an empty string, but got: %s", parsed)); + if (!((String) parsed).isEmpty()) + throw new MarshalException(String.format("'%s' is not empty", parsed)); + + return new Constants.Value(ByteBufferUtil.EMPTY_BYTE_BUFFER); + } + public TypeSerializer<Void> getSerializer() { return EmptySerializer.instance; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/FloatType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/FloatType.java b/src/java/org/apache/cassandra/db/marshal/FloatType.java index 9364928..722df87 100644 --- a/src/java/org/apache/cassandra/db/marshal/FloatType.java +++ b/src/java/org/apache/cassandra/db/marshal/FloatType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.FloatSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -53,10 +55,33 @@ public class FloatType extends AbstractType<Float> } catch (NumberFormatException e1) { - throw new MarshalException(String.format("unable to coerce '%s' to a float", source), e1); + throw new MarshalException(String.format("Unable to make float from '%s'", source), e1); } } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + if (parsed instanceof String) + return new Constants.Value(fromString((String) parsed)); + else + return new Constants.Value(getSerializer().serialize(((Number) parsed).floatValue())); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a float value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return getSerializer().deserialize(buffer).toString(); + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.FLOAT; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/FrozenType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/FrozenType.java b/src/java/org/apache/cassandra/db/marshal/FrozenType.java index f440c90..7713028 100644 --- a/src/java/org/apache/cassandra/db/marshal/FrozenType.java +++ b/src/java/org/apache/cassandra/db/marshal/FrozenType.java @@ -20,6 +20,7 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import java.util.List; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.TypeSerializer; @@ -55,6 +56,16 @@ public class FrozenType extends AbstractType<Void> throw new UnsupportedOperationException(); } + public Term fromJSONObject(Object parsed) throws MarshalException + { + throw new UnsupportedOperationException(); + } + + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + throw new UnsupportedOperationException(); + } + public TypeSerializer<Void> getSerializer() { throw new UnsupportedOperationException(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java index 0473ee8..a4eac07 100644 --- a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java +++ b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java @@ -21,6 +21,8 @@ import java.net.InetAddress; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.InetAddressSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -51,12 +53,32 @@ public class InetAddressType extends AbstractType<InetAddress> } catch (Exception e) { - throw new MarshalException(String.format("unable to make inetaddress from '%s'", source), e); + throw new MarshalException(String.format("Unable to make inet address from '%s'", source), e); } return decompose(address); } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(InetAddressType.instance.fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a string representation of an inet value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return '"' + getSerializer().deserialize(buffer).getHostAddress() + '"'; + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.INET; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/Int32Type.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/Int32Type.java b/src/java/org/apache/cassandra/db/marshal/Int32Type.java index 976c7a8..9ea8f78 100644 --- a/src/java/org/apache/cassandra/db/marshal/Int32Type.java +++ b/src/java/org/apache/cassandra/db/marshal/Int32Type.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.Int32Serializer; import org.apache.cassandra.serializers.MarshalException; @@ -59,12 +61,39 @@ public class Int32Type extends AbstractType<Integer> } catch (Exception e) { - throw new MarshalException(String.format("unable to make int from '%s'", source), e); + throw new MarshalException(String.format("Unable to make int from '%s'", source), e); } return decompose(int32Type); } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + if (parsed instanceof String) + return new Constants.Value(fromString((String) parsed)); + + Number parsedNumber = (Number) parsed; + if (!(parsedNumber instanceof Integer)) + throw new MarshalException(String.format("Expected an int value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + + return new Constants.Value(getSerializer().serialize(parsedNumber.intValue())); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected an int value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return getSerializer().deserialize(buffer).toString(); + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.INT; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/IntegerType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/IntegerType.java b/src/java/org/apache/cassandra/db/marshal/IntegerType.java index 6da8d39..5fe8fd8 100644 --- a/src/java/org/apache/cassandra/db/marshal/IntegerType.java +++ b/src/java/org/apache/cassandra/db/marshal/IntegerType.java @@ -21,6 +21,8 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.IntegerSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -142,6 +144,26 @@ public final class IntegerType extends AbstractType<BigInteger> } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(getSerializer().serialize(new BigInteger(parsed.toString()))); + } + catch (NumberFormatException exc) + { + throw new MarshalException(String.format( + "Value '%s' is not a valid representation of a varint value", parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return getSerializer().deserialize(buffer).toString(); + } + + @Override public boolean isValueCompatibleWithInternal(AbstractType<?> otherType) { return this == otherType || Int32Type.instance.isValueCompatibleWith(otherType) || LongType.instance.isValueCompatibleWith(otherType); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java index 634194f..3ca5c74 100644 --- a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import java.util.UUID; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.UUIDSerializer; @@ -58,6 +60,20 @@ public class LexicalUUIDType extends AbstractType<UUID> } } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a string representation of a uuid, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + public TypeSerializer<UUID> getSerializer() { return UUIDSerializer.instance; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/ListType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/ListType.java b/src/java/org/apache/cassandra/db/marshal/ListType.java index 510a526..bae8043 100644 --- a/src/java/org/apache/cassandra/db/marshal/ListType.java +++ b/src/java/org/apache/cassandra/db/marshal/ListType.java @@ -20,11 +20,16 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import java.util.*; +import org.apache.cassandra.cql3.Lists; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.db.Cell; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.CollectionSerializer; +import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.ListSerializer; + +import org.apache.cassandra.transport.Server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -171,4 +176,42 @@ public class ListType<T> extends CollectionType<List<T>> bbs.add(c.value()); return bbs; } + + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (!(parsed instanceof List)) + throw new MarshalException(String.format( + "Expected a list, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + + List list = (List) parsed; + List<Term> terms = new ArrayList<>(list.size()); + for (Object element : list) + { + if (element == null) + throw new MarshalException("Invalid null element in list"); + terms.add(elements.fromJSONObject(element)); + } + + return new Lists.DelayedValue(terms); + } + + public static String setOrListToJsonString(ByteBuffer buffer, AbstractType elementsType, int protocolVersion) + { + StringBuilder sb = new StringBuilder("["); + int size = CollectionSerializer.readCollectionSize(buffer, protocolVersion); + for (int i = 0; i < size; i++) + { + if (i > 0) + sb.append(", "); + sb.append(elementsType.toJSONString(CollectionSerializer.readValue(buffer, protocolVersion), protocolVersion)); + } + return sb.append("]").toString(); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return setOrListToJsonString(buffer, elements, protocolVersion); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java b/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java index d1aaac0..427598d 100644 --- a/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java +++ b/src/java/org/apache/cassandra/db/marshal/LocalByPartionerType.java @@ -19,6 +19,7 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -59,6 +60,18 @@ public class LocalByPartionerType extends AbstractType<ByteBuffer> throw new UnsupportedOperationException(); } + @Override + public Term fromJSONObject(Object parsed) + { + throw new UnsupportedOperationException(); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + throw new UnsupportedOperationException(); + } + public int compare(ByteBuffer o1, ByteBuffer o2) { // o1 and o2 can be empty so we need to use RowPosition, not DecoratedKey http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/LongType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java index feedaeb..31bad93 100644 --- a/src/java/org/apache/cassandra/db/marshal/LongType.java +++ b/src/java/org/apache/cassandra/db/marshal/LongType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.LongSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -62,13 +64,40 @@ public class LongType extends AbstractType<Long> } catch (Exception e) { - throw new MarshalException(String.format("unable to make long from '%s'", source), e); + throw new MarshalException(String.format("Unable to make long from '%s'", source), e); } return decompose(longType); } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + if (parsed instanceof String) + return new Constants.Value(fromString((String) parsed)); + + Number parsedNumber = (Number) parsed; + if (!(parsedNumber instanceof Integer || parsedNumber instanceof Long)) + throw new MarshalException(String.format("Expected a bigint value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + + return new Constants.Value(getSerializer().serialize(parsedNumber.longValue())); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a bigint value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return getSerializer().deserialize(buffer).toString(); + } + + @Override public boolean isValueCompatibleWithInternal(AbstractType<?> otherType) { return this == otherType || otherType == DateType.instance || otherType == TimestampType.instance; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/MapType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/MapType.java b/src/java/org/apache/cassandra/db/marshal/MapType.java index 64f3e2a..3ed3dd1 100644 --- a/src/java/org/apache/cassandra/db/marshal/MapType.java +++ b/src/java/org/apache/cassandra/db/marshal/MapType.java @@ -20,10 +20,13 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import java.util.*; +import org.apache.cassandra.cql3.Maps; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.db.Cell; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.CollectionSerializer; +import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.MapSerializer; import org.apache.cassandra.transport.Server; import org.apache.cassandra.utils.Pair; @@ -193,4 +196,43 @@ public class MapType<K, V> extends CollectionType<Map<K, V>> } return bbs; } + + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (!(parsed instanceof Map)) + throw new MarshalException(String.format( + "Expected a map, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + + Map<Object, Object> map = (Map<Object, Object>) parsed; + Map<Term, Term> terms = new HashMap<>(map.size()); + for (Map.Entry<Object, Object> entry : map.entrySet()) + { + if (entry.getKey() == null) + throw new MarshalException("Invalid null key in map"); + + if (entry.getValue() == null) + throw new MarshalException("Invalid null value in map"); + + terms.put(keys.fromJSONObject(entry.getKey()), values.fromJSONObject(entry.getValue())); + } + return new Maps.DelayedValue(keys, terms); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + StringBuilder sb = new StringBuilder("{"); + int size = CollectionSerializer.readCollectionSize(buffer, protocolVersion); + for (int i = 0; i < size; i++) + { + if (i > 0) + sb.append(", "); + + sb.append(keys.toJSONString(CollectionSerializer.readValue(buffer, protocolVersion), protocolVersion)); + sb.append(": "); + sb.append(values.toJSONString(CollectionSerializer.readValue(buffer, protocolVersion), protocolVersion)); + } + return sb.append("}").toString(); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/ReversedType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/ReversedType.java b/src/java/org/apache/cassandra/db/marshal/ReversedType.java index 1323dc6..14d069a 100644 --- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java +++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java @@ -23,8 +23,10 @@ import java.util.Map; import java.util.List; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; +import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.TypeSerializer; public class ReversedType<T> extends AbstractType<T> @@ -84,6 +86,18 @@ public class ReversedType<T> extends AbstractType<T> } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + return baseType.fromJSONObject(parsed); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return baseType.toJSONString(buffer, protocolVersion); + } + + @Override public boolean isCompatibleWith(AbstractType<?> otherType) { if (!(otherType instanceof ReversedType)) http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/SetType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/SetType.java b/src/java/org/apache/cassandra/db/marshal/SetType.java index e10f2a1..372555a 100644 --- a/src/java/org/apache/cassandra/db/marshal/SetType.java +++ b/src/java/org/apache/cassandra/db/marshal/SetType.java @@ -20,10 +20,14 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import java.util.*; +import org.apache.cassandra.cql3.Sets; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.db.Cell; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; +import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.SetSerializer; +import org.apache.cassandra.transport.Server; public class SetType<T> extends CollectionType<Set<T>> { @@ -146,4 +150,29 @@ public class SetType<T> extends CollectionType<Set<T>> bbs.add(c.name().collectionElement()); return bbs; } + + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (!(parsed instanceof List)) + throw new MarshalException(String.format( + "Expected a list (representing a set), but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + + List list = (List) parsed; + Set<Term> terms = new HashSet<>(list.size()); + for (Object element : list) + { + if (element == null) + throw new MarshalException("Invalid null element in set"); + terms.add(elements.fromJSONObject(element)); + } + + return new Sets.DelayedValue(elements, terms); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return ListType.setOrListToJsonString(buffer, elements, protocolVersion); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java index 1dbcf03..a34646f 100644 --- a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java +++ b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.SimpleDateSerializer; import org.apache.cassandra.serializers.TypeSerializer; @@ -60,6 +62,27 @@ public class SimpleDateType extends AbstractType<Integer> } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a string representation of a date value, but got a %s: %s", + parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return '"' + SimpleDateSerializer.instance.toString(SimpleDateSerializer.instance.deserialize(buffer)) + '"'; + } + + @Override public CQL3Type asCQL3Type() { return CQL3Type.Native.DATE; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/TimeType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TimeType.java b/src/java/org/apache/cassandra/db/marshal/TimeType.java index b9a0076..c5c7b98 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimeType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimeType.java @@ -19,6 +19,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TimeSerializer; import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.serializers.TypeSerializer; @@ -60,6 +62,26 @@ public class TimeType extends AbstractType<Long> return this == otherType || otherType == LongType.instance; } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a string representation of a time value, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return '"' + TimeSerializer.instance.toString(TimeSerializer.instance.deserialize(buffer)) + '"'; + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.TIME; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java b/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java index 06443c4..3b38582 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java @@ -21,6 +21,8 @@ import java.nio.ByteBuffer; import java.util.UUID; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.TimeUUIDSerializer; @@ -98,6 +100,19 @@ public class TimeUUIDType extends AbstractType<UUID> } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException( + String.format("Expected a string representation of a timeuuid, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + public CQL3Type asCQL3Type() { return CQL3Type.Native.TIMEUUID; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/TimestampType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TimestampType.java b/src/java/org/apache/cassandra/db/marshal/TimestampType.java index d7ce47b..095f2c2 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimestampType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimestampType.java @@ -20,6 +20,8 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; import java.util.Date; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.cql3.CQL3Type; @@ -58,6 +60,30 @@ public class TimestampType extends AbstractType<Date> } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (parsed instanceof Long) + return new Constants.Value(ByteBufferUtil.bytes((Long) parsed)); + + try + { + return new Constants.Value(TimestampType.instance.fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a long or a datestring representation of a timestamp value, but got a %s: %s", + parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + return '"' + TimestampSerializer.TO_JSON_FORMAT.format(TimestampSerializer.instance.deserialize(buffer)) + '"'; + } + + @Override public boolean isCompatibleWith(AbstractType<?> previous) { if (super.isCompatibleWith(previous)) http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/TupleType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TupleType.java b/src/java/org/apache/cassandra/db/marshal/TupleType.java index 9c63644..6093137 100644 --- a/src/java/org/apache/cassandra/db/marshal/TupleType.java +++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java @@ -18,15 +18,21 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import com.google.common.base.Objects; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; +import org.apache.cassandra.cql3.Tuples; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.*; +import org.apache.cassandra.transport.Server; import org.apache.cassandra.utils.ByteBufferUtil; /** @@ -224,6 +230,56 @@ public class TupleType extends AbstractType<ByteBuffer> return buildValue(fields); } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (!(parsed instanceof List)) + throw new MarshalException(String.format( + "Expected a list representation of a tuple, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + + List list = (List) parsed; + + if (list.size() > types.size()) + throw new MarshalException(String.format("Tuple contains extra items (expected %s): %s", types.size(), parsed)); + else if (types.size() > list.size()) + throw new MarshalException(String.format("Tuple is missing items (expected %s): %s", types.size(), parsed)); + + List<Term> terms = new ArrayList<>(list.size()); + Iterator<AbstractType<?>> typeIterator = types.iterator(); + for (Object element : list) + { + if (element == null) + { + typeIterator.next(); + terms.add(Constants.NULL_VALUE); + } + else + { + terms.add(typeIterator.next().fromJSONObject(element)); + } + } + + return new Tuples.DelayedValue(this, terms); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + StringBuilder sb = new StringBuilder("["); + for (int i = 0; i < types.size(); i++) + { + if (i > 0) + sb.append(", "); + + ByteBuffer value = CollectionSerializer.readValue(buffer, protocolVersion); + if (value == null) + sb.append("null"); + else + sb.append(types.get(i).toJSONString(value, protocolVersion)); + } + return sb.append("]").toString(); + } + public TypeSerializer<ByteBuffer> getSerializer() { return BytesSerializer.instance; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java b/src/java/org/apache/cassandra/db/marshal/UTF8Type.java index 6d58db2..3fd175c 100644 --- a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java +++ b/src/java/org/apache/cassandra/db/marshal/UTF8Type.java @@ -18,8 +18,15 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Json; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Term; +import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.UTF8Serializer; import org.apache.cassandra.utils.ByteBufferUtil; @@ -40,6 +47,34 @@ public class UTF8Type extends AbstractType<String> return decompose(source); } + + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a UTF-8 string, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + try + { + return '"' + new String(Json.JSON_STRING_ENCODER.quoteAsString(ByteBufferUtil.string(buffer, Charset.forName("UTF-8")))) + '"'; + } + catch (CharacterCodingException exc) + { + throw new AssertionError("UTF-8 value contained non-utf8 characters: ", exc); + } + } + @Override public boolean isCompatibleWith(AbstractType<?> previous) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/UUIDType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/UUIDType.java b/src/java/org/apache/cassandra/db/marshal/UUIDType.java index 09dd8b4..eba696e 100644 --- a/src/java/org/apache/cassandra/db/marshal/UUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/UUIDType.java @@ -24,6 +24,8 @@ import java.util.regex.Pattern; import com.google.common.primitives.UnsignedLongs; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.Constants; +import org.apache.cassandra.cql3.Term; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.serializers.UUIDSerializer; @@ -107,7 +109,7 @@ public class UUIDType extends AbstractType<UUID> if (parsed != null) return parsed; - throw new MarshalException(String.format("unable to coerce '%s' to UUID", source)); + throw new MarshalException(String.format("Unable to make UUID from '%s'", source)); } @Override @@ -136,12 +138,27 @@ public class UUIDType extends AbstractType<UUID> } catch (IllegalArgumentException e) { - throw new MarshalException(String.format("unable to make UUID from '%s'", source), e); + throw new MarshalException(String.format("Unable to make UUID from '%s'", source), e); } } + return null; } + @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + try + { + return new Constants.Value(fromString((String) parsed)); + } + catch (ClassCastException exc) + { + throw new MarshalException(String.format( + "Expected a string representation of a uuid, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + } + } + static int version(ByteBuffer uuid) { return (uuid.get(6) & 0xf0) >> 4; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/db/marshal/UserType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/UserType.java b/src/java/org/apache/cassandra/db/marshal/UserType.java index 24488cc..ce8bb43 100644 --- a/src/java/org/apache/cassandra/db/marshal/UserType.java +++ b/src/java/org/apache/cassandra/db/marshal/UserType.java @@ -18,15 +18,17 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.util.*; import com.google.common.base.Objects; -import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.*; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.*; +import org.apache.cassandra.transport.Server; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.Pair; @@ -40,6 +42,7 @@ public class UserType extends TupleType public final String keyspace; public final ByteBuffer name; private final List<ByteBuffer> fieldNames; + private final List<String> stringFieldNames; public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes) { @@ -48,6 +51,18 @@ public class UserType extends TupleType this.keyspace = keyspace; this.name = name; this.fieldNames = fieldNames; + this.stringFieldNames = new ArrayList<>(fieldNames.size()); + for (ByteBuffer fieldName : fieldNames) + { + try + { + stringFieldNames.add(ByteBufferUtil.string(fieldName, Charset.forName("UTF-8"))); + } + catch (CharacterCodingException ex) + { + throw new AssertionError("Got non-UTF8 field name for user-defined type: " + ByteBufferUtil.bytesToHex(fieldName), ex); + } + } } public static UserType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException @@ -123,6 +138,78 @@ public class UserType extends TupleType } @Override + public Term fromJSONObject(Object parsed) throws MarshalException + { + if (!(parsed instanceof Map)) + throw new MarshalException(String.format( + "Expected a map, but got a %s: %s", parsed.getClass().getSimpleName(), parsed)); + + Map<String, Object> map = (Map<String, Object>) parsed; + + Json.handleCaseSensitivity(map); + + List<Term> terms = new ArrayList<>(types.size()); + + Set keys = map.keySet(); + assert keys.isEmpty() || keys.iterator().next() instanceof String; + + int foundValues = 0; + for (int i = 0; i < types.size(); i++) + { + Object value = map.get(stringFieldNames.get(i)); + if (value == null) + { + terms.add(Constants.NULL_VALUE); + } + else + { + terms.add(types.get(i).fromJSONObject(value)); + foundValues += 1; + } + } + + // check for extra, unrecognized fields + if (foundValues != map.size()) + { + for (Object fieldName : keys) + { + if (!stringFieldNames.contains((String) fieldName)) + throw new MarshalException(String.format( + "Unknown field '%s' in value of user defined type %s", fieldName, getNameAsString())); + } + } + + return new UserTypes.DelayedValue(this, terms); + } + + @Override + public String toJSONString(ByteBuffer buffer, int protocolVersion) + { + ByteBuffer[] buffers = split(buffer); + StringBuilder sb = new StringBuilder("{"); + for (int i = 0; i < types.size(); i++) + { + if (i > 0) + sb.append(", "); + + String name = stringFieldNames.get(i); + if (!name.equals(name.toLowerCase(Locale.US))) + name = "\"" + name + "\""; + + sb.append('"'); + sb.append(Json.JSON_STRING_ENCODER.quoteAsString(name)); + sb.append("\": "); + + ByteBuffer valueBuffer = buffers[i]; + if (valueBuffer == null) + sb.append("null"); + else + sb.append(types.get(i).toJSONString(valueBuffer, protocolVersion)); + } + return sb.append("}").toString(); + } + + @Override public int hashCode() { return Objects.hashCode(keyspace, name, fieldNames, types); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java b/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java index 45f16f2..1389488 100644 --- a/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java +++ b/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java @@ -25,6 +25,7 @@ import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.util.*; +import org.apache.cassandra.transport.Server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -437,7 +438,7 @@ public abstract class AbstractCassandraStorage extends LoadFunc implements Store } // NOTE: using protocol v1 serialization format for collections so as to not break // compatibility. Not sure if that's the right thing. - return CollectionSerializer.pack(serialized, objects.size(), 1); + return CollectionSerializer.pack(serialized, objects.size(), Server.VERSION_1); } private ByteBuffer objToMapBB(List<Object> objects) @@ -454,7 +455,7 @@ public abstract class AbstractCassandraStorage extends LoadFunc implements Store } // NOTE: using protocol v1 serialization format for collections so as to not break // compatibility. Not sure if that's the right thing. - return CollectionSerializer.pack(serialized, objects.size(), 1); + return CollectionSerializer.pack(serialized, objects.size(), Server.VERSION_1); } private ByteBuffer objToCompositeBB(List<Object> objects) http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/serializers/CollectionSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/serializers/CollectionSerializer.java b/src/java/org/apache/cassandra/serializers/CollectionSerializer.java index c747bfd..5fb3e0a 100644 --- a/src/java/org/apache/cassandra/serializers/CollectionSerializer.java +++ b/src/java/org/apache/cassandra/serializers/CollectionSerializer.java @@ -92,7 +92,7 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T> return version >= Server.VERSION_3 ? 4 : 2; } - protected static void writeValue(ByteBuffer output, ByteBuffer value, int version) + public static void writeValue(ByteBuffer output, ByteBuffer value, int version) { if (version >= Server.VERSION_3) { @@ -129,7 +129,7 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T> } } - protected static int sizeOfValue(ByteBuffer value, int version) + public static int sizeOfValue(ByteBuffer value, int version) { if (version >= Server.VERSION_3) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/serializers/ListSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/serializers/ListSerializer.java b/src/java/org/apache/cassandra/serializers/ListSerializer.java index aeee2b9..2bbb6d4 100644 --- a/src/java/org/apache/cassandra/serializers/ListSerializer.java +++ b/src/java/org/apache/cassandra/serializers/ListSerializer.java @@ -112,6 +112,20 @@ public class ListSerializer<T> extends CollectionSerializer<List<T>> } /** + * Deserializes a serialized list and returns a list of unserialized (ByteBuffer) elements. + */ + public List<ByteBuffer> deserializeToByteBufferCollection(ByteBuffer bytes, int version) + { + ByteBuffer input = bytes.duplicate(); + int n = readCollectionSize(input, version); + List<ByteBuffer> l = new ArrayList<>(n); + for (int i = 0; i < n; i++) + l.add(readValue(input, version)); + + return l; + } + + /** * Returns the element at the given index in a list. * @param serializedList a serialized list * @param index the index to get
