cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tylerho...@apache.org
Subject cassandra git commit: Fix string encoding of JSON map keys
Date Wed, 13 May 2015 17:39:09 GMT
Repository: cassandra
Updated Branches:
  refs/heads/trunk e379f978b -> b7be1980b


Fix string encoding of JSON map keys

Patch by Tyler Hobbs; reviewed by Jonathan Ellis for CASSANDRA-9190


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b7be1980
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b7be1980
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b7be1980

Branch: refs/heads/trunk
Commit: b7be1980b1b41731f27433cd80743ec76b4beab1
Parents: e379f97
Author: Tyler Hobbs <tylerlhobbs@gmail.com>
Authored: Wed May 13 12:38:26 2015 -0500
Committer: Tyler Hobbs <tylerlhobbs@gmail.com>
Committed: Wed May 13 12:38:26 2015 -0500

----------------------------------------------------------------------
 src/java/org/apache/cassandra/cql3/Json.java    | 12 +++
 .../apache/cassandra/db/marshal/ListType.java   |  4 +
 .../apache/cassandra/db/marshal/MapType.java    | 12 ++-
 .../apache/cassandra/db/marshal/SetType.java    |  4 +
 .../apache/cassandra/db/marshal/TupleType.java  |  8 +-
 .../apache/cassandra/db/marshal/UserType.java   |  3 +
 .../org/apache/cassandra/cql3/JsonTest.java     | 87 ++++++++++++++++++++
 7 files changed, 125 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7be1980/src/java/org/apache/cassandra/cql3/Json.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Json.java b/src/java/org/apache/cassandra/cql3/Json.java
index 905f6e0..e4bce29 100644
--- a/src/java/org/apache/cassandra/cql3/Json.java
+++ b/src/java/org/apache/cassandra/cql3/Json.java
@@ -39,6 +39,18 @@ public class Json
 
     public static final ColumnIdentifier JSON_COLUMN_ID = new ColumnIdentifier("[json]",
true);
 
+    public static Object decodeJson(String json)
+    {
+        try
+        {
+            return JSON_OBJECT_MAPPER.readValue(json, Object.class);
+        }
+        catch (IOException exc)
+        {
+            throw new MarshalException("Error decoding JSON string: " + exc.getMessage());
+        }
+    }
+
     public interface Raw
     {
         public Prepared prepareAndCollectMarkers(CFMetaData metadata, Collection<ColumnDefinition>
receivers, VariableSpecifications boundNames);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7be1980/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 bae8043..03f39d7 100644
--- a/src/java/org/apache/cassandra/db/marshal/ListType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ListType.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.db.marshal;
 import java.nio.ByteBuffer;
 import java.util.*;
 
+import org.apache.cassandra.cql3.Json;
 import org.apache.cassandra.cql3.Lists;
 import org.apache.cassandra.cql3.Term;
 import org.apache.cassandra.db.Cell;
@@ -180,6 +181,9 @@ public class ListType<T> extends CollectionType<List<T>>
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
+        if (parsed instanceof String)
+            parsed = Json.decodeJson((String) parsed);
+
         if (!(parsed instanceof List))
             throw new MarshalException(String.format(
                     "Expected a list, but got a %s: %s", parsed.getClass().getSimpleName(),
parsed));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7be1980/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 3ed3dd1..983710b 100644
--- a/src/java/org/apache/cassandra/db/marshal/MapType.java
+++ b/src/java/org/apache/cassandra/db/marshal/MapType.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.db.marshal;
 import java.nio.ByteBuffer;
 import java.util.*;
 
+import org.apache.cassandra.cql3.Json;
 import org.apache.cassandra.cql3.Maps;
 import org.apache.cassandra.cql3.Term;
 import org.apache.cassandra.db.Cell;
@@ -200,6 +201,9 @@ public class MapType<K, V> extends CollectionType<Map<K, V>>
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
+        if (parsed instanceof String)
+            parsed = Json.decodeJson((String) parsed);
+
         if (!(parsed instanceof Map))
             throw new MarshalException(String.format(
                     "Expected a map, but got a %s: %s", parsed.getClass().getSimpleName(),
parsed));
@@ -229,7 +233,13 @@ public class MapType<K, V> extends CollectionType<Map<K,
V>>
             if (i > 0)
                 sb.append(", ");
 
-            sb.append(keys.toJSONString(CollectionSerializer.readValue(buffer, protocolVersion),
protocolVersion));
+            // map keys must be JSON strings, so convert non-string keys to strings
+            String key = keys.toJSONString(CollectionSerializer.readValue(buffer, protocolVersion),
protocolVersion);
+            if (key.startsWith("\""))
+                sb.append(key);
+            else
+                sb.append('"').append(Json.JSON_STRING_ENCODER.quoteAsString(key)).append('"');
+
             sb.append(": ");
             sb.append(values.toJSONString(CollectionSerializer.readValue(buffer, protocolVersion),
protocolVersion));
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7be1980/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 372555a..78aac25 100644
--- a/src/java/org/apache/cassandra/db/marshal/SetType.java
+++ b/src/java/org/apache/cassandra/db/marshal/SetType.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.db.marshal;
 import java.nio.ByteBuffer;
 import java.util.*;
 
+import org.apache.cassandra.cql3.Json;
 import org.apache.cassandra.cql3.Sets;
 import org.apache.cassandra.cql3.Term;
 import org.apache.cassandra.db.Cell;
@@ -154,6 +155,9 @@ public class SetType<T> extends CollectionType<Set<T>>
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
+        if (parsed instanceof String)
+            parsed = Json.decodeJson((String) parsed);
+
         if (!(parsed instanceof List))
             throw new MarshalException(String.format(
                     "Expected a list (representing a set), but got a %s: %s", parsed.getClass().getSimpleName(),
parsed));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7be1980/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 6093137..e874cb6 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -25,10 +25,7 @@ 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.cql3.*;
 import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.serializers.*;
@@ -233,6 +230,9 @@ public class TupleType extends AbstractType<ByteBuffer>
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
+        if (parsed instanceof String)
+            parsed = Json.decodeJson((String) parsed);
+
         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));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7be1980/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 45c5f0e..5879d6b 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -145,6 +145,9 @@ public class UserType extends TupleType
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
+        if (parsed instanceof String)
+            parsed = Json.decodeJson((String) parsed);
+
         if (!(parsed instanceof Map))
             throw new MarshalException(String.format(
                     "Expected a map, but got a %s: %s", parsed.getClass().getSimpleName(),
parsed));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7be1980/test/unit/org/apache/cassandra/cql3/JsonTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/JsonTest.java b/test/unit/org/apache/cassandra/cql3/JsonTest.java
index 47d6ddd..305502e 100644
--- a/test/unit/org/apache/cassandra/cql3/JsonTest.java
+++ b/test/unit/org/apache/cassandra/cql3/JsonTest.java
@@ -843,6 +843,93 @@ public class JsonTest extends CQLTester
     }
 
     @Test
