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 71EE518182 for ; Wed, 24 Jun 2015 16:15:01 +0000 (UTC) Received: (qmail 9358 invoked by uid 500); 24 Jun 2015 16:14:56 -0000 Delivered-To: apmail-cassandra-commits-archive@cassandra.apache.org Received: (qmail 9264 invoked by uid 500); 24 Jun 2015 16:14:56 -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 7858 invoked by uid 99); 24 Jun 2015 16:14:54 -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, 24 Jun 2015 16:14:54 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id D3BC4E3650; Wed, 24 Jun 2015 16:14:54 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jmckenzie@apache.org To: commits@cassandra.apache.org Date: Wed, 24 Jun 2015 16:15:13 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [20/32] cassandra git commit: 2.2 commit for CASSANDRA-9160 http://git-wip-us.apache.org/repos/asf/cassandra/blob/01115f72/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java new file mode 100644 index 0000000..7f8fa0b --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java @@ -0,0 +1,958 @@ +/* + * 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.validation.entities; + +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.Json; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.dht.ByteOrderedPartitioner; +import org.apache.cassandra.serializers.SimpleDateSerializer; +import org.apache.cassandra.serializers.TimeSerializer; +import org.apache.cassandra.utils.ByteBufferUtil; + +import org.junit.BeforeClass; +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 +{ + @BeforeClass + public static void setUp() + { + DatabaseDescriptor.setPartitioner(ByteOrderedPartitioner.instance); + } + + @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) CALLED ON NULL INPUT RETURNS text LANGUAGE java AS $$ return a.toString(); $$"); + createFunctionOverload(func1, "int", "CREATE FUNCTION %s (a text) CALLED ON NULL INPUT 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 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, " + + "bigintmap map, " + + "varintmap map, " + + "booleanmap map, " + + "floatmap map, " + + "doublemap map, " + + "decimalmap map, " + + "tuplemap map>, boolean>, " + + "udtmap map, boolean>, " + + "setmap map>, boolean>, " + + "listmap map>, boolean>, " + + "textsetmap map>, boolean>, " + + "nestedsetmap map, text>>, boolean>, " + + "frozensetmap frozen, 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 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 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 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 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, 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 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>, 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)); + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/01115f72/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexOnMapEntriesTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexOnMapEntriesTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexOnMapEntriesTest.java new file mode 100644 index 0000000..fb0d027 --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexOnMapEntriesTest.java @@ -0,0 +1,348 @@ +/* + * 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.validation.entities; + +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.dht.ByteOrderedPartitioner; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.commons.lang3.StringUtils; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class SecondaryIndexOnMapEntriesTest extends CQLTester +{ + @BeforeClass + public static void setUp() + { + DatabaseDescriptor.setPartitioner(ByteOrderedPartitioner.instance); + } + + @Test + public void testShouldNotCreateIndexOnFrozenMaps() throws Throwable + { + createTable("CREATE TABLE %s (k TEXT PRIMARY KEY, v FROZEN>)"); + assertIndexInvalidForColumn("v"); + } + + @Test + public void testShouldNotCreateIndexOnNonMapTypes() throws Throwable + { + createTable("CREATE TABLE %s (k TEXT PRIMARY KEY, i INT, t TEXT, b BLOB, s SET, l LIST, tu TUPLE)"); + assertIndexInvalidForColumn("i"); + assertIndexInvalidForColumn("t"); + assertIndexInvalidForColumn("b"); + assertIndexInvalidForColumn("s"); + assertIndexInvalidForColumn("l"); + assertIndexInvalidForColumn("tu"); + } + + @Test + public void testShouldValidateMapKeyAndValueTypes() throws Throwable + { + createSimpleTableAndIndex(); + + String query = "SELECT * FROM %s WHERE v[?] = ?"; + Object validKey = "valid key"; + Object invalidKey = 31415; + Object validValue = 31415; + Object invalidValue = "invalid value"; + assertInvalid(query, invalidKey, invalidValue); + assertInvalid(query, invalidKey, validValue); + assertInvalid(query, validKey, invalidValue); + assertReturnsNoRows(query, validKey, validValue); + } + + @Test + public void testShouldFindRowsMatchingSingleEqualityRestriction() throws Throwable + { + createSimpleTableAndIndex(); + Object[] foo = insertIntoSimpleTable("foo", map("a", 1, + "c", 3)); + Object[] bar = insertIntoSimpleTable("bar", map("a", 1, + "b", 2)); + Object[] baz = insertIntoSimpleTable("baz", map("b", 2, + "c", 5, + "d", 4)); + Object[] qux = insertIntoSimpleTable("qux", map("b", 2, + "d", 4)); + + assertRowsForConditions(entry("a", 1), bar, foo); + assertRowsForConditions(entry("b", 2), bar, baz, qux); + assertRowsForConditions(entry("c", 3), foo); + assertRowsForConditions(entry("c", 5), baz); + assertRowsForConditions(entry("d", 4), baz, qux); + } + + @Test + public void testRequireFilteringDirectiveIfMultipleRestrictionsSpecified() throws Throwable + { + createSimpleTableAndIndex(); + String baseQuery = "SELECT * FROM %s WHERE v['foo'] = 31415 AND v['baz'] = 31416"; + assertInvalid(baseQuery); + assertReturnsNoRows(baseQuery + " ALLOW FILTERING"); + } + + @Test + public void testShouldFindRowsMatchingMultipleEqualityRestrictions() throws Throwable + { + createSimpleTableAndIndex(); + + Object[] foo = insertIntoSimpleTable("foo", map("k1", 1)); + Object[] bar = insertIntoSimpleTable("bar", map("k1", 1, + "k2", 2)); + Object[] baz = insertIntoSimpleTable("baz", map("k2", 2, + "k3", 3)); + Object[] qux = insertIntoSimpleTable("qux", map("k2", 2, + "k3", 3, + "k4", 4)); + + assertRowsForConditions(entry("k1", 1), + bar, foo); + assertRowsForConditions(entry("k1", 1).entry("k2", 2), + bar); + assertNoRowsForConditions(entry("k1", 1).entry("k2", 2).entry("k3", 3)); + assertRowsForConditions(entry("k2", 2).entry("k3", 3), + baz, qux); + assertRowsForConditions(entry("k2", 2).entry("k3", 3).entry("k4", 4), + qux); + assertRowsForConditions(entry("k3", 3).entry("k4", 4), + qux); + assertNoRowsForConditions(entry("k3", 3).entry("k4", 4).entry("k5", 5)); + } + + @Test + public void testShouldFindRowsMatchingEqualityAndContainsRestrictions() throws Throwable + { + createSimpleTableAndIndex(); + + Object[] foo = insertIntoSimpleTable("foo", map("common", 31415, + "k1", 1, + "k2", 2, + "k3", 3)); + Object[] bar = insertIntoSimpleTable("bar", map("common", 31415, + "k3", 3, + "k4", 4, + "k5", 5)); + Object[] baz = insertIntoSimpleTable("baz", map("common", 31415, + "k5", 5, + "k6", 6, + "k7", 7)); + + assertRowsForConditions(entry("common", 31415), + bar, baz, foo); + assertRowsForConditions(entry("common", 31415).key("k1"), + foo); + assertRowsForConditions(entry("common", 31415).key("k2"), + foo); + assertRowsForConditions(entry("common", 31415).key("k3"), + bar, foo); + assertRowsForConditions(entry("common", 31415).key("k3").value(2), + foo); + assertRowsForConditions(entry("common", 31415).key("k3").value(3), + bar, foo); + assertRowsForConditions(entry("common", 31415).key("k3").value(4), + bar); + assertRowsForConditions(entry("common", 31415).key("k3").key("k5"), + bar); + assertRowsForConditions(entry("common", 31415).key("k5"), + bar, baz); + assertRowsForConditions(entry("common", 31415).key("k5").value(4), + bar); + assertRowsForConditions(entry("common", 31415).key("k5").value(5), + bar, baz); + assertRowsForConditions(entry("common", 31415).key("k5").value(6), + baz); + assertNoRowsForConditions(entry("common", 31415).key("k5").value(8)); + } + + @Test + public void testShouldNotAcceptUnsupportedRelationsOnEntries() throws Throwable + { + createSimpleTableAndIndex(); + assertInvalidRelation("< 31415"); + assertInvalidRelation("<= 31415"); + assertInvalidRelation("> 31415"); + assertInvalidRelation(">= 31415"); + assertInvalidRelation("IN (31415, 31416, 31417)"); + assertInvalidRelation("CONTAINS 31415"); + assertInvalidRelation("CONTAINS KEY 'foo'"); + } + + @Test + public void testShouldRecognizeAlteredOrDeletedMapEntries() throws Throwable + { + createSimpleTableAndIndex(); + Object[] foo = insertIntoSimpleTable("foo", map("common", 31415, + "target", 8192)); + Object[] bar = insertIntoSimpleTable("bar", map("common", 31415, + "target", 8192)); + Object[] baz = insertIntoSimpleTable("baz", map("common", 31415, + "target", 8192)); + + assertRowsForConditions(entry("target", 8192), + bar, baz, foo); + baz = updateMapInSimpleTable(baz, "target", 4096); + assertRowsForConditions(entry("target", 8192), + bar, foo); + bar = updateMapInSimpleTable(bar, "target", null); + assertRowsForConditions(entry("target", 8192), + foo); + execute("DELETE FROM %s WHERE k = 'foo'"); + assertNoRowsForConditions(entry("target", 8192)); + assertRowsForConditions(entry("common", 31415), + bar, baz); + assertRowsForConditions(entry("target", 4096), + baz); + } + + @Test + public void testShouldRejectQueriesForNullEntries() throws Throwable + { + createSimpleTableAndIndex(); + assertInvalid("SELECT * FROM %s WHERE v['somekey'] = null"); + } + + @Test + public void testShouldTreatQueriesAgainstFrozenMapIndexesAsInvalid() throws Throwable + { + createTable("CREATE TABLE %s (k TEXT PRIMARY KEY, v FROZEN>)"); + createIndex("CREATE INDEX ON %s(FULL(V))"); + + try + { + execute("SELECT * FROM %s WHERE v['somekey'] = 'somevalue'"); + fail("Expected index query to fail"); + } + catch (InvalidRequestException e) + { + String expectedMessage = "Map-entry equality predicates on frozen map column v are not supported"; + assertTrue("Expected error message to contain '" + expectedMessage + "' but got '" + + e.getMessage() + "'", e.getMessage().contains(expectedMessage)); + } + } + + private void assertIndexInvalidForColumn(String colname) throws Throwable + { + String query = String.format("CREATE INDEX ON %%s(ENTRIES(%s))", colname); + assertInvalid(query); + } + + private void assertReturnsNoRows(String query, Object... params) throws Throwable + { + assertRows(execute(query, params)); + } + + private void createSimpleTableAndIndex() throws Throwable + { + createTable("CREATE TABLE %s (k TEXT PRIMARY KEY, v MAP)"); + createIndex("CREATE INDEX ON %s(ENTRIES(v))"); + } + + private Object[] insertIntoSimpleTable(String key, Object value) throws Throwable + { + String query = "INSERT INTO %s (k, v) VALUES (?, ?)"; + execute(query, key, value); + return row(key, value); + } + + private void assertRowsForConditions(IndexWhereClause whereClause, Object[]... rows) throws Throwable + { + assertRows(execute("SELECT * FROM %s WHERE " + whereClause.text(), whereClause.params()), rows); + } + + private void assertNoRowsForConditions(IndexWhereClause whereClause) throws Throwable + { + assertRowsForConditions(whereClause); + } + + private void assertInvalidRelation(String rel) throws Throwable + { + String query = "SELECT * FROM %s WHERE v " + rel; + assertInvalid(query); + } + + private Object[] updateMapInSimpleTable(Object[] row, String mapKey, Integer mapValue) throws Throwable + { + execute("UPDATE %s SET v[?] = ? WHERE k = ?", mapKey, mapValue, row[0]); + UntypedResultSet rawResults = execute("SELECT * FROM %s WHERE k = ?", row[0]); + Map value = (Map)row[1]; + if (mapValue == null) + { + value.remove(mapKey); + } + else + { + value.put(mapKey, mapValue); + } + return row; + } + + private IndexWhereClause entry(Object key, Object value) + { + return (new IndexWhereClause()).entry(key, value); + } + + private static final class IndexWhereClause + { + private final List preds = new ArrayList<>(); + private final List params = new ArrayList<>(); + + public IndexWhereClause entry(Object key, Object value) + { + preds.add("v[?] = ?"); + params.add(key); + params.add(value); + return this; + } + + public IndexWhereClause key(Object key) + { + preds.add("v CONTAINS KEY ?"); + params.add(key); + return this; + } + + public IndexWhereClause value(Object value) + { + preds.add("v CONTAINS ?"); + params.add(value); + return this; + } + + public String text() + { + if (preds.size() == 1) + return preds.get(0); + return StringUtils.join(preds, " AND ") + " ALLOW FILTERING"; + } + + public Object[] params() + { + return params.toArray(); + } + } +}