Files
scylladb/test/cqlpy/test_select_collection_element.py
Michael Litvak 5ef7afb968 cql3: allow SELECT of specific collection key
This adds to the grammar the option to SELECT a specific key in a
collection column using subscript syntax.

For example:
SELECT map['key'] FROM table
SELECT map['key1']['key2'] FROM table

The key can also be parameterized in a prepared query. For this we need
to pass the query options to result_set_builder where we process the
selectors.

Fixes scylladb/scylladb#7751
2024-12-30 17:05:20 +02:00

159 lines
8.2 KiB
Python

# Copyright 2024-present ScyllaDB
#
# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
#############################################################################
# Tests for SELECT of a specific key in a collection column
#############################################################################
import pytest
import re
import time
from cassandra.protocol import InvalidRequest
from .util import unique_name, unique_key_int, unique_key_string, new_test_table, new_type, new_function
@pytest.fixture(scope="module")
def table1(cql, test_keyspace):
table = test_keyspace + "." + unique_name()
cql.execute(f"CREATE TABLE {table} (p int PRIMARY KEY, m map<int, int>)")
yield table
cql.execute("DROP TABLE " + table)
def test_basic_int_key_selection(cql, table1):
p = unique_key_int()
cql.execute(f"INSERT INTO {table1}(p,m) VALUES ({p}, " + "{1:10,2:20})")
assert list(cql.execute(f"SELECT m[1] FROM {table1} WHERE p={p}")) == [(10,)]
assert list(cql.execute(f"SELECT m[2] FROM {table1} WHERE p={p}")) == [(20,)]
assert list(cql.execute(f"SELECT m[3] FROM {table1} WHERE p={p}")) == [(None,)]
def test_basic_string_key_selection(cql, test_keyspace):
schema = 'p int PRIMARY KEY, m map<text, int>'
with new_test_table(cql, test_keyspace, schema) as table:
p = unique_key_int()
cql.execute(f"INSERT INTO {table}(p,m) VALUES ({p}, " + "{'aa':10,'ab':20})")
assert list(cql.execute(f"SELECT m['aa'] FROM {table} WHERE p={p}")) == [(10,)]
assert list(cql.execute(f"SELECT m['ab'] FROM {table} WHERE p={p}")) == [(20,)]
assert list(cql.execute(f"SELECT m['ac'] FROM {table} WHERE p={p}")) == [(None,)]
def test_subscript_type_mismatch(cql, table1):
p = unique_key_int()
cql.execute(f"INSERT INTO {table1}(p,m) VALUES ({p}, " + "{1:10,2:20})")
with pytest.raises(InvalidRequest):
cql.execute(f"SELECT m['x'] FROM {table1} WHERE p={p}")
def test_subscript_with_alias(cql, table1):
p = unique_key_int()
cql.execute(f"INSERT INTO {table1}(p,m) VALUES ({p}, " + "{1:10,2:20})")
assert [(r.m1, r.m2) for r in cql.execute(f"SELECT m[1] as m1, m[2] as m2 FROM {table1} WHERE p={p}")] == [(10, 20)]
def test_frozen_map_subscript(cql, test_keyspace):
schema = 'p int PRIMARY KEY, m frozen<map<int, int>>'
with new_test_table(cql, test_keyspace, schema) as table:
p = unique_key_int()
cql.execute(f"INSERT INTO {table}(p,m) VALUES ({p}, " + "{1:10,2:20})")
assert list(cql.execute(f"SELECT m[1] FROM {table} WHERE p={p}")) == [(10,)]
assert list(cql.execute(f"SELECT m[2] FROM {table} WHERE p={p}")) == [(20,)]
assert list(cql.execute(f"SELECT m[3] FROM {table} WHERE p={p}")) == [(None,)]
def test_nested_key_selection(cql, test_keyspace):
schema = 'p int PRIMARY KEY, m map<text, frozen<map<text, int>>>'
with new_test_table(cql, test_keyspace, schema) as table:
p = unique_key_int()
cql.execute(f"INSERT INTO {table}(p, m) VALUES ({p}, " + "{'1': {'a': 10, 'b': 11}, '2': {'a': 12}})")
assert list(cql.execute(f"SELECT m['1']['a'] FROM {table} WHERE p={p}")) == [(10,)]
assert list(cql.execute(f"SELECT m['1']['b'] FROM {table} WHERE p={p}")) == [(11,)]
assert list(cql.execute(f"SELECT m['2']['a'] FROM {table} WHERE p={p}")) == [(12,)]
assert list(cql.execute(f"SELECT m['2']['b'] FROM {table} WHERE p={p}")) == [(None,)]
def test_prepare_key(cql, table1):
p = unique_key_int()
cql.execute(f"INSERT INTO {table1} (p,m) VALUES ({p}, " + "{1:10,2:20})")
lookup1 = cql.prepare(f"SELECT m[?] FROM {table1} WHERE p = ?")
assert list(cql.execute(lookup1, [1, p])) == [(10,)]
assert list(cql.execute(lookup1, [2, p])) == [(20,)]
assert list(cql.execute(lookup1, [3, p])) == [(None,)]
lookup2 = cql.prepare(f"SELECT m[:x1], m[:x2] FROM {table1} WHERE p = :key")
assert list(cql.execute(lookup2, {'x1':2, 'x2':1, 'key':p})) == [(20,10)]
def test_null_map(cql, table1):
p = unique_key_int()
cql.execute(f"INSERT INTO {table1}(p) VALUES ({p})")
assert list(cql.execute(f"SELECT m[1] FROM {table1} WHERE p={p}")) == [(None,)]
# scylla only because scylla returns null while cassandra returns error
def test_null_subscript(scylla_only, cql, table1):
p = unique_key_int()
cql.execute(f"INSERT INTO {table1}(p,m) VALUES ({p}, " + "{1:10,2:20})")
assert list(cql.execute(f"SELECT m[null] FROM {table1} WHERE p={p}")) == [(None,)]
def test_subscript_and_field(cql, test_keyspace):
with new_type(cql, test_keyspace, '(a int)') as typ:
schema = f"p int PRIMARY KEY, m map<int, frozen<{typ}>>"
with new_test_table(cql, test_keyspace, schema) as table:
p = unique_key_int()
cql.execute(f"INSERT INTO {table}(p,m) VALUES ({p}, " + "{1:{a:10}})")
assert list(cql.execute(f"SELECT m[1].a FROM {table} WHERE p={p}")) == [(10,)]
def test_field_and_subscript(cql, test_keyspace):
with new_type(cql, test_keyspace, '(a frozen<map<int,int>>)') as typ:
schema = f"p int PRIMARY KEY, t {typ}"
with new_test_table(cql, test_keyspace, schema) as table:
p = unique_key_int()
cql.execute(f"INSERT INTO {table}(p,t) VALUES ({p}, " + "{a:{1:10}})")
assert list(cql.execute(f"SELECT t.a[1] FROM {table} WHERE p={p}")) == [(10,)]
def test_field_and_subscript_and_field(cql, test_keyspace):
with new_type(cql, test_keyspace, '(b int)') as typ1, \
new_type(cql, test_keyspace, f"(a frozen<map<int,{typ1}>>)") as typ2:
schema = f"p int PRIMARY KEY, t {typ2}"
with new_test_table(cql, test_keyspace, schema) as table:
p = unique_key_int()
cql.execute(f"INSERT INTO {table}(p,t) VALUES ({p}, " + "{a:{1:{b:10}}})")
assert list(cql.execute(f"SELECT t.a[1].b FROM {table} WHERE p={p}")) == [(10,)]
def test_other_types_cannot_be_subscripted(cql, table1):
with pytest.raises(InvalidRequest, match='not a'):
cql.execute(f"SELECT p[2] FROM {table1}")
with pytest.raises(InvalidRequest, match='not a'):
cql.execute(f"SELECT token(p)[2] FROM {table1}")
def test_udf_subscript(scylla_only, cql, test_keyspace, table1):
fn = "(k int) CALLED ON NULL INPUT RETURNS int LANGUAGE Lua AS 'return k+1'"
with new_function(cql, test_keyspace, fn, 'add_one'):
p = unique_key_int()
cql.execute(f"INSERT INTO {table1}(p,m) VALUES ({p}, " + "{1:10,2:20})")
assert list(cql.execute(f"SELECT m[add_one(1)] FROM {table1} WHERE p={p}")) == [(20,)]
# cassandra doesn't support subscript on a list
def test_list_subscript(scylla_only, cql, test_keyspace):
schema = 'p int PRIMARY KEY, l list<int>'
with new_test_table(cql, test_keyspace, schema) as table:
p = unique_key_int()
cql.execute(f"INSERT INTO {table}(p,l) VALUES ({p}, " + "[10,20])")
assert list(cql.execute(f"SELECT l[0] FROM {table} WHERE p={p}")) == [(10,)]
assert list(cql.execute(f"SELECT l[1] FROM {table} WHERE p={p}")) == [(20,)]
assert list(cql.execute(f"SELECT l[2] FROM {table} WHERE p={p}")) == [(None,)]
assert list(cql.execute(f"SELECT l[10] FROM {table} WHERE p={p}")) == [(None,)]
def test_set_subscript(cql, test_keyspace):
schema = 'p int PRIMARY KEY, s set<int>'
with new_test_table(cql, test_keyspace, schema) as table:
p = unique_key_int()
cql.execute(f"INSERT INTO {table}(p,s) VALUES ({p}, " + "{10,20})")
assert list(cql.execute(f"SELECT s[0] FROM {table} WHERE p={p}")) == [(None,)]
assert list(cql.execute(f"SELECT s[10] FROM {table} WHERE p={p}")) == [(10,)]
assert list(cql.execute(f"SELECT s[11] FROM {table} WHERE p={p}")) == [(None,)]
assert list(cql.execute(f"SELECT s[20] FROM {table} WHERE p={p}")) == [(20,)]
# scylla only because cassandra doesn't support lua language
@pytest.mark.xfail(reason="#22075")
def test_subscript_function_arg(scylla_only, cql, test_keyspace, table1):
fn = "(k int) CALLED ON NULL INPUT RETURNS int LANGUAGE Lua AS 'return k+1'"
with new_function(cql, test_keyspace, fn, 'add_one'):
p = unique_key_int()
cql.execute(f"INSERT INTO {table1}(p,m) VALUES ({p}, " + "{1:10,2:20})")
assert list(cql.execute(f"SELECT add_one(m[1]) FROM {table1} WHERE p={p}")) == [(11,)]