+    public void testInsertJsonSyntaxWithNonNativeMapKeys() throws Throwable
+    {
+        // JSON doesn't allow non-string keys, so we accept string representations of any
type as map keys and
+        // return maps with string keys when necessary.
+
+        String typeName = createType("CREATE TYPE %s (a int)");
+        createTable("CREATE TABLE %s (" +
+                "k int PRIMARY KEY, " +
+                "intmap map<int, boolean>, " +
+                "bigintmap map<bigint, boolean>, " +
+                "varintmap map<varint, boolean>, " +
+                "booleanmap map<boolean, boolean>, " +
+                "floatmap map<float, boolean>, " +
+                "doublemap map<double, boolean>, " +
+                "decimalmap map<decimal, boolean>, " +
+                "tuplemap map<frozen<tuple<int, text>>, boolean>, " +
+                "udtmap map<frozen<" + typeName + ">, boolean>, " +
+                "setmap map<frozen<set<int>>, boolean>, " +
+                "listmap map<frozen<list<int>>, boolean>, " +
+                "textsetmap map<frozen<set<text>>, boolean>, " +
+                "nestedsetmap map<frozen<map<set<text>, text>>, boolean>,
" +
+                "frozensetmap frozen<map<set<int>, boolean>>)");
+
+        // int keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"intmap\": {\"0\": true, \"1\": false}}");
+        assertRows(execute("SELECT JSON k, intmap FROM %s"), row("{\"k\": 0, \"intmap\":
{\"0\": true, \"1\": false}}"));
+
+        // bigint keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"bigintmap\": {\"0\": true, \"1\":
false}}");
+        assertRows(execute("SELECT JSON k, bigintmap FROM %s"), row("{\"k\": 0, \"bigintmap\":
{\"0\": true, \"1\": false}}"));
+
+        // varint keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"varintmap\": {\"0\": true, \"1\":
false}}");
+        assertRows(execute("SELECT JSON k, varintmap FROM %s"), row("{\"k\": 0, \"varintmap\":
{\"0\": true, \"1\": false}}"));
+
+        // boolean keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"booleanmap\": {\"true\": true, \"false\":
false}}");
+        assertRows(execute("SELECT JSON k, booleanmap FROM %s"), row("{\"k\": 0, \"booleanmap\":
{\"false\": false, \"true\": true}}"));
+
+        // float keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"floatmap\": {\"1.23\": true, \"4.56\":
false}}");
+        assertRows(execute("SELECT JSON k, floatmap FROM %s"), row("{\"k\": 0, \"floatmap\":
{\"1.23\": true, \"4.56\": false}}"));
+
+        // double keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"doublemap\": {\"1.23\": true, \"4.56\":
false}}");
+        assertRows(execute("SELECT JSON k, doublemap FROM %s"), row("{\"k\": 0, \"doublemap\":
{\"1.23\": true, \"4.56\": false}}"));
+
+        // decimal keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"decimalmap\": {\"1.23\": true, \"4.56\":
false}}");
+        assertRows(execute("SELECT JSON k, decimalmap FROM %s"), row("{\"k\": 0, \"decimalmap\":
{\"1.23\": true, \"4.56\": false}}"));
+
+        // tuple<int, text> keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"tuplemap\": {\"[0, \\\"a\\\"]\": true,
\"[1, \\\"b\\\"]\": false}}");
+        assertRows(execute("SELECT JSON k, tuplemap FROM %s"), row("{\"k\": 0, \"tuplemap\":
{\"[0, \\\"a\\\"]\": true, \"[1, \\\"b\\\"]\": false}}"));
+
+        // UDT keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"udtmap\": {\"{\\\"a\\\": 0}\": true,
\"{\\\"a\\\": 1}\": false}}");
+        assertRows(execute("SELECT JSON k, udtmap FROM %s"), row("{\"k\": 0, \"udtmap\":
{\"{\\\"a\\\": 0}\": true, \"{\\\"a\\\": 1}\": false}}"));
+
+        // set<int> keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"setmap\": {\"[0, 1, 2]\": true, \"[3,
4, 5]\": false}}");
+        assertRows(execute("SELECT JSON k, setmap FROM %s"), row("{\"k\": 0, \"setmap\":
{\"[0, 1, 2]\": true, \"[3, 4, 5]\": false}}"));
+
+        // list<int> keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"listmap\": {\"[0, 1, 2]\": true, \"[3,
4, 5]\": false}}");
+        assertRows(execute("SELECT JSON k, listmap FROM %s"), row("{\"k\": 0, \"listmap\":
{\"[0, 1, 2]\": true, \"[3, 4, 5]\": false}}"));
+
+        // set<text> keys
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"textsetmap\": {\"[\\\"0\\\", \\\"1\\\"]\":
true, \"[\\\"3\\\", \\\"4\\\"]\": false}}");
+        assertRows(execute("SELECT JSON k, textsetmap FROM %s"), row("{\"k\": 0, \"textsetmap\":
{\"[\\\"0\\\", \\\"1\\\"]\": true, \"[\\\"3\\\", \\\"4\\\"]\": false}}"));
+
+        // map<set<text>, text> keys
+        String innerKey1 = "[\"0\", \"1\"]";
+        String fullKey1 = String.format("{\"%s\": \"%s\"}", new String(Json.JSON_STRING_ENCODER.quoteAsString(innerKey1)),
"a");
+        String stringKey1 = new String(Json.JSON_STRING_ENCODER.quoteAsString(fullKey1));
+        String innerKey2 = "[\"3\", \"4\"]";
+        String fullKey2 = String.format("{\"%s\": \"%s\"}", new String(Json.JSON_STRING_ENCODER.quoteAsString(innerKey2)),
"b");
+        String stringKey2 = new String(Json.JSON_STRING_ENCODER.quoteAsString(fullKey2));
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"nestedsetmap\": {\"" + stringKey1
+ "\": true, \"" + stringKey2 + "\": false}}");
+        assertRows(execute("SELECT JSON k, nestedsetmap FROM %s"), row("{\"k\": 0, \"nestedsetmap\":
{\"" + stringKey1 + "\": true, \"" + stringKey2 + "\": false}}"));
+
+        // set<int> keys in a frozen map
+        execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"frozensetmap\": {\"[0, 1, 2]\": true,
\"[3, 4, 5]\": false}}");
+        assertRows(execute("SELECT JSON k, frozensetmap FROM %s"), row("{\"k\": 0, \"frozensetmap\":
{\"[0, 1, 2]\": true, \"[3, 4, 5]\": false}}"));
+    }
+
+    @Test
     public void testInsertJsonSyntaxWithTuplesAndUDTs() throws Throwable
     {
         String typeName = createType("CREATE TYPE %s (a int, b frozen<set<int>>,
c tuple<int, int>)");


Mime
View raw message