blob: 7ad41e30847004c5eee69be235c7c2bce77844ce [file] [log] [blame]
/*
* 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.operations;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.SyntaxException;
/* InsertUpdateIfConditionCollectionsTest class has been split into multiple ones because of timeout issues (CASSANDRA-16670)
* Any changes here check if they apply to the other classes
* - InsertUpdateIfConditionStaticsTest
* - InsertUpdateIfConditionCollectionsTest
* - InsertUpdateIfConditionTest
*/
@RunWith(Parameterized.class)
public class InsertUpdateIfConditionCollectionsTest extends CQLTester
{
@Parameterized.Parameter(0)
public String clusterMinVersion;
@Parameterized.Parameter(1)
public Runnable assertion;
@Parameterized.Parameters(name = "{index}: clusterMinVersion={0}")
public static Collection<Object[]> data()
{
return InsertUpdateIfConditionTest.data();
}
@BeforeClass
public static void beforeClass()
{
InsertUpdateIfConditionTest.beforeClass();
}
@Before
public void before()
{
InsertUpdateIfConditionTest.beforeSetup(clusterMinVersion, assertion);
}
@AfterClass
public static void afterClass()
{
InsertUpdateIfConditionTest.afterClass();
}
/**
* Migrated from cql_tests.py:TestCQL.bug_6069_test()
*/
@Test
public void testInsertSetIfNotExists() throws Throwable
{
createTable("CREATE TABLE %s (k int PRIMARY KEY, s set<int>)");
assertRows(execute("INSERT INTO %s (k, s) VALUES (0, {1, 2, 3}) IF NOT EXISTS"),
row(true));
assertRows(execute("SELECT * FROM %s "), row(0, set(1, 2, 3)));
}
@Test
public void testWholeUDT() throws Throwable
{
String typename = createType("CREATE TYPE %s (a int, b text)");
String myType = KEYSPACE + '.' + typename;
for (boolean frozen : new boolean[] {false, true})
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, v %s)",
frozen
? "frozen<" + myType + ">"
: myType));
Object v = userType("a", 0, "b", "abc");
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
checkAppliesUDT("v = {a: 0, b: 'abc'}", v);
checkAppliesUDT("v != null", v);
checkAppliesUDT("v != {a: 1, b: 'abc'}", v);
checkAppliesUDT("v != {a: 0, b: 'def'}", v);
checkAppliesUDT("v > {a: -1, b: 'abc'}", v);
checkAppliesUDT("v > {a: 0, b: 'aaa'}", v);
checkAppliesUDT("v > {a: 0}", v);
checkAppliesUDT("v >= {a: 0, b: 'aaa'}", v);
checkAppliesUDT("v >= {a: 0, b: 'abc'}", v);
checkAppliesUDT("v < {a: 0, b: 'zzz'}", v);
checkAppliesUDT("v < {a: 1, b: 'abc'}", v);
checkAppliesUDT("v < {a: 1}", v);
checkAppliesUDT("v <= {a: 0, b: 'zzz'}", v);
checkAppliesUDT("v <= {a: 0, b: 'abc'}", v);
checkAppliesUDT("v IN (null, {a: 0, b: 'abc'}, {a: 1})", v);
// multiple conditions
checkAppliesUDT("v > {a: -1, b: 'abc'} AND v > {a: 0}", v);
checkAppliesUDT("v != null AND v IN ({a: 0, b: 'abc'})", v);
// should not apply
checkDoesNotApplyUDT("v = {a: 0, b: 'def'}", v);
checkDoesNotApplyUDT("v = {a: 1, b: 'abc'}", v);
checkDoesNotApplyUDT("v = null", v);
checkDoesNotApplyUDT("v != {a: 0, b: 'abc'}", v);
checkDoesNotApplyUDT("v > {a: 1, b: 'abc'}", v);
checkDoesNotApplyUDT("v > {a: 0, b: 'zzz'}", v);
checkDoesNotApplyUDT("v >= {a: 1, b: 'abc'}", v);
checkDoesNotApplyUDT("v >= {a: 0, b: 'zzz'}", v);
checkDoesNotApplyUDT("v < {a: -1, b: 'abc'}", v);
checkDoesNotApplyUDT("v < {a: 0, b: 'aaa'}", v);
checkDoesNotApplyUDT("v <= {a: -1, b: 'abc'}", v);
checkDoesNotApplyUDT("v <= {a: 0, b: 'aaa'}", v);
checkDoesNotApplyUDT("v IN ({a: 0}, {b: 'abc'}, {a: 0, b: 'def'}, null)", v);
checkDoesNotApplyUDT("v IN ()", v);
// multiple conditions
checkDoesNotApplyUDT("v IN () AND v IN ({a: 0, b: 'abc'})", v);
checkDoesNotApplyUDT("v > {a: 0, b: 'aaa'} AND v < {a: 0, b: 'aaa'}", v);
// invalid conditions
checkInvalidUDT("v = {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
checkInvalidUDT("v = {foo: 'foo'}", v, InvalidRequestException.class);
checkInvalidUDT("v < {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
checkInvalidUDT("v < null", v, InvalidRequestException.class);
checkInvalidUDT("v <= {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
checkInvalidUDT("v <= null", v, InvalidRequestException.class);
checkInvalidUDT("v > {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
checkInvalidUDT("v > null", v, InvalidRequestException.class);
checkInvalidUDT("v >= {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
checkInvalidUDT("v >= null", v, InvalidRequestException.class);
checkInvalidUDT("v IN null", v, SyntaxException.class);
checkInvalidUDT("v IN 367", v, SyntaxException.class);
checkInvalidUDT("v CONTAINS KEY 123", v, SyntaxException.class);
checkInvalidUDT("v CONTAINS 'bar'", v, SyntaxException.class);
/////////////////// null suffix on stored udt ////////////////////
v = userType("a", 0, "b", null);
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
checkAppliesUDT("v = {a: 0}", v);
checkAppliesUDT("v = {a: 0, b: null}", v);
checkAppliesUDT("v != null", v);
checkAppliesUDT("v != {a: 1, b: null}", v);
checkAppliesUDT("v != {a: 1}", v);
checkAppliesUDT("v != {a: 0, b: 'def'}", v);
checkAppliesUDT("v > {a: -1, b: 'abc'}", v);
checkAppliesUDT("v > {a: -1}", v);
checkAppliesUDT("v >= {a: 0}", v);
checkAppliesUDT("v >= {a: -1, b: 'abc'}", v);
checkAppliesUDT("v < {a: 0, b: 'zzz'}", v);
checkAppliesUDT("v < {a: 1, b: 'abc'}", v);
checkAppliesUDT("v < {a: 1}", v);
checkAppliesUDT("v <= {a: 0, b: 'zzz'}", v);
checkAppliesUDT("v <= {a: 0}", v);
checkAppliesUDT("v IN (null, {a: 0, b: 'abc'}, {a: 0})", v);
// multiple conditions
checkAppliesUDT("v > {a: -1, b: 'abc'} AND v >= {a: 0}", v);
checkAppliesUDT("v != null AND v IN ({a: 0}, {a: 0, b: null})", v);
// should not apply
checkDoesNotApplyUDT("v = {a: 0, b: 'def'}", v);
checkDoesNotApplyUDT("v = {a: 1}", v);
checkDoesNotApplyUDT("v = {b: 'abc'}", v);
checkDoesNotApplyUDT("v = null", v);
checkDoesNotApplyUDT("v != {a: 0}", v);
checkDoesNotApplyUDT("v != {a: 0, b: null}", v);
checkDoesNotApplyUDT("v > {a: 1, b: 'abc'}", v);
checkDoesNotApplyUDT("v > {a: 0}", v);
checkDoesNotApplyUDT("v >= {a: 1, b: 'abc'}", v);
checkDoesNotApplyUDT("v >= {a: 1}", v);
checkDoesNotApplyUDT("v < {a: -1, b: 'abc'}", v);
checkDoesNotApplyUDT("v < {a: -1}", v);
checkDoesNotApplyUDT("v < {a: 0}", v);
checkDoesNotApplyUDT("v <= {a: -1, b: 'abc'}", v);
checkDoesNotApplyUDT("v <= {a: -1}", v);
checkDoesNotApplyUDT("v IN ({a: 1}, {b: 'abc'}, {a: 0, b: 'def'}, null)", v);
checkDoesNotApplyUDT("v IN ()", v);
// multiple conditions
checkDoesNotApplyUDT("v IN () AND v IN ({a: 0})", v);
checkDoesNotApplyUDT("v > {a: -1} AND v < {a: 0}", v);
/////////////////// null prefix on stored udt ////////////////////
v = userType("a", null, "b", "abc");
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
checkAppliesUDT("v = {a: null, b: 'abc'}", v);
checkAppliesUDT("v = {b: 'abc'}", v);
checkAppliesUDT("v != null", v);
checkAppliesUDT("v != {a: 0, b: 'abc'}", v);
checkAppliesUDT("v != {a: 0}", v);
checkAppliesUDT("v != {b: 'def'}", v);
checkAppliesUDT("v > {a: null, b: 'aaa'}", v);
checkAppliesUDT("v > {b: 'aaa'}", v);
checkAppliesUDT("v >= {a: null, b: 'aaa'}", v);
checkAppliesUDT("v >= {b: 'abc'}", v);
checkAppliesUDT("v < {a: null, b: 'zzz'}", v);
checkAppliesUDT("v < {a: 0, b: 'abc'}", v);
checkAppliesUDT("v < {a: 0}", v);
checkAppliesUDT("v < {b: 'zzz'}", v);
checkAppliesUDT("v <= {a: null, b: 'zzz'}", v);
checkAppliesUDT("v <= {a: 0}", v);
checkAppliesUDT("v <= {b: 'abc'}", v);
checkAppliesUDT("v IN (null, {a: null, b: 'abc'}, {a: 0})", v);
checkAppliesUDT("v IN (null, {a: 0, b: 'abc'}, {b: 'abc'})", v);
// multiple conditions
checkAppliesUDT("v > {b: 'aaa'} AND v >= {b: 'abc'}", v);
checkAppliesUDT("v != null AND v IN ({a: 0}, {a: null, b: 'abc'})", v);
// should not apply
checkDoesNotApplyUDT("v = {a: 0, b: 'def'}", v);
checkDoesNotApplyUDT("v = {a: 1}", v);
checkDoesNotApplyUDT("v = {b: 'def'}", v);
checkDoesNotApplyUDT("v = null", v);
checkDoesNotApplyUDT("v != {b: 'abc'}", v);
checkDoesNotApplyUDT("v != {a: null, b: 'abc'}", v);
checkDoesNotApplyUDT("v > {a: 1, b: 'abc'}", v);
checkDoesNotApplyUDT("v > {a: null, b: 'zzz'}", v);
checkDoesNotApplyUDT("v > {b: 'zzz'}", v);
checkDoesNotApplyUDT("v >= {a: null, b: 'zzz'}", v);
checkDoesNotApplyUDT("v >= {a: 1}", v);
checkDoesNotApplyUDT("v >= {b: 'zzz'}", v);
checkDoesNotApplyUDT("v < {a: null, b: 'aaa'}", v);
checkDoesNotApplyUDT("v < {b: 'aaa'}", v);
checkDoesNotApplyUDT("v <= {a: null, b: 'aaa'}", v);
checkDoesNotApplyUDT("v <= {b: 'aaa'}", v);
checkDoesNotApplyUDT("v IN ({a: 1}, {a: 1, b: 'abc'}, {a: null, b: 'def'}, null)", v);
checkDoesNotApplyUDT("v IN ()", v);
// multiple conditions
checkDoesNotApplyUDT("v IN () AND v IN ({b: 'abc'})", v);
checkDoesNotApplyUDT("v IN () AND v IN ({a: null, b: 'abc'})", v);
checkDoesNotApplyUDT("v > {a: -1} AND v < {a: 0}", v);
/////////////////// null udt ////////////////////
v = null;
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
checkAppliesUDT("v = null", v);
checkAppliesUDT("v IN (null, {a: null, b: 'abc'}, {a: 0})", v);
checkAppliesUDT("v IN (null, {a: 0, b: 'abc'}, {b: 'abc'})", v);
// multiple conditions
checkAppliesUDT("v = null AND v IN (null, {a: 0}, {a: null, b: 'abc'})", v);
// should not apply
checkDoesNotApplyUDT("v = {a: 0, b: 'def'}", v);
checkDoesNotApplyUDT("v = {a: 1}", v);
checkDoesNotApplyUDT("v = {b: 'def'}", v);
checkDoesNotApplyUDT("v != null", v);
checkDoesNotApplyUDT("v > {a: 1, b: 'abc'}", v);
checkDoesNotApplyUDT("v > {a: null, b: 'zzz'}", v);
checkDoesNotApplyUDT("v > {b: 'zzz'}", v);
checkDoesNotApplyUDT("v >= {a: null, b: 'zzz'}", v);
checkDoesNotApplyUDT("v >= {a: 1}", v);
checkDoesNotApplyUDT("v >= {b: 'zzz'}", v);
checkDoesNotApplyUDT("v < {a: null, b: 'aaa'}", v);
checkDoesNotApplyUDT("v < {b: 'aaa'}", v);
checkDoesNotApplyUDT("v <= {a: null, b: 'aaa'}", v);
checkDoesNotApplyUDT("v <= {b: 'aaa'}", v);
checkDoesNotApplyUDT("v IN ({a: 1}, {a: 1, b: 'abc'}, {a: null, b: 'def'})", v);
checkDoesNotApplyUDT("v IN ()", v);
// multiple conditions
checkDoesNotApplyUDT("v IN () AND v IN ({b: 'abc'})", v);
checkDoesNotApplyUDT("v > {a: -1} AND v < {a: 0}", v);
}
}
@Test
public void testUDTField() throws Throwable
{
String typename = createType("CREATE TYPE %s (a int, b text)");
String myType = KEYSPACE + '.' + typename;
for (boolean frozen : new boolean[] {false, true})
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, v %s)",
frozen
? "frozen<" + myType + ">"
: myType));
Object v = userType("a", 0, "b", "abc");
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
checkAppliesUDT("v.a = 0", v);
checkAppliesUDT("v.b = 'abc'", v);
checkAppliesUDT("v.a < 1", v);
checkAppliesUDT("v.b < 'zzz'", v);
checkAppliesUDT("v.b <= 'bar'", v);
checkAppliesUDT("v.b > 'aaa'", v);
checkAppliesUDT("v.b >= 'abc'", v);
checkAppliesUDT("v.a != -1", v);
checkAppliesUDT("v.b != 'xxx'", v);
checkAppliesUDT("v.a != null", v);
checkAppliesUDT("v.b != null", v);
checkAppliesUDT("v.a IN (null, 0, 1)", v);
checkAppliesUDT("v.b IN (null, 'xxx', 'abc')", v);
checkAppliesUDT("v.b > 'aaa' AND v.b < 'zzz'", v);
checkAppliesUDT("v.a = 0 AND v.b > 'aaa'", v);
// do not apply
checkDoesNotApplyUDT("v.a = -1", v);
checkDoesNotApplyUDT("v.b = 'xxx'", v);
checkDoesNotApplyUDT("v.a < -1", v);
checkDoesNotApplyUDT("v.b < 'aaa'", v);
checkDoesNotApplyUDT("v.b <= 'aaa'", v);
checkDoesNotApplyUDT("v.b > 'zzz'", v);
checkDoesNotApplyUDT("v.b >= 'zzz'", v);
checkDoesNotApplyUDT("v.a != 0", v);
checkDoesNotApplyUDT("v.b != 'abc'", v);
checkDoesNotApplyUDT("v.a IN (null, -1)", v);
checkDoesNotApplyUDT("v.b IN (null, 'xxx')", v);
checkDoesNotApplyUDT("v.a IN ()", v);
checkDoesNotApplyUDT("v.b IN ()", v);
checkDoesNotApplyUDT("v.b != null AND v.b IN ()", v);
// invalid
checkInvalidUDT("v.c = null", v, InvalidRequestException.class);
checkInvalidUDT("v.a < null", v, InvalidRequestException.class);
checkInvalidUDT("v.a <= null", v, InvalidRequestException.class);
checkInvalidUDT("v.a > null", v, InvalidRequestException.class);
checkInvalidUDT("v.a >= null", v, InvalidRequestException.class);
checkInvalidUDT("v.a IN null", v, SyntaxException.class);
checkInvalidUDT("v.a IN 367", v, SyntaxException.class);
checkInvalidUDT("v.b IN (1, 2, 3)", v, InvalidRequestException.class);
checkInvalidUDT("v.a CONTAINS 367", v, SyntaxException.class);
checkInvalidUDT("v.a CONTAINS KEY 367", v, SyntaxException.class);
/////////////// null suffix on udt ////////////////
v = userType("a", 0, "b", null);
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
checkAppliesUDT("v.a = 0", v);
checkAppliesUDT("v.b = null", v);
checkAppliesUDT("v.b != 'xxx'", v);
checkAppliesUDT("v.a != null", v);
checkAppliesUDT("v.a IN (null, 0, 1)", v);
checkAppliesUDT("v.b IN (null, 'xxx', 'abc')", v);
checkAppliesUDT("v.a = 0 AND v.b = null", v);
// do not apply
checkDoesNotApplyUDT("v.b = 'abc'", v);
checkDoesNotApplyUDT("v.a < -1", v);
checkDoesNotApplyUDT("v.b < 'aaa'", v);
checkDoesNotApplyUDT("v.b <= 'aaa'", v);
checkDoesNotApplyUDT("v.b > 'zzz'", v);
checkDoesNotApplyUDT("v.b >= 'zzz'", v);
checkDoesNotApplyUDT("v.a != 0", v);
checkDoesNotApplyUDT("v.b != null", v);
checkDoesNotApplyUDT("v.a IN (null, -1)", v);
checkDoesNotApplyUDT("v.b IN ('xxx', 'abc')", v);
checkDoesNotApplyUDT("v.a IN ()", v);
checkDoesNotApplyUDT("v.b IN ()", v);
checkDoesNotApplyUDT("v.b != null AND v.b IN ()", v);
/////////////// null prefix on udt ////////////////
v = userType("a", null, "b", "abc");
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
checkAppliesUDT("v.a = null", v);
checkAppliesUDT("v.b = 'abc'", v);
checkAppliesUDT("v.a != 0", v);
checkAppliesUDT("v.b != null", v);
checkAppliesUDT("v.a IN (null, 0, 1)", v);
checkAppliesUDT("v.b IN (null, 'xxx', 'abc')", v);
checkAppliesUDT("v.a = null AND v.b = 'abc'", v);
// do not apply
checkDoesNotApplyUDT("v.a = 0", v);
checkDoesNotApplyUDT("v.a < -1", v);
checkDoesNotApplyUDT("v.b >= 'zzz'", v);
checkDoesNotApplyUDT("v.a != null", v);
checkDoesNotApplyUDT("v.b != 'abc'", v);
checkDoesNotApplyUDT("v.a IN (-1, 0)", v);
checkDoesNotApplyUDT("v.b IN (null, 'xxx')", v);
checkDoesNotApplyUDT("v.a IN ()", v);
checkDoesNotApplyUDT("v.b IN ()", v);
checkDoesNotApplyUDT("v.b != null AND v.b IN ()", v);
/////////////// null udt ////////////////
v = null;
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
checkAppliesUDT("v.a = null", v);
checkAppliesUDT("v.b = null", v);
checkAppliesUDT("v.a != 0", v);
checkAppliesUDT("v.b != 'abc'", v);
checkAppliesUDT("v.a IN (null, 0, 1)", v);
checkAppliesUDT("v.b IN (null, 'xxx', 'abc')", v);
checkAppliesUDT("v.a = null AND v.b = null", v);
// do not apply
checkDoesNotApplyUDT("v.a = 0", v);
checkDoesNotApplyUDT("v.a < -1", v);
checkDoesNotApplyUDT("v.b >= 'zzz'", v);
checkDoesNotApplyUDT("v.a != null", v);
checkDoesNotApplyUDT("v.b != null", v);
checkDoesNotApplyUDT("v.a IN (-1, 0)", v);
checkDoesNotApplyUDT("v.b IN ('xxx', 'abc')", v);
checkDoesNotApplyUDT("v.a IN ()", v);
checkDoesNotApplyUDT("v.b IN ()", v);
checkDoesNotApplyUDT("v.b != null AND v.b IN ()", v);
}
}
void checkAppliesUDT(String condition, Object value) throws Throwable
{
assertRows(execute("UPDATE %s SET v = ? WHERE k = 0 IF " + condition, value), row(true));
assertRows(execute("SELECT * FROM %s"), row(0, value));
}
void checkDoesNotApplyUDT(String condition, Object value) throws Throwable
{
assertRows(execute("UPDATE %s SET v = ? WHERE k = 0 IF " + condition, value),
row(false, value));
assertRows(execute("SELECT * FROM %s"), row(0, value));
}
void checkInvalidUDT(String condition, Object value, Class<? extends Throwable> expected) throws Throwable
{
assertInvalidThrow(expected, "UPDATE %s SET v = ? WHERE k = 0 IF " + condition, value);
assertRows(execute("SELECT * FROM %s"), row(0, value));
}
/**
* Migrated from cql_tests.py:TestCQL.whole_list_conditional_test()
*/
@Test
public void testWholeList() throws Throwable
{
for (boolean frozen : new boolean[] {false, true})
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, l %s)",
frozen
? "frozen<list<text>>"
: "list<text>"));
execute("INSERT INTO %s(k, l) VALUES (0, ['foo', 'bar', 'foobar'])");
check_applies_list("l = ['foo', 'bar', 'foobar']");
check_applies_list("l != ['baz']");
check_applies_list("l > ['a']");
check_applies_list("l >= ['a']");
check_applies_list("l < ['z']");
check_applies_list("l <= ['z']");
check_applies_list("l IN (null, ['foo', 'bar', 'foobar'], ['a'])");
// multiple conditions
check_applies_list("l > ['aaa', 'bbb'] AND l > ['aaa']");
check_applies_list("l != null AND l IN (['foo', 'bar', 'foobar'])");
// should not apply
check_does_not_apply_list("l = ['baz']");
check_does_not_apply_list("l != ['foo', 'bar', 'foobar']");
check_does_not_apply_list("l > ['z']");
check_does_not_apply_list("l >= ['z']");
check_does_not_apply_list("l < ['a']");
check_does_not_apply_list("l <= ['a']");
check_does_not_apply_list("l IN (['a'], null)");
check_does_not_apply_list("l IN ()");
// multiple conditions
check_does_not_apply_list("l IN () AND l IN (['foo', 'bar', 'foobar'])");
check_does_not_apply_list("l > ['zzz'] AND l < ['zzz']");
check_invalid_list("l = [null]", InvalidRequestException.class);
check_invalid_list("l < null", InvalidRequestException.class);
check_invalid_list("l <= null", InvalidRequestException.class);
check_invalid_list("l > null", InvalidRequestException.class);
check_invalid_list("l >= null", InvalidRequestException.class);
check_invalid_list("l IN null", SyntaxException.class);
check_invalid_list("l IN 367", SyntaxException.class);
check_invalid_list("l CONTAINS KEY 123", SyntaxException.class);
// not supported yet
check_invalid_list("m CONTAINS 'bar'", SyntaxException.class);
}
}
void check_applies_list(String condition) throws Throwable
{
assertRows(execute("UPDATE %s SET l = ['foo', 'bar', 'foobar'] WHERE k=0 IF " + condition), row(true));
assertRows(execute("SELECT * FROM %s"), row(0, list("foo", "bar", "foobar")));
}
void check_does_not_apply_list(String condition) throws Throwable
{
assertRows(execute("UPDATE %s SET l = ['foo', 'bar', 'foobar'] WHERE k=0 IF " + condition),
row(false, list("foo", "bar", "foobar")));
assertRows(execute("SELECT * FROM %s"), row(0, list("foo", "bar", "foobar")));
}
void check_invalid_list(String condition, Class<? extends Throwable> expected) throws Throwable
{
assertInvalidThrow(expected, "UPDATE %s SET l = ['foo', 'bar', 'foobar'] WHERE k=0 IF " + condition);
assertRows(execute("SELECT * FROM %s"), row(0, list("foo", "bar", "foobar")));
}
/**
* Migrated from cql_tests.py:TestCQL.list_item_conditional_test()
*/
@Test
public void testListItem() throws Throwable
{
for (boolean frozen : new boolean[]{ false, true })
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, l %s)",
frozen
? "frozen<list<text>>"
: "list<text>"));
execute("INSERT INTO %s(k, l) VALUES (0, ['foo', 'bar', 'foobar'])");
assertInvalidMessage("Invalid null value for list element access",
"DELETE FROM %s WHERE k=0 IF l[?] = ?", null, "foobar");
assertInvalidMessage("Invalid negative list index -2",
"DELETE FROM %s WHERE k=0 IF l[?] = ?", -2, "foobar");
assertRows(execute("DELETE FROM %s WHERE k=0 IF l[?] = ?", 1, null), row(false, list("foo", "bar", "foobar")));
assertRows(execute("DELETE FROM %s WHERE k=0 IF l[?] = ?", 1, "foobar"), row(false, list("foo", "bar", "foobar")));
assertRows(execute("SELECT * FROM %s"), row(0, list("foo", "bar", "foobar")));
assertRows(execute("DELETE FROM %s WHERE k=0 IF l[?] = ?", 1, "bar"), row(true));
assertEmpty(execute("SELECT * FROM %s"));
}
}
/**
* Test expanded functionality from CASSANDRA-6839,
* migrated from cql_tests.py:TestCQL.expanded_list_item_conditional_test()
*/
@Test
public void testExpandedListItem() throws Throwable
{
for (boolean frozen : new boolean[] {false, true})
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, l %s)",
frozen
? "frozen<list<text>>"
: "list<text>"));
execute("INSERT INTO %s (k, l) VALUES (0, ['foo', 'bar', 'foobar'])");
check_applies_list("l[1] < 'zzz'");
check_applies_list("l[1] <= 'bar'");
check_applies_list("l[1] > 'aaa'");
check_applies_list("l[1] >= 'bar'");
check_applies_list("l[1] != 'xxx'");
check_applies_list("l[1] != null");
check_applies_list("l[1] IN (null, 'xxx', 'bar')");
check_applies_list("l[1] > 'aaa' AND l[1] < 'zzz'");
// check beyond end of list
check_applies_list("l[3] = null");
check_applies_list("l[3] IN (null, 'xxx', 'bar')");
check_does_not_apply_list("l[1] < 'aaa'");
check_does_not_apply_list("l[1] <= 'aaa'");
check_does_not_apply_list("l[1] > 'zzz'");
check_does_not_apply_list("l[1] >= 'zzz'");
check_does_not_apply_list("l[1] != 'bar'");
check_does_not_apply_list("l[1] IN (null, 'xxx')");
check_does_not_apply_list("l[1] IN ()");
check_does_not_apply_list("l[1] != null AND l[1] IN ()");
// check beyond end of list
check_does_not_apply_list("l[3] != null");
check_does_not_apply_list("l[3] = 'xxx'");
check_invalid_list("l[1] < null", InvalidRequestException.class);
check_invalid_list("l[1] <= null", InvalidRequestException.class);
check_invalid_list("l[1] > null", InvalidRequestException.class);
check_invalid_list("l[1] >= null", InvalidRequestException.class);
check_invalid_list("l[1] IN null", SyntaxException.class);
check_invalid_list("l[1] IN 367", SyntaxException.class);
check_invalid_list("l[1] IN (1, 2, 3)", InvalidRequestException.class);
check_invalid_list("l[1] CONTAINS 367", SyntaxException.class);
check_invalid_list("l[1] CONTAINS KEY 367", SyntaxException.class);
check_invalid_list("l[null] = null", InvalidRequestException.class);
}
}
/**
* Migrated from cql_tests.py:TestCQL.whole_set_conditional_test()
*/
@Test
public void testWholeSet() throws Throwable
{
for (boolean frozen : new boolean[] {false, true})
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, s %s)",
frozen
? "frozen<set<text>>"
: "set<text>"));
execute("INSERT INTO %s (k, s) VALUES (0, {'bar', 'foo'})");
check_applies_set("s = {'bar', 'foo'}");
check_applies_set("s = {'foo', 'bar'}");
check_applies_set("s != {'baz'}");
check_applies_set("s > {'a'}");
check_applies_set("s >= {'a'}");
check_applies_set("s < {'z'}");
check_applies_set("s <= {'z'}");
check_applies_set("s IN (null, {'bar', 'foo'}, {'a'})");
// multiple conditions
check_applies_set("s > {'a'} AND s < {'z'}");
check_applies_set("s IN (null, {'bar', 'foo'}, {'a'}) AND s IN ({'a'}, {'bar', 'foo'}, null)");
// should not apply
check_does_not_apply_set("s = {'baz'}");
check_does_not_apply_set("s != {'bar', 'foo'}");
check_does_not_apply_set("s > {'z'}");
check_does_not_apply_set("s >= {'z'}");
check_does_not_apply_set("s < {'a'}");
check_does_not_apply_set("s <= {'a'}");
check_does_not_apply_set("s IN ({'a'}, null)");
check_does_not_apply_set("s IN ()");
check_does_not_apply_set("s != null AND s IN ()");
check_invalid_set("s = {null}", InvalidRequestException.class);
check_invalid_set("s < null", InvalidRequestException.class);
check_invalid_set("s <= null", InvalidRequestException.class);
check_invalid_set("s > null", InvalidRequestException.class);
check_invalid_set("s >= null", InvalidRequestException.class);
check_invalid_set("s IN null", SyntaxException.class);
check_invalid_set("s IN 367", SyntaxException.class);
check_invalid_set("s CONTAINS KEY 123", SyntaxException.class);
// element access is not allow for sets
check_invalid_set("s['foo'] = 'foobar'", InvalidRequestException.class);
// not supported yet
check_invalid_set("m CONTAINS 'bar'", SyntaxException.class);
}
}
void check_applies_set(String condition) throws Throwable
{
assertRows(execute("UPDATE %s SET s = {'bar', 'foo'} WHERE k=0 IF " + condition), row(true));
assertRows(execute("SELECT * FROM %s"), row(0, set("bar", "foo")));
}
void check_does_not_apply_set(String condition) throws Throwable
{
assertRows(execute("UPDATE %s SET s = {'bar', 'foo'} WHERE k=0 IF " + condition), row(false, set("bar", "foo")));
assertRows(execute("SELECT * FROM %s"), row(0, set("bar", "foo")));
}
void check_invalid_set(String condition, Class<? extends Throwable> expected) throws Throwable
{
assertInvalidThrow(expected, "UPDATE %s SET s = {'bar', 'foo'} WHERE k=0 IF " + condition);
assertRows(execute("SELECT * FROM %s"), row(0, set("bar", "foo")));
}
/**
* Migrated from cql_tests.py:TestCQL.whole_map_conditional_test()
*/
@Test
public void testWholeMap() throws Throwable
{
for (boolean frozen : new boolean[] {false, true})
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)",
frozen
? "frozen<map<text, text>>"
: "map<text, text>"));
execute("INSERT INTO %s (k, m) VALUES (0, {'foo' : 'bar'})");
check_applies_map("m = {'foo': 'bar'}");
check_applies_map("m > {'a': 'a'}");
check_applies_map("m >= {'a': 'a'}");
check_applies_map("m < {'z': 'z'}");
check_applies_map("m <= {'z': 'z'}");
check_applies_map("m != {'a': 'a'}");
check_applies_map("m IN (null, {'a': 'a'}, {'foo': 'bar'})");
// multiple conditions
check_applies_map("m > {'a': 'a'} AND m < {'z': 'z'}");
check_applies_map("m != null AND m IN (null, {'a': 'a'}, {'foo': 'bar'})");
// should not apply
check_does_not_apply_map("m = {'a': 'a'}");
check_does_not_apply_map("m > {'z': 'z'}");
check_does_not_apply_map("m >= {'z': 'z'}");
check_does_not_apply_map("m < {'a': 'a'}");
check_does_not_apply_map("m <= {'a': 'a'}");
check_does_not_apply_map("m != {'foo': 'bar'}");
check_does_not_apply_map("m IN ({'a': 'a'}, null)");
check_does_not_apply_map("m IN ()");
check_does_not_apply_map("m = null AND m != null");
check_invalid_map("m = {null: null}", InvalidRequestException.class);
check_invalid_map("m = {'a': null}", InvalidRequestException.class);
check_invalid_map("m = {null: 'a'}", InvalidRequestException.class);
check_invalid_map("m < null", InvalidRequestException.class);
check_invalid_map("m IN null", SyntaxException.class);
// not supported yet
check_invalid_map("m CONTAINS 'bar'", SyntaxException.class);
check_invalid_map("m CONTAINS KEY 'foo'", SyntaxException.class);
check_invalid_map("m CONTAINS null", SyntaxException.class);
check_invalid_map("m CONTAINS KEY null", SyntaxException.class);
}
}
/**
* Migrated from cql_tests.py:TestCQL.map_item_conditional_test()
*/
@Test
public void testMapItem() throws Throwable
{
for (boolean frozen : new boolean[]{ false, true })
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)",
frozen
? "frozen<map<text, text>>"
: "map<text, text>"));
execute("INSERT INTO %s (k, m) VALUES (0, {'foo' : 'bar'})");
assertInvalidMessage("Invalid null value for map element access",
"DELETE FROM %s WHERE k=0 IF m[?] = ?", null, "foo");
assertRows(execute("DELETE FROM %s WHERE k=0 IF m[?] = ?", "foo", "foo"), row(false, map("foo", "bar")));
assertRows(execute("DELETE FROM %s WHERE k=0 IF m[?] = ?", "foo", null), row(false, map("foo", "bar")));
assertRows(execute("SELECT * FROM %s"), row(0, map("foo", "bar")));
assertRows(execute("DELETE FROM %s WHERE k=0 IF m[?] = ?", "foo", "bar"), row(true));
assertEmpty(execute("SELECT * FROM %s"));
execute("INSERT INTO %s(k, m) VALUES (1, null)");
if (frozen)
assertInvalidMessage("Invalid operation (m['foo'] = 'bar') for frozen collection column m",
"UPDATE %s set m['foo'] = 'bar', m['bar'] = 'foo' WHERE k = 1 IF m[?] IN (?, ?)", "foo", "blah", null);
else
assertRows(execute("UPDATE %s set m['foo'] = 'bar', m['bar'] = 'foo' WHERE k = 1 IF m[?] IN (?, ?)", "foo", "blah", null), row(true));
}
}
@Test
public void testFrozenWithNullValues() throws Throwable
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)", "frozen<list<text>>"));
execute("INSERT INTO %s (k, m) VALUES (0, null)");
assertRows(execute("UPDATE %s SET m = ? WHERE k = 0 IF m = ?", list("test"), list("comparison")), row(false, null));
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)", "frozen<map<text,int>>"));
execute("INSERT INTO %s (k, m) VALUES (0, null)");
assertRows(execute("UPDATE %s SET m = ? WHERE k = 0 IF m = ?", map("test", 3), map("comparison", 2)), row(false, null));
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)", "frozen<set<text>>"));
execute("INSERT INTO %s (k, m) VALUES (0, null)");
assertRows(execute("UPDATE %s SET m = ? WHERE k = 0 IF m = ?", set("test"), set("comparison")), row(false, null));
}
/**
* Test expanded functionality from CASSANDRA-6839,
* migrated from cql_tests.py:TestCQL.expanded_map_item_conditional_test()
*/
@Test
public void testExpandedMapItem() throws Throwable
{
for (boolean frozen : new boolean[]{ false, true })
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, m %s)",
frozen
? "frozen<map<text, text>>"
: "map<text, text>"));
execute("INSERT INTO %s (k, m) VALUES (0, {'foo' : 'bar'})");
check_applies_map("m['xxx'] = null");
check_applies_map("m['foo'] < 'zzz'");
check_applies_map("m['foo'] <= 'bar'");
check_applies_map("m['foo'] > 'aaa'");
check_applies_map("m['foo'] >= 'bar'");
check_applies_map("m['foo'] != 'xxx'");
check_applies_map("m['foo'] != null");
check_applies_map("m['foo'] IN (null, 'xxx', 'bar')");
check_applies_map("m['xxx'] IN (null, 'xxx', 'bar')"); // m['xxx'] is not set
// multiple conditions
check_applies_map("m['foo'] < 'zzz' AND m['foo'] > 'aaa'");
check_does_not_apply_map("m['foo'] < 'aaa'");
check_does_not_apply_map("m['foo'] <= 'aaa'");
check_does_not_apply_map("m['foo'] > 'zzz'");
check_does_not_apply_map("m['foo'] >= 'zzz'");
check_does_not_apply_map("m['foo'] != 'bar'");
check_does_not_apply_map("m['xxx'] != null"); // m['xxx'] is not set
check_does_not_apply_map("m['foo'] IN (null, 'xxx')");
check_does_not_apply_map("m['foo'] IN ()");
check_does_not_apply_map("m['foo'] != null AND m['foo'] = null");
check_invalid_map("m['foo'] < null", InvalidRequestException.class);
check_invalid_map("m['foo'] <= null", InvalidRequestException.class);
check_invalid_map("m['foo'] > null", InvalidRequestException.class);
check_invalid_map("m['foo'] >= null", InvalidRequestException.class);
check_invalid_map("m['foo'] IN null", SyntaxException.class);
check_invalid_map("m['foo'] IN 367", SyntaxException.class);
check_invalid_map("m['foo'] IN (1, 2, 3)", InvalidRequestException.class);
check_invalid_map("m['foo'] CONTAINS 367", SyntaxException.class);
check_invalid_map("m['foo'] CONTAINS KEY 367", SyntaxException.class);
check_invalid_map("m[null] = null", InvalidRequestException.class);
}
}
void check_applies_map(String condition) throws Throwable
{
assertRows(execute("UPDATE %s SET m = {'foo': 'bar'} WHERE k=0 IF " + condition), row(true));
assertRows(execute("SELECT * FROM %s"), row(0, map("foo", "bar")));
}
void check_does_not_apply_map(String condition) throws Throwable
{
assertRows(execute("UPDATE %s SET m = {'foo': 'bar'} WHERE k=0 IF " + condition), row(false, map("foo", "bar")));
assertRows(execute("SELECT * FROM %s"), row(0, map("foo", "bar")));
}
void check_invalid_map(String condition, Class<? extends Throwable> expected) throws Throwable
{
assertInvalidThrow(expected, "UPDATE %s SET m = {'foo': 'bar'} WHERE k=0 IF " + condition);
assertRows(execute("SELECT * FROM %s"), row(0, map("foo", "bar")));
}
@Test
public void testInMarkerWithUDTs() throws Throwable
{
String typename = createType("CREATE TYPE %s (a int, b text)");
String myType = KEYSPACE + '.' + typename;
for (boolean frozen : new boolean[] {false, true})
{
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, v %s)",
frozen
? "frozen<" + myType + ">"
: myType));
Object v = userType("a", 0, "b", "abc");
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
// Does not apply
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType("a", 1, "b", "abc"), userType("a", 0, "b", "ac")),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType("a", 1, "b", "abc"), null),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", null, null),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType("a", 1, "b", "abc"), unset()),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", unset(), unset()),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN ?", list(userType("a", 1, "b", "abc"), userType("a", 0, "b", "ac"))),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v.a IN (?, ?)", 1, 2),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v.a IN (?, ?)", 1, null),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v.a IN ?", list(1, 2)),
row(false, v));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v.a IN (?, ?)", 1, unset()),
row(false, v));
// Does apply
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType("a", 0, "b", "abc"), userType("a", 0, "b", "ac")),
row(true));
assertRows(execute("UPDATE %s SET v = {a: 1, b: 'bc'} WHERE k = 0 IF v IN (?, ?)", userType("a", 0, "b", "bc"), null),
row(true));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'abc'} WHERE k = 0 IF v IN ?", list(userType("a", 1, "b", "bc"), userType("a", 0, "b", "ac"))),
row(true));
assertRows(execute("UPDATE %s SET v = {a: 1, b: 'bc'} WHERE k = 0 IF v IN (?, ?, ?)", userType("a", 1, "b", "bc"), unset(), userType("a", 0, "b", "abc")),
row(true));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v.a IN (?, ?)", 1, 0),
row(true));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'abc'} WHERE k = 0 IF v.a IN (?, ?)", 0, null),
row(true));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v.a IN ?", list(0, 1)),
row(true));
assertRows(execute("UPDATE %s SET v = {a: 0, b: 'abc'} WHERE k = 0 IF v.a IN (?, ?, ?)", 1, unset(), 0),
row(true));
assertInvalidMessage("Invalid null list in IN condition",
"UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN ?", (List<ByteBuffer>) null);
assertInvalidMessage("Invalid 'unset' value in condition",
"UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v IN ?", unset());
assertInvalidMessage("Invalid 'unset' value in condition",
"UPDATE %s SET v = {a: 0, b: 'bc'} WHERE k = 0 IF v.a IN ?", unset());
}
}
}