Return-Path: X-Original-To: apmail-cassandra-commits-archive@www.apache.org Delivered-To: apmail-cassandra-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 5FE4317AAD for ; Wed, 1 Apr 2015 17:42:45 +0000 (UTC) Received: (qmail 80239 invoked by uid 500); 1 Apr 2015 17:42:45 -0000 Delivered-To: apmail-cassandra-commits-archive@cassandra.apache.org Received: (qmail 80128 invoked by uid 500); 1 Apr 2015 17:42:44 -0000 Mailing-List: contact commits-help@cassandra.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cassandra.apache.org Delivered-To: mailing list commits@cassandra.apache.org Received: (qmail 79895 invoked by uid 99); 1 Apr 2015 17:42:44 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 01 Apr 2015 17:42:44 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 73521E0AB7; Wed, 1 Apr 2015 17:42:44 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: tylerhobbs@apache.org To: commits@cassandra.apache.org Date: Wed, 01 Apr 2015 17:42:44 -0000 Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: [1/3] cassandra git commit: Add SELECT/INSERT JSON support, toJson(), fromJson() Repository: cassandra Updated Branches: refs/heads/trunk 61e063b04 -> c7b02d1a6 http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/serializers/MapSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/serializers/MapSerializer.java b/src/java/org/apache/cassandra/serializers/MapSerializer.java index 8350f66..87b620a 100644 --- a/src/java/org/apache/cassandra/serializers/MapSerializer.java +++ b/src/java/org/apache/cassandra/serializers/MapSerializer.java @@ -116,6 +116,21 @@ public class MapSerializer extends CollectionSerializer> } /** + * Deserializes a serialized map and returns a map of unserialized (ByteBuffer) keys and values. + */ + public Map deserializeToByteBufferCollection(ByteBuffer bytes, int protocolVersion) + { + ByteBuffer input = bytes.duplicate(); + int n = readCollectionSize(input, protocolVersion); + Map m = new LinkedHashMap<>(n); + + for (int i = 0; i < n; i++) + m.put(readValue(input, protocolVersion), readValue(input, protocolVersion)); + + return m; + } + + /** * Given a serialized map, gets the value associated with a given key. * @param serializedMap a serialized map * @param serializedKey a serialized key http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/serializers/SetSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/serializers/SetSerializer.java b/src/java/org/apache/cassandra/serializers/SetSerializer.java index 21f5075..970cfab 100644 --- a/src/java/org/apache/cassandra/serializers/SetSerializer.java +++ b/src/java/org/apache/cassandra/serializers/SetSerializer.java @@ -98,6 +98,19 @@ public class SetSerializer extends CollectionSerializer> } } + /** + * Deserializes a serialized set and returns a set of unserialized (ByteBuffer) elements. + */ + public Set deserializeToByteBufferCollection(ByteBuffer bytes, int version) + { + ByteBuffer input = bytes.duplicate(); + int n = readCollectionSize(input, version); + Set s = new LinkedHashSet<>(n); + for (int i = 0; i < n; i++) + s.add(readValue(input, version)); + return s; + } + public String toString(Set value) { StringBuilder sb = new StringBuilder(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java index 5cb9586..b575e73 100644 --- a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java +++ b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java @@ -33,12 +33,12 @@ public class TimestampSerializer implements TypeSerializer "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mmX", - "yyyy-MM-dd HH:mmXX", + "yyyy-MM-dd HH:mmXX", // DEFAULT_FORMAT "yyyy-MM-dd HH:mmXXX", "yyyy-MM-dd HH:mm:ssX", "yyyy-MM-dd HH:mm:ssXX", "yyyy-MM-dd HH:mm:ssXXX", - "yyyy-MM-dd HH:mm:ss.SSS", + "yyyy-MM-dd HH:mm:ss.SSS", // TO_JSON_FORMAT "yyyy-MM-dd HH:mm:ss.SSSX", "yyyy-MM-dd HH:mm:ss.SSSXX", "yyyy-MM-dd HH:mm:ss.SSSXXX", @@ -71,6 +71,8 @@ public class TimestampSerializer implements TypeSerializer } }; + public static final SimpleDateFormat TO_JSON_FORMAT = new SimpleDateFormat(dateStringPatterns[8]); + public static final TimestampSerializer instance = new TimestampSerializer(); public Date deserialize(ByteBuffer bytes) @@ -97,7 +99,7 @@ public class TimestampSerializer implements TypeSerializer } catch (NumberFormatException e) { - throw new MarshalException(String.format("unable to make long (for date) from: '%s'", source), e); + throw new MarshalException(String.format("Unable to make long (for date) from: '%s'", source), e); } } @@ -108,7 +110,7 @@ public class TimestampSerializer implements TypeSerializer } catch (ParseException e1) { - throw new MarshalException(String.format("unable to coerce '%s' to a formatted date (long)", source), e1); + throw new MarshalException(String.format("Unable to coerce '%s' to a formatted date (long)", source), e1); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/test/unit/org/apache/cassandra/cql3/CQLTester.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java index 98a696c..6a7e52f 100644 --- a/test/unit/org/apache/cassandra/cql3/CQLTester.java +++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java @@ -69,7 +69,7 @@ public abstract class CQLTester public static final String KEYSPACE = "cql_test_keyspace"; public static final String KEYSPACE_PER_TEST = "cql_test_keyspace_alt"; - private static final boolean USE_PREPARED_VALUES = Boolean.valueOf(System.getProperty("cassandra.test.use_prepared", "true")); + protected static final boolean USE_PREPARED_VALUES = Boolean.valueOf(System.getProperty("cassandra.test.use_prepared", "true")); private static final AtomicInteger seqNumber = new AtomicInteger(); private static org.apache.cassandra.transport.Server server; @@ -668,8 +668,8 @@ public abstract class CQLTester { execute(query, values); String q = USE_PREPARED_VALUES - ? query + " (values: " + formatAllValues(values) + ")" - : replaceValues(query, values); + ? query + " (values: " + formatAllValues(values) + ")" + : replaceValues(query, values); Assert.fail("Query should be invalid but no error was thrown. Query is: " + q); } catch (CassandraException e) @@ -924,10 +924,11 @@ public abstract class CQLTester AbstractType type = typeFor(value); String s = type.getString(type.decompose(value)); - if (type instanceof UTF8Type) + if (type instanceof InetAddressType || type instanceof TimestampType) + return String.format("'%s'", s); + else if (type instanceof UTF8Type) return String.format("'%s'", s.replaceAll("'", "''")); - - if (type instanceof BytesType) + else if (type instanceof BytesType) return "0x" + s; return s; @@ -1022,6 +1023,15 @@ public abstract class CQLTester if (value instanceof Boolean) return BooleanType.instance; + if (value instanceof InetAddress) + return InetAddressType.instance; + + if (value instanceof Date) + return TimestampType.instance; + + if (value instanceof UUID) + return UUIDType.instance; + if (value instanceof List) { List l = (List)value; http://git-wip-us.apache.org/repos/asf/cassandra/blob/c7b02d1a/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 new file mode 100644 index 0000000..47d6ddd --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/JsonTest.java @@ -0,0 +1,860 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.cql3; + +import org.apache.cassandra.serializers.SimpleDateSerializer; +import org.apache.cassandra.serializers.TimeSerializer; +import org.apache.cassandra.utils.ByteBufferUtil; +import org.junit.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.InetAddress; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class JsonTest extends CQLTester +{ + + @Test + public void testFromJsonFct() throws Throwable + { + String typeName = createType("CREATE TYPE %s (a int, b uuid, c set)"); + createTable("CREATE TABLE %s (" + + "k int PRIMARY KEY, " + + "asciival ascii, " + + "bigintval bigint, " + + "blobval blob, " + + "booleanval boolean, " + + "dateval date, " + + "decimalval decimal, " + + "doubleval double, " + + "floatval float, " + + "inetval inet, " + + "intval int, " + + "textval text, " + + "timeval time, " + + "timestampval timestamp, " + + "timeuuidval timeuuid, " + + "uuidval uuid," + + "varcharval varchar, " + + "varintval varint, " + + "listval list, " + + "frozenlistval frozen>, " + + "setval set, " + + "frozensetval frozen>, " + + "mapval map," + + "frozenmapval frozen>," + + "tupleval frozen>," + + "udtval frozen<" + typeName + ">)"); + + + // fromJson() can only be used when the receiver type is known + assertInvalidMessage("fromJson() cannot be used in the selection clause", "SELECT fromJson(asciival) FROM %s", 0, 0); + + String func1 = createFunction(KEYSPACE, "int", "CREATE FUNCTION %s (a int) RETURNS text LANGUAGE java AS $$ return a.toString(); $$"); + createFunctionOverload(func1, "int", "CREATE FUNCTION %s (a text) RETURNS text LANGUAGE java AS $$ return new String(a); $$"); + + assertInvalidMessage("Ambiguous call to function", + "INSERT INTO %s (k, textval) VALUES (?, " + func1 + "(fromJson(?)))", 0, "123"); + + // fails JSON parsing + assertInvalidMessage("Could not decode JSON string '\u038E\u0394\u03B4\u03E0'", + "INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\u038E\u0394\u03B4\u03E0"); + + // handle nulls + execute("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, null); + + // ================ ascii ================ + execute("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii text\""); + assertRows(execute("SELECT k, asciival FROM %s WHERE k = ?", 0), row(0, "ascii text")); + + execute("INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"ascii \\\" text\""); + assertRows(execute("SELECT k, asciival FROM %s WHERE k = ?", 0), row(0, "ascii \" text")); + + assertInvalidMessage("Invalid ASCII character in string literal", + "INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "\"\\u1fff\\u2013\\u33B4\\u2014\""); + + assertInvalidMessage("Expected an ascii string, but got a Integer", + "INSERT INTO %s (k, asciival) VALUES (?, fromJson(?))", 0, "123"); + + // test that we can use fromJson() in other valid places in queries + assertRows(execute("SELECT asciival FROM %s WHERE k = fromJson(?)", "0"), row("ascii \" text")); + execute("UPDATE %s SET asciival = fromJson(?) WHERE k = fromJson(?)", "\"ascii \\\" text\"", "0"); + execute("DELETE FROM %s WHERE k = fromJson(?)", "0"); + + // ================ bigint ================ + execute("INSERT INTO %s (k, bigintval) VALUES (?, fromJson(?))", 0, "123123123123"); + assertRows(execute("SELECT k, bigintval FROM %s WHERE k = ?", 0), row(0, 123123123123L)); + + // strings are also accepted + execute("INSERT INTO %s (k, bigintval) VALUES (?, fromJson(?))", 0, "\"123123123123\""); + assertRows(execute("SELECT k, bigintval FROM %s WHERE k = ?", 0), row(0, 123123123123L)); + + // overflow (Long.MAX_VALUE + 1) + assertInvalidMessage("Expected a bigint value, but got a", + "INSERT INTO %s (k, bigintval) VALUES (?, fromJson(?))", 0, "9223372036854775808"); + + assertInvalidMessage("Expected a bigint value, but got a", + "INSERT INTO %s (k, bigintval) VALUES (?, fromJson(?))", 0, "123.456"); + + assertInvalidMessage("Unable to make long from", + "INSERT INTO %s (k, bigintval) VALUES (?, fromJson(?))", 0, "\"abc\""); + + assertInvalidMessage("Expected a bigint value, but got a", + "INSERT INTO %s (k, bigintval) VALUES (?, fromJson(?))", 0, "[\"abc\"]"); + + // ================ blob ================ + execute("INSERT INTO %s (k, blobval) VALUES (?, fromJson(?))", 0, "\"0x00000001\""); + assertRows(execute("SELECT k, blobval FROM %s WHERE k = ?", 0), row(0, ByteBufferUtil.bytes(1))); + + assertInvalidMessage("Value 'xyzz' is not a valid blob representation", + "INSERT INTO %s (k, blobval) VALUES (?, fromJson(?))", 0, "\"xyzz\""); + + assertInvalidMessage("String representation of blob is missing 0x prefix: 123", + "INSERT INTO %s (k, blobval) VALUES (?, fromJson(?))", 0, "\"123\""); + + assertInvalidMessage("Value '0x123' is not a valid blob representation", + "INSERT INTO %s (k, blobval) VALUES (?, fromJson(?))", 0, "\"0x123\""); + + assertInvalidMessage("Value '123' is not a valid blob representation", + "INSERT INTO %s (k, blobval) VALUES (?, fromJson(?))", 0, "123"); + + // ================ boolean ================ + execute("INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "true"); + assertRows(execute("SELECT k, booleanval FROM %s WHERE k = ?", 0), row(0, true)); + + execute("INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "false"); + assertRows(execute("SELECT k, booleanval FROM %s WHERE k = ?", 0), row(0, false)); + + // strings are also accepted + execute("INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "\"false\""); + assertRows(execute("SELECT k, booleanval FROM %s WHERE k = ?", 0), row(0, false)); + + assertInvalidMessage("Unable to make boolean from", + "INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "\"abc\""); + + assertInvalidMessage("Expected a boolean value, but got a Integer", + "INSERT INTO %s (k, booleanval) VALUES (?, fromJson(?))", 0, "123"); + + // ================ date ================ + execute("INSERT INTO %s (k, dateval) VALUES (?, fromJson(?))", 0, "\"1987-03-23\""); + assertRows(execute("SELECT k, dateval FROM %s WHERE k = ?", 0), row(0, SimpleDateSerializer.dateStringToDays("1987-03-23"))); + + assertInvalidMessage("Expected a string representation of a date", + "INSERT INTO %s (k, dateval) VALUES (?, fromJson(?))", 0, "123"); + + assertInvalidMessage("Unable to coerce 'xyz' to a formatted date", + "INSERT INTO %s (k, dateval) VALUES (?, fromJson(?))", 0, "\"xyz\""); + + // ================ decimal ================ + execute("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "123123.123123"); + assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("123123.123123"))); + + execute("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "123123"); + assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("123123"))); + + // accept strings for numbers that cannot be represented as doubles + execute("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"123123.123123\""); + assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("123123.123123"))); + + execute("INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"-1.23E-12\""); + assertRows(execute("SELECT k, decimalval FROM %s WHERE k = ?", 0), row(0, new BigDecimal("-1.23E-12"))); + + assertInvalidMessage("Value 'xyzz' is not a valid representation of a decimal value", + "INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "\"xyzz\""); + + assertInvalidMessage("Value 'true' is not a valid representation of a decimal value", + "INSERT INTO %s (k, decimalval) VALUES (?, fromJson(?))", 0, "true"); + + // ================ double ================ + execute("INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "123123.123123"); + assertRows(execute("SELECT k, doubleval FROM %s WHERE k = ?", 0), row(0, 123123.123123d)); + + execute("INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "123123"); + assertRows(execute("SELECT k, doubleval FROM %s WHERE k = ?", 0), row(0, 123123.0d)); + + // strings are also accepted + execute("INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "\"123123\""); + assertRows(execute("SELECT k, doubleval FROM %s WHERE k = ?", 0), row(0, 123123.0d)); + + assertInvalidMessage("Unable to make double from", + "INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "\"xyzz\""); + + assertInvalidMessage("Expected a double value, but got", + "INSERT INTO %s (k, doubleval) VALUES (?, fromJson(?))", 0, "true"); + + // ================ float ================ + execute("INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "123123.123123"); + assertRows(execute("SELECT k, floatval FROM %s WHERE k = ?", 0), row(0, 123123.123123f)); + + execute("INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "123123"); + assertRows(execute("SELECT k, floatval FROM %s WHERE k = ?", 0), row(0, 123123.0f)); + + // strings are also accepted + execute("INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "\"123123.0\""); + assertRows(execute("SELECT k, floatval FROM %s WHERE k = ?", 0), row(0, 123123.0f)); + + assertInvalidMessage("Unable to make float from", + "INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "\"xyzz\""); + + assertInvalidMessage("Expected a float value, but got a", + "INSERT INTO %s (k, floatval) VALUES (?, fromJson(?))", 0, "true"); + + // ================ inet ================ + execute("INSERT INTO %s (k, inetval) VALUES (?, fromJson(?))", 0, "\"127.0.0.1\""); + assertRows(execute("SELECT k, inetval FROM %s WHERE k = ?", 0), row(0, InetAddress.getByName("127.0.0.1"))); + + execute("INSERT INTO %s (k, inetval) VALUES (?, fromJson(?))", 0, "\"::1\""); + assertRows(execute("SELECT k, inetval FROM %s WHERE k = ?", 0), row(0, InetAddress.getByName("::1"))); + + assertInvalidMessage("Unable to make inet address from 'xyzz'", + "INSERT INTO %s (k, inetval) VALUES (?, fromJson(?))", 0, "\"xyzz\""); + + assertInvalidMessage("Expected a string representation of an inet value, but got a Integer", + "INSERT INTO %s (k, inetval) VALUES (?, fromJson(?))", 0, "123"); + + // ================ int ================ + execute("INSERT INTO %s (k, intval) VALUES (?, fromJson(?))", 0, "123123"); + assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 123123)); + + // strings are also accepted + execute("INSERT INTO %s (k, intval) VALUES (?, fromJson(?))", 0, "\"123123\""); + assertRows(execute("SELECT k, intval FROM %s WHERE k = ?", 0), row(0, 123123)); + + // int overflow (2 ^ 32, or Integer.MAX_INT + 1) + assertInvalidMessage("Expected an int value, but got a", + "INSERT INTO %s (k, intval) VALUES (?, fromJson(?))", 0, "2147483648"); + + assertInvalidMessage("Expected an int value, but got a", + "INSERT INTO %s (k, intval) VALUES (?, fromJson(?))", 0, "123.456"); + + assertInvalidMessage("Unable to make int from", + "INSERT INTO %s (k, intval) VALUES (?, fromJson(?))", 0, "\"xyzz\""); + + assertInvalidMessage("Expected an int value, but got a", + "INSERT INTO %s (k, intval) VALUES (?, fromJson(?))", 0, "true"); + + // ================ text (varchar) ================ + execute("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"\""); + assertRows(execute("SELECT k, textval FROM %s WHERE k = ?", 0), row(0, "")); + + execute("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"abcd\""); + assertRows(execute("SELECT k, textval FROM %s WHERE k = ?", 0), row(0, "abcd")); + + execute("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"some \\\" text\""); + assertRows(execute("SELECT k, textval FROM %s WHERE k = ?", 0), row(0, "some \" text")); + + execute("INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "\"\\u2013\""); + assertRows(execute("SELECT k, textval FROM %s WHERE k = ?", 0), row(0, "\u2013")); + + assertInvalidMessage("Expected a UTF-8 string, but got a Integer", + "INSERT INTO %s (k, textval) VALUES (?, fromJson(?))", 0, "123"); + + // ================ time ================ + execute("INSERT INTO %s (k, timeval) VALUES (?, fromJson(?))", 0, "\"07:35:07.000111222\""); + assertRows(execute("SELECT k, timeval FROM %s WHERE k = ?", 0), row(0, TimeSerializer.timeStringToLong("07:35:07.000111222"))); + + assertInvalidMessage("Expected a string representation of a time value", + "INSERT INTO %s (k, timeval) VALUES (?, fromJson(?))", 0, "123456"); + + assertInvalidMessage("Unable to coerce 'xyz' to a formatted time", + "INSERT INTO %s (k, timeval) VALUES (?, fromJson(?))", 0, "\"xyz\""); + + // ================ timestamp ================ + execute("INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "123123123123"); + assertRows(execute("SELECT k, timestampval FROM %s WHERE k = ?", 0), row(0, new Date(123123123123L))); + + execute("INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "\"2014-01-01\""); + assertRows(execute("SELECT k, timestampval FROM %s WHERE k = ?", 0), row(0, new SimpleDateFormat("y-M-d").parse("2014-01-01"))); + + assertInvalidMessage("Expected a long or a datestring representation of a timestamp value, but got a Double", + "INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "123.456"); + + assertInvalidMessage("Unable to coerce 'abcd' to a formatted date", + "INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "\"abcd\""); + + // ================ timeuuid ================ + execute("INSERT INTO %s (k, timeuuidval) VALUES (?, fromJson(?))", 0, "\"6bddc89a-5644-11e4-97fc-56847afe9799\""); + assertRows(execute("SELECT k, timeuuidval FROM %s WHERE k = ?", 0), row(0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); + + execute("INSERT INTO %s (k, timeuuidval) VALUES (?, fromJson(?))", 0, "\"6BDDC89A-5644-11E4-97FC-56847AFE9799\""); + assertRows(execute("SELECT k, timeuuidval FROM %s WHERE k = ?", 0), row(0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); + + assertInvalidMessage("TimeUUID supports only version 1 UUIDs", + "INSERT INTO %s (k, timeuuidval) VALUES (?, fromJson(?))", 0, "\"00000000-0000-0000-0000-000000000000\""); + + assertInvalidMessage("Expected a string representation of a timeuuid, but got a Integer", + "INSERT INTO %s (k, timeuuidval) VALUES (?, fromJson(?))", 0, "123"); + + // ================ uuidval ================ + execute("INSERT INTO %s (k, uuidval) VALUES (?, fromJson(?))", 0, "\"6bddc89a-5644-11e4-97fc-56847afe9799\""); + assertRows(execute("SELECT k, uuidval FROM %s WHERE k = ?", 0), row(0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); + + execute("INSERT INTO %s (k, uuidval) VALUES (?, fromJson(?))", 0, "\"6BDDC89A-5644-11E4-97FC-56847AFE9799\""); + assertRows(execute("SELECT k, uuidval FROM %s WHERE k = ?", 0), row(0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); + + assertInvalidMessage("Unable to make UUID from", + "INSERT INTO %s (k, uuidval) VALUES (?, fromJson(?))", 0, "\"00000000-0000-0000-zzzz-000000000000\""); + + assertInvalidMessage("Expected a string representation of a uuid, but got a Integer", + "INSERT INTO %s (k, uuidval) VALUES (?, fromJson(?))", 0, "123"); + + // ================ varint ================ + execute("INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "123123123123"); + assertRows(execute("SELECT k, varintval FROM %s WHERE k = ?", 0), row(0, new BigInteger("123123123123"))); + + // accept strings for numbers that cannot be represented as longs + execute("INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "\"1234567890123456789012345678901234567890\""); + assertRows(execute("SELECT k, varintval FROM %s WHERE k = ?", 0), row(0, new BigInteger("1234567890123456789012345678901234567890"))); + + assertInvalidMessage("Value '123123.123' is not a valid representation of a varint value", + "INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "123123.123"); + + assertInvalidMessage("Value 'xyzz' is not a valid representation of a varint value", + "INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "\"xyzz\""); + + assertInvalidMessage("Value '' is not a valid representation of a varint value", + "INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "\"\""); + + assertInvalidMessage("Value 'true' is not a valid representation of a varint value", + "INSERT INTO %s (k, varintval) VALUES (?, fromJson(?))", 0, "true"); + + // ================ lists ================ + execute("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[1, 2, 3]"); + assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, list(1, 2, 3))); + + execute("INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[]"); + assertRows(execute("SELECT k, listval FROM %s WHERE k = ?", 0), row(0, null)); + + assertInvalidMessage("Expected a list, but got a Integer", + "INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "123"); + + assertInvalidMessage("Unable to make int from", + "INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[\"abc\"]"); + + assertInvalidMessage("Invalid null element in list", + "INSERT INTO %s (k, listval) VALUES (?, fromJson(?))", 0, "[null]"); + + // frozen + execute("INSERT INTO %s (k, frozenlistval) VALUES (?, fromJson(?))", 0, "[1, 2, 3]"); + assertRows(execute("SELECT k, frozenlistval FROM %s WHERE k = ?", 0), row(0, list(1, 2, 3))); + + // ================ sets ================ + execute("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", + 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + assertRows(execute("SELECT k, setval FROM %s WHERE k = ?", 0), + row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) + ); + + // duplicates are okay, just like in CQL + execute("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", + 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + assertRows(execute("SELECT k, setval FROM %s WHERE k = ?", 0), + row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) + ); + + execute("INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", 0, "[]"); + assertRows(execute("SELECT k, setval FROM %s WHERE k = ?", 0), row(0, null)); + + assertInvalidMessage("Expected a list (representing a set), but got a Integer", + "INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", 0, "123"); + + assertInvalidMessage("Unable to make UUID from", + "INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", 0, "[\"abc\"]"); + + assertInvalidMessage("Invalid null element in set", + "INSERT INTO %s (k, setval) VALUES (?, fromJson(?))", 0, "[null]"); + + // frozen + execute("INSERT INTO %s (k, frozensetval) VALUES (?, fromJson(?))", + 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + assertRows(execute("SELECT k, frozensetval FROM %s WHERE k = ?", 0), + row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) + ); + + execute("INSERT INTO %s (k, frozensetval) VALUES (?, fromJson(?))", + 0, "[\"6bddc89a-5644-11e4-97fc-56847afe9799\", \"6bddc89a-5644-11e4-97fc-56847afe9798\"]"); + assertRows(execute("SELECT k, frozensetval FROM %s WHERE k = ?", 0), + row(0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))) + ); + + // ================ maps ================ + execute("INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": 2}"); + assertRows(execute("SELECT k, mapval FROM %s WHERE k = ?", 0), row(0, map("a", 1, "b", 2))); + + execute("INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "{}"); + assertRows(execute("SELECT k, mapval FROM %s WHERE k = ?", 0), row(0, null)); + + assertInvalidMessage("Expected a map, but got a Integer", + "INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "123"); + + assertInvalidMessage("Invalid ASCII character in string literal", + "INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "{\"\\u1fff\\u2013\\u33B4\\u2014\": 1}"); + + assertInvalidMessage("Invalid null value in map", + "INSERT INTO %s (k, mapval) VALUES (?, fromJson(?))", 0, "{\"a\": null}"); + + // frozen + execute("INSERT INTO %s (k, frozenmapval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": 2}"); + assertRows(execute("SELECT k, frozenmapval FROM %s WHERE k = ?", 0), row(0, map("a", 1, "b", 2))); + + execute("INSERT INTO %s (k, frozenmapval) VALUES (?, fromJson(?))", 0, "{\"b\": 2, \"a\": 1}"); + assertRows(execute("SELECT k, frozenmapval FROM %s WHERE k = ?", 0), row(0, map("a", 1, "b", 2))); + + // ================ tuples ================ + execute("INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", 0, "[1, \"foobar\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + assertRows(execute("SELECT k, tupleval FROM %s WHERE k = ?", 0), + row(0, tuple(1, "foobar", UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))) + ); + + execute("INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", 0, "[1, null, \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + assertRows(execute("SELECT k, tupleval FROM %s WHERE k = ?", 0), + row(0, tuple(1, null, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))) + ); + + assertInvalidMessage("Tuple contains extra items", + "INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", + 0, "[1, \"foobar\", \"6bddc89a-5644-11e4-97fc-56847afe9799\", 1, 2, 3]"); + + assertInvalidMessage("Tuple is missing items", + "INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", + 0, "[1, \"foobar\"]"); + + assertInvalidMessage("Unable to make int from", + "INSERT INTO %s (k, tupleval) VALUES (?, fromJson(?))", + 0, "[\"not an int\", \"foobar\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]"); + + // ================ UDTs ================ + execute("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"c\": [\"foo\", \"bar\"]}"); + assertRows(execute("SELECT k, udtval.a, udtval.b, udtval.c FROM %s WHERE k = ?", 0), + row(0, 1, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), set("bar", "foo")) + ); + + // order of fields shouldn't matter + execute("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"a\": 1, \"c\": [\"foo\", \"bar\"]}"); + assertRows(execute("SELECT k, udtval.a, udtval.b, udtval.c FROM %s WHERE k = ?", 0), + row(0, 1, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), set("bar", "foo")) + ); + + // test nulls + execute("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": null, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"c\": [\"foo\", \"bar\"]}"); + assertRows(execute("SELECT k, udtval.a, udtval.b, udtval.c FROM %s WHERE k = ?", 0), + row(0, null, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), set("bar", "foo")) + ); + + // test missing fields + execute("INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": 1, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\"}"); + assertRows(execute("SELECT k, udtval.a, udtval.b, udtval.c FROM %s WHERE k = ?", 0), + row(0, 1, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), null) + ); + + assertInvalidMessage("Unknown field", "INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"xxx\": 1}"); + assertInvalidMessage("Unable to make int from", + "INSERT INTO %s (k, udtval) VALUES (?, fromJson(?))", 0, "{\"a\": \"foobar\"}"); + } + + @Test + public void testToJsonFct() throws Throwable + { + String typeName = createType("CREATE TYPE %s (a int, b uuid, c set)"); + createTable("CREATE TABLE %s (" + + "k int PRIMARY KEY, " + + "asciival ascii, " + + "bigintval bigint, " + + "blobval blob, " + + "booleanval boolean, " + + "dateval date, " + + "decimalval decimal, " + + "doubleval double, " + + "floatval float, " + + "inetval inet, " + + "intval int, " + + "textval text, " + + "timeval time, " + + "timestampval timestamp, " + + "timeuuidval timeuuid, " + + "uuidval uuid," + + "varcharval varchar, " + + "varintval varint, " + + "listval list, " + + "frozenlistval frozen>, " + + "setval set, " + + "frozensetval frozen>, " + + "mapval map, " + + "frozenmapval frozen>, " + + "tupleval frozen>," + + "udtval frozen<" + typeName + ">)"); + + // toJson() can only be used in selections + assertInvalidMessage("toJson() may only be used within the selection clause", + "INSERT INTO %s (k, asciival) VALUES (?, toJson(?))", 0, 0); + assertInvalidMessage("toJson() may only be used within the selection clause", + "UPDATE %s SET asciival = toJson(?) WHERE k = ?", 0, 0); + assertInvalidMessage("toJson() may only be used within the selection clause", + "DELETE FROM %s WHERE k = fromJson(toJson(?))", 0); + + // ================ ascii ================ + execute("INSERT INTO %s (k, asciival) VALUES (?, ?)", 0, "ascii text"); + assertRows(execute("SELECT k, toJson(asciival) FROM %s WHERE k = ?", 0), row(0, "\"ascii text\"")); + + execute("INSERT INTO %s (k, asciival) VALUES (?, ?)", 0, ""); + assertRows(execute("SELECT k, toJson(asciival) FROM %s WHERE k = ?", 0), row(0, "\"\"")); + + // ================ bigint ================ + execute("INSERT INTO %s (k, bigintval) VALUES (?, ?)", 0, 123123123123L); + assertRows(execute("SELECT k, toJson(bigintval) FROM %s WHERE k = ?", 0), row(0, "123123123123")); + + execute("INSERT INTO %s (k, bigintval) VALUES (?, ?)", 0, 0L); + assertRows(execute("SELECT k, toJson(bigintval) FROM %s WHERE k = ?", 0), row(0, "0")); + + execute("INSERT INTO %s (k, bigintval) VALUES (?, ?)", 0, -123123123123L); + assertRows(execute("SELECT k, toJson(bigintval) FROM %s WHERE k = ?", 0), row(0, "-123123123123")); + + // ================ blob ================ + execute("INSERT INTO %s (k, blobval) VALUES (?, ?)", 0, ByteBufferUtil.bytes(1)); + assertRows(execute("SELECT k, toJson(blobval) FROM %s WHERE k = ?", 0), row(0, "\"0x00000001\"")); + + execute("INSERT INTO %s (k, blobval) VALUES (?, ?)", 0, ByteBufferUtil.EMPTY_BYTE_BUFFER); + assertRows(execute("SELECT k, toJson(blobval) FROM %s WHERE k = ?", 0), row(0, "\"0x\"")); + + // ================ boolean ================ + execute("INSERT INTO %s (k, booleanval) VALUES (?, ?)", 0, true); + assertRows(execute("SELECT k, toJson(booleanval) FROM %s WHERE k = ?", 0), row(0, "true")); + + execute("INSERT INTO %s (k, booleanval) VALUES (?, ?)", 0, false); + assertRows(execute("SELECT k, toJson(booleanval) FROM %s WHERE k = ?", 0), row(0, "false")); + + // ================ date ================ + execute("INSERT INTO %s (k, dateval) VALUES (?, ?)", 0, SimpleDateSerializer.dateStringToDays("1987-03-23")); + assertRows(execute("SELECT k, toJson(dateval) FROM %s WHERE k = ?", 0), row(0, "\"1987-03-23\"")); + + // ================ decimal ================ + execute("INSERT INTO %s (k, decimalval) VALUES (?, ?)", 0, new BigDecimal("123123.123123")); + assertRows(execute("SELECT k, toJson(decimalval) FROM %s WHERE k = ?", 0), row(0, "123123.123123")); + + execute("INSERT INTO %s (k, decimalval) VALUES (?, ?)", 0, new BigDecimal("-1.23E-12")); + assertRows(execute("SELECT k, toJson(decimalval) FROM %s WHERE k = ?", 0), row(0, "-1.23E-12")); + + // ================ double ================ + execute("INSERT INTO %s (k, doubleval) VALUES (?, ?)", 0, 123123.123123d); + assertRows(execute("SELECT k, toJson(doubleval) FROM %s WHERE k = ?", 0), row(0, "123123.123123")); + + execute("INSERT INTO %s (k, doubleval) VALUES (?, ?)", 0, 123123d); + assertRows(execute("SELECT k, toJson(doubleval) FROM %s WHERE k = ?", 0), row(0, "123123.0")); + + // ================ float ================ + execute("INSERT INTO %s (k, floatval) VALUES (?, ?)", 0, 123.123f); + assertRows(execute("SELECT k, toJson(floatval) FROM %s WHERE k = ?", 0), row(0, "123.123")); + + execute("INSERT INTO %s (k, floatval) VALUES (?, ?)", 0, 123123f); + assertRows(execute("SELECT k, toJson(floatval) FROM %s WHERE k = ?", 0), row(0, "123123.0")); + + // ================ inet ================ + execute("INSERT INTO %s (k, inetval) VALUES (?, ?)", 0, InetAddress.getByName("127.0.0.1")); + assertRows(execute("SELECT k, toJson(inetval) FROM %s WHERE k = ?", 0), row(0, "\"127.0.0.1\"")); + + execute("INSERT INTO %s (k, inetval) VALUES (?, ?)", 0, InetAddress.getByName("::1")); + assertRows(execute("SELECT k, toJson(inetval) FROM %s WHERE k = ?", 0), row(0, "\"0:0:0:0:0:0:0:1\"")); + + // ================ int ================ + execute("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 123123); + assertRows(execute("SELECT k, toJson(intval) FROM %s WHERE k = ?", 0), row(0, "123123")); + + execute("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, 0); + assertRows(execute("SELECT k, toJson(intval) FROM %s WHERE k = ?", 0), row(0, "0")); + + execute("INSERT INTO %s (k, intval) VALUES (?, ?)", 0, -123123); + assertRows(execute("SELECT k, toJson(intval) FROM %s WHERE k = ?", 0), row(0, "-123123")); + + // ================ text (varchar) ================ + execute("INSERT INTO %s (k, textval) VALUES (?, ?)", 0, ""); + assertRows(execute("SELECT k, toJson(textval) FROM %s WHERE k = ?", 0), row(0, "\"\"")); + + execute("INSERT INTO %s (k, textval) VALUES (?, ?)", 0, "abcd"); + assertRows(execute("SELECT k, toJson(textval) FROM %s WHERE k = ?", 0), row(0, "\"abcd\"")); + + execute("INSERT INTO %s (k, textval) VALUES (?, ?)", 0, "\u8422"); + assertRows(execute("SELECT k, toJson(textval) FROM %s WHERE k = ?", 0), row(0, "\"\u8422\"")); + + execute("INSERT INTO %s (k, textval) VALUES (?, ?)", 0, "\u0000"); + assertRows(execute("SELECT k, toJson(textval) FROM %s WHERE k = ?", 0), row(0, "\"\\u0000\"")); + + // ================ timestamp ================ + execute("INSERT INTO %s (k, timeval) VALUES (?, ?)", 0, 123L); + assertRows(execute("SELECT k, toJson(timeval) FROM %s WHERE k = ?", 0), row(0, "\"00:00:00.000000123\"")); + + // ================ timestamp ================ + execute("INSERT INTO %s (k, timestampval) VALUES (?, ?)", 0, new SimpleDateFormat("y-M-d").parse("2014-01-01")); + assertRows(execute("SELECT k, toJson(timestampval) FROM %s WHERE k = ?", 0), row(0, "\"2014-01-01 00:00:00.000\"")); + + // ================ timeuuid ================ + execute("INSERT INTO %s (k, timeuuidval) VALUES (?, ?)", 0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")); + assertRows(execute("SELECT k, toJson(timeuuidval) FROM %s WHERE k = ?", 0), row(0, "\"6bddc89a-5644-11e4-97fc-56847afe9799\"")); + + // ================ uuidval ================ + execute("INSERT INTO %s (k, uuidval) VALUES (?, ?)", 0, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")); + assertRows(execute("SELECT k, toJson(uuidval) FROM %s WHERE k = ?", 0), row(0, "\"6bddc89a-5644-11e4-97fc-56847afe9799\"")); + + // ================ varint ================ + execute("INSERT INTO %s (k, varintval) VALUES (?, ?)", 0, new BigInteger("123123123123123123123")); + assertRows(execute("SELECT k, toJson(varintval) FROM %s WHERE k = ?", 0), row(0, "123123123123123123123")); + + // ================ lists ================ + execute("INSERT INTO %s (k, listval) VALUES (?, ?)", 0, list(1, 2, 3)); + assertRows(execute("SELECT k, toJson(listval) FROM %s WHERE k = ?", 0), row(0, "[1, 2, 3]")); + + execute("INSERT INTO %s (k, listval) VALUES (?, ?)", 0, list()); + assertRows(execute("SELECT k, toJson(listval) FROM %s WHERE k = ?", 0), row(0, "null")); + + // frozen + execute("INSERT INTO %s (k, frozenlistval) VALUES (?, ?)", 0, list(1, 2, 3)); + assertRows(execute("SELECT k, toJson(frozenlistval) FROM %s WHERE k = ?", 0), row(0, "[1, 2, 3]")); + + // ================ sets ================ + execute("INSERT INTO %s (k, setval) VALUES (?, ?)", + 0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))); + assertRows(execute("SELECT k, toJson(setval) FROM %s WHERE k = ?", 0), + row(0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]") + ); + + execute("INSERT INTO %s (k, setval) VALUES (?, ?)", 0, set()); + assertRows(execute("SELECT k, toJson(setval) FROM %s WHERE k = ?", 0), row(0, "null")); + + // frozen + execute("INSERT INTO %s (k, frozensetval) VALUES (?, ?)", + 0, set(UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9798"), (UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")))); + assertRows(execute("SELECT k, toJson(frozensetval) FROM %s WHERE k = ?", 0), + row(0, "[\"6bddc89a-5644-11e4-97fc-56847afe9798\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]") + ); + + // ================ maps ================ + execute("INSERT INTO %s (k, mapval) VALUES (?, ?)", 0, map("a", 1, "b", 2)); + assertRows(execute("SELECT k, toJson(mapval) FROM %s WHERE k = ?", 0), row(0, "{\"a\": 1, \"b\": 2}")); + + execute("INSERT INTO %s (k, mapval) VALUES (?, ?)", 0, map()); + assertRows(execute("SELECT k, toJson(mapval) FROM %s WHERE k = ?", 0), row(0, "null")); + + // frozen + execute("INSERT INTO %s (k, frozenmapval) VALUES (?, ?)", 0, map("a", 1, "b", 2)); + assertRows(execute("SELECT k, toJson(frozenmapval) FROM %s WHERE k = ?", 0), row(0, "{\"a\": 1, \"b\": 2}")); + + // ================ tuples ================ + execute("INSERT INTO %s (k, tupleval) VALUES (?, ?)", 0, tuple(1, "foobar", UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"))); + assertRows(execute("SELECT k, toJson(tupleval) FROM %s WHERE k = ?", 0), + row(0, "[1, \"foobar\", \"6bddc89a-5644-11e4-97fc-56847afe9799\"]") + ); + + execute("INSERT INTO %s (k, tupleval) VALUES (?, ?)", 0, tuple(1, "foobar", null)); + assertRows(execute("SELECT k, toJson(tupleval) FROM %s WHERE k = ?", 0), + row(0, "[1, \"foobar\", null]") + ); + + // ================ UDTs ================ + execute("INSERT INTO %s (k, udtval) VALUES (?, {a: ?, b: ?, c: ?})", 0, 1, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799"), set("foo", "bar")); + assertRows(execute("SELECT k, toJson(udtval) FROM %s WHERE k = ?", 0), + row(0, "{\"a\": 1, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"c\": [\"bar\", \"foo\"]}") + ); + + execute("INSERT INTO %s (k, udtval) VALUES (?, {a: ?, b: ?})", 0, 1, UUID.fromString("6bddc89a-5644-11e4-97fc-56847afe9799")); + assertRows(execute("SELECT k, toJson(udtval) FROM %s WHERE k = ?", 0), + row(0, "{\"a\": 1, \"b\": \"6bddc89a-5644-11e4-97fc-56847afe9799\", \"c\": null}") + ); + } + + @Test + public void testSelectJsonSyntax() throws Throwable + { + // tests SELECT JSON statements + createTable("CREATE TABLE %s (k int primary key, v int)"); + execute("INSERT INTO %s (k, v) VALUES (0, 0)"); + execute("INSERT INTO %s (k, v) VALUES (1, 1)"); + + assertRows(execute("SELECT JSON * FROM %s"), + row("{\"k\": 0, \"v\": 0}"), + row("{\"k\": 1, \"v\": 1}") + ); + + assertRows(execute("SELECT JSON k, v FROM %s"), + row("{\"k\": 0, \"v\": 0}"), + row("{\"k\": 1, \"v\": 1}") + ); + + assertRows(execute("SELECT JSON v, k FROM %s"), + row("{\"v\": 0, \"k\": 0}"), + row("{\"v\": 1, \"k\": 1}") + ); + + assertRows(execute("SELECT JSON v as foo, k as bar FROM %s"), + row("{\"foo\": 0, \"bar\": 0}"), + row("{\"foo\": 1, \"bar\": 1}") + ); + + assertRows(execute("SELECT JSON ttl(v), k FROM %s"), + row("{\"ttl(v)\": null, \"k\": 0}"), + row("{\"ttl(v)\": null, \"k\": 1}") + ); + + assertRows(execute("SELECT JSON ttl(v) as foo, k FROM %s"), + row("{\"foo\": null, \"k\": 0}"), + row("{\"foo\": null, \"k\": 1}") + ); + + assertRows(execute("SELECT JSON count(*) FROM %s"), + row("{\"count\": 2}") + ); + + assertRows(execute("SELECT JSON count(*) as foo FROM %s"), + row("{\"foo\": 2}") + ); + + assertRows(execute("SELECT JSON toJson(blobAsInt(intAsBlob(v))) FROM %s LIMIT 1"), + row("{\"system.tojson(system.blobasint(system.intasblob(v)))\": \"0\"}") + ); + } + + @Test + public void testInsertJsonSyntax() throws Throwable + { + createTable("CREATE TABLE %s (k int primary key, v int)"); + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"v\": 0}"); + assertRows(execute("SELECT * FROM %s"), + row(0, 0) + ); + + // without specifying column names + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"v\": 0}"); + assertRows(execute("SELECT * FROM %s"), + row(0, 0) + ); + + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"v\": null}"); + assertRows(execute("SELECT * FROM %s"), + row(0, null) + ); + + execute("INSERT INTO %s JSON ?", "{\"v\": 1, \"k\": 0}"); + assertRows(execute("SELECT * FROM %s"), + row(0, 1) + ); + + execute("INSERT INTO %s JSON ?", "{\"k\": 0}"); + assertRows(execute("SELECT * FROM %s"), + row(0, null) + ); + + if (USE_PREPARED_VALUES) + assertInvalidMessage("Got null for INSERT JSON values", "INSERT INTO %s JSON ?", new Object[]{null}); + + assertInvalidMessage("Got null for INSERT JSON values", "INSERT INTO %s JSON ?", "null"); + assertInvalidMessage("Could not decode JSON string as a map", "INSERT INTO %s JSON ?", "\"notamap\""); + assertInvalidMessage("Could not decode JSON string as a map", "INSERT INTO %s JSON ?", "12.34"); + assertInvalidMessage("JSON values map contains unrecognized column", + "INSERT INTO %s JSON ?", + "{\"k\": 0, \"v\": 0, \"zzz\": 0}"); + + assertInvalidMessage("Unable to make int from", + "INSERT INTO %s JSON ?", + "{\"k\": 0, \"v\": \"notanint\"}"); + } + + @Test + public void testCaseSensitivity() throws Throwable + { + createTable("CREATE TABLE %s (k int primary key, \"Foo\" int)"); + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"\\\"Foo\\\"\": 0}"); + execute("INSERT INTO %s JSON ?", "{\"K\": 0, \"\\\"Foo\\\"\": 0}"); + execute("INSERT INTO %s JSON ?", "{\"\\\"k\\\"\": 0, \"\\\"Foo\\\"\": 0}"); + + // results should preserve and quote case-sensitive identifiers + assertRows(execute("SELECT JSON * FROM %s"), row("{\"k\": 0, \"\\\"Foo\\\"\": 0}")); + assertRows(execute("SELECT JSON k, \"Foo\" as foo FROM %s"), row("{\"k\": 0, \"foo\": 0}")); + assertRows(execute("SELECT JSON k, \"Foo\" as \"Bar\" FROM %s"), row("{\"k\": 0, \"\\\"Bar\\\"\": 0}")); + + assertInvalid("INSERT INTO %s JSON ?", "{\"k\": 0, \"foo\": 0}"); + assertInvalid("INSERT INTO %s JSON ?", "{\"k\": 0, \"\\\"foo\\\"\": 0}"); + + // user-defined types also need to handle case-sensitivity + String typeName = createType("CREATE TYPE %s (a int, \"Foo\" int)"); + createTable("CREATE TABLE %s (k int primary key, v frozen<" + typeName + ">)"); + + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"v\": {\"a\": 0, \"\\\"Foo\\\"\": 0}}"); + assertRows(execute("SELECT JSON k, v FROM %s"), row("{\"k\": 0, \"v\": {\"a\": 0, \"\\\"Foo\\\"\": 0}}")); + + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"v\": {\"A\": 0, \"\\\"Foo\\\"\": 0}}"); + assertRows(execute("SELECT JSON k, v FROM %s"), row("{\"k\": 0, \"v\": {\"a\": 0, \"\\\"Foo\\\"\": 0}}")); + } + + @Test + public void testInsertJsonSyntaxWithCollections() throws Throwable + { + createTable("CREATE TABLE %s (" + + "k int PRIMARY KEY, " + + "m map, " + + "mf frozen>, " + + "s set, " + + "sf frozen>, " + + "l list, " + + "lf frozen>)"); + + // map + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"m\": {\"a\": true, \"b\": false}}"); + assertRows(execute("SELECT k, m FROM %s"), row(0, map("a", true, "b", false))); + + // frozen map + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"mf\": {\"a\": true, \"b\": false}}"); + assertRows(execute("SELECT k, mf FROM %s"), row(0, map("a", true, "b", false))); + + // set + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"s\": [3, 1, 2]}"); + assertRows(execute("SELECT k, s FROM %s"), row(0, set(1, 2, 3))); + + // frozen set + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"sf\": [3, 1, 2]}"); + assertRows(execute("SELECT k, sf FROM %s"), row(0, set(1, 2, 3))); + + // list + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"l\": [1, 2, 3]}"); + assertRows(execute("SELECT k, l FROM %s"), row(0, list(1, 2, 3))); + + // frozen list + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"lf\": [1, 2, 3]}"); + assertRows(execute("SELECT k, lf FROM %s"), row(0, list(1, 2, 3))); + } + + @Test + public void testInsertJsonSyntaxWithTuplesAndUDTs() throws Throwable + { + String typeName = createType("CREATE TYPE %s (a int, b frozen>, c tuple)"); + createTable("CREATE TABLE %s (" + + "k int PRIMARY KEY, " + + "a frozen<" + typeName + ">, " + + "b tuple)"); + + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"a\": {\"a\": 0, \"b\": [1, 2, 3], \"c\": [0, 1]}, \"b\": [0, true]}"); + assertRows(execute("SELECT k, a.a, a.b, a.c, b FROM %s"), row(0, 0, set(1, 2, 3), tuple(0, 1), tuple(0, true))); + + execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"a\": {\"a\": 0, \"b\": [1, 2, 3], \"c\": null}, \"b\": null}"); + assertRows(execute("SELECT k, a.a, a.b, a.c, b FROM %s"), row(0, 0, set(1, 2, 3), null, null)); + } +}