Files
scylladb/test/alternator/test_key_conditions.py
Szymon Malewski cb8e11653f test/alternator: Number normalization tests
DynamoDB normalizes Number values, so different string representations
of the same number (e.g., "1000" vs "1e3") should be treated as the
same value in all contexts.
In Alternator this is true in most cases, thanks to implicit normalization in
Decimal `to_string()` function.
However this is fragile - and in fact this function should be fixed
due to OOM vulnerability in CQL use (#8002).

This patch adds tests that should prevent regression in cases
that work currently.

Unfortunately not all contexts work currently - mainly the HASH keys
are not normalized and backend handles them by byte representation.
Added test replicate this incorrect behaviour

All added tests pass with DynamoDB, with one exception: weirdly
DynamoDB doesn't recognise unnormalized numbers in BatchGetItem
 as duplicate keys.

Ref SCYLLADB-1575

Closes scylladb/scylladb#29501
2026-05-18 09:42:33 +03:00

551 lines
29 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2020-present ScyllaDB
#
# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1
# Tests for the KeyConditions parameter of the Query operation.
# KeyConditions is the older version of the newer "KeyConditionExpression"
# syntax. That newer syntax is tested in test_key_conditions.py.
import pytest
from botocore.exceptions import ClientError
from decimal import Decimal
from test.alternator.util import random_string, random_bytes, full_query, multiset
# The test_table_{sn,ss,sb}_with_sorted_partition fixtures are the regular
# test_table_{sn,ss,sb} fixture with a partition inserted with many items.
# The table, the partition key, and the items are returned - the items are
# sorted (by column 'c'), so they can be easily used to test various range
# queries. This fixture is useful for writing many small query tests which
# read the same input data without needing to re-insert data for every test,
# so overall the test suite is faster.
@pytest.fixture(scope="module")
def test_table_sn_with_sorted_partition(test_table_sn):
p = random_string()
items = [{'p': p, 'c': i, 'a': random_string()} for i in range(12)]
with test_table_sn.batch_writer() as batch:
for item in items:
batch.put_item(item)
# Add another partition just to make sure that a query of just
# partition p can't just match the entire table and still succeed
batch.put_item({'p': random_string(), 'c': 123, 'a': random_string()})
yield test_table_sn, p, items
@pytest.fixture(scope="module")
def test_table_ss_with_sorted_partition(test_table):
p = random_string()
items = [{'p': p, 'c': str(i).zfill(3), 'a': random_string()} for i in range(12)]
with test_table.batch_writer() as batch:
for item in items:
batch.put_item(item)
batch.put_item({'p': random_string(), 'c': '123', 'a': random_string()})
yield test_table, p, items
@pytest.fixture(scope="module")
def test_table_sb_with_sorted_partition(test_table_sb):
p = random_string()
items = [{'p': p, 'c': bytearray(str(i).zfill(3), 'ascii'), 'a': random_string()} for i in range(12)]
with test_table_sb.batch_writer() as batch:
for item in items:
batch.put_item(item)
batch.put_item({'p': random_string(), 'c': bytearray('123', 'ascii'), 'a': random_string()})
yield test_table_sb, p, items
# A key condition with just a partition key, returning the entire partition
def test_key_conditions_partition(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'}})
# Both items and got_items are already sorted, so simple equality check is enough
assert(got_items == items)
# The "ComparisonOperator" is case sensitive, "EQ" works but "eq" does not:
def test_key_conditions_comparison_operator_case(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
with pytest.raises(ClientError, match='ValidationException.*comparison'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'eq'}})
# Only the "EQ" operator is allowed for the partition keys. The operators "LE",
# "LT", "GE", "GT", "BEGINS_WITH" and "BETWEEN" are allowed for the sort key
# (we'll test this below), but NOT for the partition key. Other random strings
# also are not allowed, but with a different error message.
def test_key_conditions_partition_only_eq(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
for op in ['LE', 'LT', 'GE', 'GT', 'BEGINS_WITH']:
with pytest.raises(ClientError, match='ValidationException.*not supported'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': op}})
# 'BETWEEN' also fails the test above, but with a different error (it needs
# two arguments). So let's test it fails also with two arguments:
with pytest.raises(ClientError, match='ValidationException.*not supported'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p, p], 'ComparisonOperator': 'BETWEEN'}})
# An unknown operator, e.g., "DOG", is also not allowed, but with a
# different error message.
with pytest.raises(ClientError, match='ValidationException.*comparison'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p, p], 'ComparisonOperator': 'DOG'}})
# The operators 'NULL', 'NOT_NULL', 'IN', 'CONTAINS', 'NOT_CONTAINS', 'NE'
# exist for ComparisonOperator and are allowed in filters, but not allowed
# in key conditions:
for op in ['IN', 'CONTAINS', 'NOT_CONTAINS', 'NE']:
with pytest.raises(ClientError, match='ValidationException'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': op}})
for op in ['NULL', 'NOT_NULL']:
with pytest.raises(ClientError, match='ValidationException'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [], 'ComparisonOperator': op}})
# The "EQ" operator requires exactly one argument, not more, not less.
def test_key_conditions_partition_one(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
with pytest.raises(ClientError, match='ValidationException.*EQ'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [], 'ComparisonOperator': 'EQ'}})
with pytest.raises(ClientError, match='ValidationException.*EQ'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p, p], 'ComparisonOperator': 'EQ'}})
# A KeyCondition must define the partition key p, it can't be just on the
# sort key c.
def test_key_conditions_partition_missing(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
with pytest.raises(ClientError, match='ValidationException.* miss'):
full_query(table, KeyConditions={
'c' : {'AttributeValueList': [3], 'ComparisonOperator': 'EQ'}})
# The following tests test the various condition operators on the sort key,
# for a *numeric* sort key:
# Test the EQ operator on a numeric sort key:
def test_key_conditions_num_eq(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [3], 'ComparisonOperator': 'EQ'}})
expected_items = [item for item in items if item['c'] == 3]
assert(got_items == expected_items)
# Test the LT operator on a numeric sort key:
def test_key_conditions_num_lt(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [5], 'ComparisonOperator': 'LT'}})
expected_items = [item for item in items if item['c'] < 5]
assert(got_items == expected_items)
# Test the LE operator on a numeric sort key:
def test_key_conditions_num_le(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [5], 'ComparisonOperator': 'LE'}})
expected_items = [item for item in items if item['c'] <= 5]
assert(got_items == expected_items)
# Test the GT operator on a numeric sort key:
def test_key_conditions_num_gt(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [5], 'ComparisonOperator': 'GT'}})
expected_items = [item for item in items if item['c'] > 5]
assert(got_items == expected_items)
# Test the GE operator on a numeric sort key:
def test_key_conditions_num_ge(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [5], 'ComparisonOperator': 'GE'}})
expected_items = [item for item in items if item['c'] >= 5]
assert(got_items == expected_items)
# Test the BETWEEN operator on a numeric sort key:
def test_key_conditions_num_between(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [4, 7], 'ComparisonOperator': 'BETWEEN'}})
expected_items = [item for item in items if item['c'] >= 4 and item['c'] <= 7]
assert(got_items == expected_items)
# The BEGINS_WITH operator does *not* work on a numeric sort key (it only
# works on strings or bytes - we'll check this later):
def test_key_conditions_num_begins(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
with pytest.raises(ClientError, match='ValidationException.*BEGINS_WITH'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [3], 'ComparisonOperator': 'BEGINS_WITH'}})
# The following tests test the various condition operators on the sort key,
# for a *string* sort key:
# Test the EQ operator on a string sort key:
def test_key_conditions_str_eq(test_table_ss_with_sorted_partition):
table, p, items = test_table_ss_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['003'], 'ComparisonOperator': 'EQ'}})
expected_items = [item for item in items if item['c'] == '003']
assert(got_items == expected_items)
# Test the LT operator on a string sort key:
def test_key_conditions_str_lt(test_table_ss_with_sorted_partition):
table, p, items = test_table_ss_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['005'], 'ComparisonOperator': 'LT'}})
expected_items = [item for item in items if item['c'] < '005']
assert(got_items == expected_items)
# Test the LE operator on a string sort key:
def test_key_conditions_str_le(test_table_ss_with_sorted_partition):
table, p, items = test_table_ss_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['005'], 'ComparisonOperator': 'LE'}})
expected_items = [item for item in items if item['c'] <= '005']
assert(got_items == expected_items)
# Test the GT operator on a string sort key:
def test_key_conditions_str_gt(test_table_ss_with_sorted_partition):
table, p, items = test_table_ss_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['005'], 'ComparisonOperator': 'GT'}})
expected_items = [item for item in items if item['c'] > '005']
assert(got_items == expected_items)
# Test the GE operator on a string sort key:
def test_key_conditions_str_ge(test_table_ss_with_sorted_partition):
table, p, items = test_table_ss_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['005'], 'ComparisonOperator': 'GE'}})
expected_items = [item for item in items if item['c'] >= '005']
assert(got_items == expected_items)
# Test the BETWEEN operator on a string sort key:
def test_key_conditions_str_between(test_table_ss_with_sorted_partition):
table, p, items = test_table_ss_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['004', '007'], 'ComparisonOperator': 'BETWEEN'}})
expected_items = [item for item in items if item['c'] >= '004' and item['c'] <= '007']
assert(got_items == expected_items)
# Test the BEGINS_WITH operator on a string sort key:
def test_key_conditions_str_begins_with(test_table_ss_with_sorted_partition):
table, p, items = test_table_ss_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['00'], 'ComparisonOperator': 'BEGINS_WITH'}})
expected_items = [item for item in items if item['c'].startswith('00')]
assert(got_items == expected_items)
# This tests BEGINS_WITH with some more elaborate characters
def test_begins_with_more_str(dynamodb, test_table_ss):
p = random_string()
items = [{'p': p, 'c': sort_key, 'str': 'a'} for sort_key in [u'ÿÿÿ', u'cÿbÿ', u'cÿbÿÿabg'] ]
with test_table_ss.batch_writer() as batch:
for item in items:
batch.put_item(item)
got_items = full_query(test_table_ss, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [u'ÿÿ'], 'ComparisonOperator': 'BEGINS_WITH'}})
assert sorted([d['c'] for d in got_items]) == sorted([d['c'] for d in items if d['c'].startswith(u'ÿÿ')])
got_items = full_query(test_table_ss, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [u'cÿbÿ'], 'ComparisonOperator': 'BEGINS_WITH'}})
assert sorted([d['c'] for d in got_items]) == sorted([d['c'] for d in items if d['c'].startswith(u'cÿbÿ')])
# The following tests test the various condition operators on the sort key,
# for a *bytes* sort key:
# Test the EQ operator on a bytes sort key:
def test_key_conditions_bytes_eq(test_table_sb_with_sorted_partition):
table, p, items = test_table_sb_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray('003', 'ascii')], 'ComparisonOperator': 'EQ'}})
expected_items = [item for item in items if item['c'] == bytearray('003', 'ascii')]
assert(got_items == expected_items)
# Test the LT operator on a bytes sort key:
def test_key_conditions_bytes_lt(test_table_sb_with_sorted_partition):
table, p, items = test_table_sb_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray('005', 'ascii')], 'ComparisonOperator': 'LT'}})
expected_items = [item for item in items if item['c'] < bytearray('005', 'ascii')]
assert(got_items == expected_items)
# Test the LE operator on a bytes sort key:
def test_key_conditions_bytes_le(test_table_sb_with_sorted_partition):
table, p, items = test_table_sb_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray('005', 'ascii')], 'ComparisonOperator': 'LE'}})
expected_items = [item for item in items if item['c'] <= bytearray('005', 'ascii')]
assert(got_items == expected_items)
# Test the GT operator on a bytes sort key:
def test_key_conditions_bytes_gt(test_table_sb_with_sorted_partition):
table, p, items = test_table_sb_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray('005', 'ascii')], 'ComparisonOperator': 'GT'}})
expected_items = [item for item in items if item['c'] > bytearray('005', 'ascii')]
assert(got_items == expected_items)
# Test the GE operator on a bytes sort key:
def test_key_conditions_bytes_ge(test_table_sb_with_sorted_partition):
table, p, items = test_table_sb_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray('005', 'ascii')], 'ComparisonOperator': 'GE'}})
expected_items = [item for item in items if item['c'] >= bytearray('005', 'ascii')]
assert(got_items == expected_items)
# Test the BETWEEN operator on a bytes sort key:
def test_key_conditions_bytes_between(test_table_sb_with_sorted_partition):
table, p, items = test_table_sb_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray('004', 'ascii'), bytearray('007', 'ascii')], 'ComparisonOperator': 'BETWEEN'}})
expected_items = [item for item in items if item['c'] >= bytearray('004', 'ascii') and item['c'] <= bytearray('007', 'ascii')]
assert(got_items == expected_items)
# Test the BEGINS_WITH operator on a bytes sort key:
def test_key_conditions_bytes_begins_with(test_table_sb_with_sorted_partition):
table, p, items = test_table_sb_with_sorted_partition
got_items = full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray('00', 'ascii')], 'ComparisonOperator': 'BEGINS_WITH'}})
expected_items = [item for item in items if item['c'].startswith(bytearray('00', 'ascii'))]
assert(got_items == expected_items)
# Test the special case of the 0xFF character for which we have special handling
def test_key_conditions_bytes_begins_with_ff(dynamodb, test_table_sb):
p = random_string()
items = [{'p': p, 'c': sort_key, 'str': 'a'} for sort_key in [bytearray([1, 2,3]), bytearray([255, 3, 4]), bytearray([255, 255, 255])] ]
with test_table_sb.batch_writer() as batch:
for item in items:
batch.put_item(item)
got_items = full_query(test_table_sb, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray([255])], 'ComparisonOperator': 'BEGINS_WITH'}})
expected_items = [item for item in items if item['c'].startswith(bytearray([255]))]
assert(got_items == expected_items)
# Other ComparisonOperator values besides what we tested above may be supported by
# filters, but *not* by KeyConditions, not even on the sort keys:
def test_key_conditions_sort_unsupported(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
# The operators 'NULL', 'NOT_NULL', 'IN', 'CONTAINS', 'NOT_CONTAINS', 'NE'
# exist for ComparisonOperator and are allowed in filters, but not allowed
# in key conditions:
for op in ['IN', 'CONTAINS', 'NOT_CONTAINS', 'NE']:
with pytest.raises(ClientError, match='ValidationException'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [3], 'ComparisonOperator': op}})
for op in ['NULL', 'NOT_NULL']:
with pytest.raises(ClientError, match='ValidationException'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [], 'ComparisonOperator': op}})
# An unknown operator, e.g., "DOG", is also not allowed.
with pytest.raises(ClientError, match='ValidationException.*comparison'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [p], 'ComparisonOperator': 'DOG'}})
# We cannot have a key condition on a non-key column (not instead of a
# condition on the sort key, and not in addition to it).
def test_key_conditions_non_key(test_table_sn_with_sorted_partition):
table, p, items = test_table_sn_with_sorted_partition
with pytest.raises(ClientError, match='ValidationException'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'a' : {'AttributeValueList': [5], 'ComparisonOperator': 'EQ'}})
with pytest.raises(ClientError, match='ValidationException'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [3], 'ComparisonOperator': 'EQ'},
'a' : {'AttributeValueList': [5], 'ComparisonOperator': 'EQ'}})
# Test for trying to use a KeyCondition with an operand type which doesn't
# match the key type:
def test_key_conditions_wrong_type(test_table_ss_with_sorted_partition):
table, p, items = test_table_ss_with_sorted_partition
with pytest.raises(ClientError, match='ValidationException.* type'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [3], 'ComparisonOperator': 'EQ'}})
with pytest.raises(ClientError, match='ValidationException.* type'):
full_query(table, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [3], 'ComparisonOperator': 'EQ'}})
# Query and KeyConditions work also on a table with just a partition
# key and no sort key, although obviously it isn't very useful (for such
# tables, Query is just an elaborate way to do a GetItem).
def test_key_conditions_hash_only_s(test_table_s):
p = random_string()
item = {'p': p, 'val': 'hello'}
test_table_s.put_item(Item=item)
got_items = full_query(test_table_s, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'}})
assert(got_items == [item])
def test_key_conditions_hash_only_b(test_table_b):
p = random_bytes()
item = {'p': p, 'val': 'hello'}
test_table_b.put_item(Item=item)
got_items = full_query(test_table_b, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'}})
assert(got_items == [item])
# Reproduces SCYLLADB-1575
@pytest.mark.xfail(reason="SCYLLADB-1575")
def test_key_conditions_hash_only_n(test_table_n):
p = 1000
item = {'p': p, 'val': 'hello'}
test_table_n.put_item(Item=item)
got_items = full_query(test_table_n, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'}})
assert(got_items == [item])
# Decimal('1e3') is serialized by boto3 as "1E+3" on the wire, a
# different representation of the same number 1000.
got_items = full_query(test_table_n, KeyConditions={
'p' : {'AttributeValueList': [Decimal('1e3')], 'ComparisonOperator': 'EQ'}})
assert(got_items == [item])
# Demonstrate that issue #6573 was not a bug for KeyConditions: binary
# strings are ordered as unsigned bytes, i.e., byte 128 comes after 127,
# not as signed bytes.
# Test the five ordering operators: LT, LE, GT, GE, BETWEEN.
def test_key_conditions_unsigned_bytes(test_table_sb):
p = random_string()
items = [{'p': p, 'c': bytearray([i])} for i in range(126,129)]
with test_table_sb.batch_writer() as batch:
for item in items:
batch.put_item(item)
got_items = full_query(test_table_sb, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray([127])], 'ComparisonOperator': 'LT'}})
expected_items = [item for item in items if item['c'] < bytearray([127])]
assert(got_items == expected_items)
got_items = full_query(test_table_sb, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray([127])], 'ComparisonOperator': 'LE'}})
expected_items = [item for item in items if item['c'] <= bytearray([127])]
assert(got_items == expected_items)
got_items = full_query(test_table_sb, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray([127])], 'ComparisonOperator': 'GT'}})
expected_items = [item for item in items if item['c'] > bytearray([127])]
assert(got_items == expected_items)
got_items = full_query(test_table_sb, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray([127])], 'ComparisonOperator': 'GE'}})
expected_items = [item for item in items if item['c'] >= bytearray([127])]
assert(got_items == expected_items)
got_items = full_query(test_table_sb, KeyConditions={
'p' : {'AttributeValueList': [p], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': [bytearray([127]), bytearray([128])], 'ComparisonOperator': 'BETWEEN'}})
expected_items = [item for item in items if item['c'] >= bytearray([127]) and item['c'] <= bytearray([128])]
print(expected_items)
assert(got_items == expected_items)
# The following is an older test we had, and is probably no longer needed.
# It tests one limited use case for KeyConditions. It uses filled_test_table
# (the one we also use in test_scan.py) instead of the fixtures defined in this
# file. It also uses Boto3's paginator instead of our full_query utility.
def test_query_basic_restrictions(dynamodb, filled_test_table):
test_table, items = filled_test_table
paginator = dynamodb.meta.client.get_paginator('query')
# EQ
got_items = []
for page in paginator.paginate(TableName=test_table.name, KeyConditions={
'p' : {'AttributeValueList': ['long'], 'ComparisonOperator': 'EQ'}
}):
got_items += page['Items']
print(got_items)
assert multiset([item for item in items if item['p'] == 'long']) == multiset(got_items)
# LT
got_items = []
for page in paginator.paginate(TableName=test_table.name, KeyConditions={
'p' : {'AttributeValueList': ['long'], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['12'], 'ComparisonOperator': 'LT'}
}):
got_items += page['Items']
print(got_items)
assert multiset([item for item in items if item['p'] == 'long' and item['c'] < '12']) == multiset(got_items)
# LE
got_items = []
for page in paginator.paginate(TableName=test_table.name, KeyConditions={
'p' : {'AttributeValueList': ['long'], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['14'], 'ComparisonOperator': 'LE'}
}):
got_items += page['Items']
print(got_items)
assert multiset([item for item in items if item['p'] == 'long' and item['c'] <= '14']) == multiset(got_items)
# GT
got_items = []
for page in paginator.paginate(TableName=test_table.name, KeyConditions={
'p' : {'AttributeValueList': ['long'], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['15'], 'ComparisonOperator': 'GT'}
}):
got_items += page['Items']
print(got_items)
assert multiset([item for item in items if item['p'] == 'long' and item['c'] > '15']) == multiset(got_items)
# GE
got_items = []
for page in paginator.paginate(TableName=test_table.name, KeyConditions={
'p' : {'AttributeValueList': ['long'], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['14'], 'ComparisonOperator': 'GE'}
}):
got_items += page['Items']
print(got_items)
assert multiset([item for item in items if item['p'] == 'long' and item['c'] >= '14']) == multiset(got_items)
# BETWEEN
got_items = []
for page in paginator.paginate(TableName=test_table.name, KeyConditions={
'p' : {'AttributeValueList': ['long'], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['155', '164'], 'ComparisonOperator': 'BETWEEN'}
}):
got_items += page['Items']
print(got_items)
assert multiset([item for item in items if item['p'] == 'long' and item['c'] >= '155' and item['c'] <= '164']) == multiset(got_items)
# BEGINS_WITH
got_items = []
for page in paginator.paginate(TableName=test_table.name, KeyConditions={
'p' : {'AttributeValueList': ['long'], 'ComparisonOperator': 'EQ'},
'c' : {'AttributeValueList': ['11'], 'ComparisonOperator': 'BEGINS_WITH'}
}):
print([item for item in items if item['p'] == 'long' and item['c'].startswith('11')])
got_items += page['Items']
print(got_items)
assert multiset([item for item in items if item['p'] == 'long' and item['c'].startswith('11')]) == multiset(got_items)