alternator: nicer error message for integer overflow in list index
In the DynamoDB API, when "a" is a list attribute, a[999] returns the
1000th element. But if the list isn't that long (e.g., it only has 5
elements), a[999] returns nothing - it's not an error.
But it turns out that when the index is so long that it can't even be
parsed as an integer, e.g., 99999999999999, DynamoDB does report an
error:
Invalid ProjectionExpression: List index is not within the
allowable range; index: [99999999999999]
Before this patch, Alternator also returned an error in this case,
with the right type (ValidationException), but with a strange low-level
error text:
Failed parsing ProjectionExpression 'a[99999999999999]':
std::out_of_range (stoi)
The problem was that the code (in alternator/expressions.g) ran stoi()
without converting its std::out_of_range exception to a better user-facing
message. We do this in this patch, and the error message now looks like:
Failed parsing ProjectionExpression 'a[99999999999999]':
list index out of integer range
This patch also includes a test reproducing this error, which passes
on DynamDB and on Alternator it fails before this patch and passes with
the patch.
Fixes #25947
Signed-off-by: Nadav Har'El <nyh@scylladb.com>
Closes scylladb/scylladb#25951
This commit is contained in:
committed by
Pavel Emelyanov
parent
208d3986a7
commit
b4e3d4ac2f
@@ -184,7 +184,13 @@ path_component: NAME | NAMEREF;
|
||||
path returns [parsed::path p]:
|
||||
root=path_component { $p.set_root($root.text); }
|
||||
( '.' name=path_component { $p.add_dot($name.text); }
|
||||
| '[' INTEGER ']' { $p.add_index(std::stoi($INTEGER.text)); }
|
||||
| '[' INTEGER ']' {
|
||||
try {
|
||||
$p.add_index(std::stoi($INTEGER.text));
|
||||
} catch(std::out_of_range&) {
|
||||
throw expressions_syntax_error("list index out of integer range");
|
||||
}
|
||||
}
|
||||
)*;
|
||||
|
||||
/* See comment above why the "depth" counter was needed here */
|
||||
|
||||
@@ -347,3 +347,34 @@ def test_projection_expression_path_nesting_levels(test_table_s):
|
||||
# nesting levels: 33".
|
||||
with pytest.raises(ClientError, match='ValidationException.*nesting levels'):
|
||||
test_table_s.get_item(Key={'p': p}, ConsistentRead=True, ProjectionExpression='a'+('.b'*32))
|
||||
|
||||
# Above we already checked different cases of reading individual elements
|
||||
# from a list - the expression a[i]. The following test exercises these
|
||||
# list indexes more rigourously, including testing what happens when the
|
||||
# index overflows an integer (reproducing #25947).
|
||||
def test_projection_expression_list_index(test_table_s):
|
||||
p = random_string()
|
||||
test_table_s.put_item(Item={'p': p, 'a': [7, 42]})
|
||||
# a[0] and a[1] return the elements from the list, as expected
|
||||
# (note that a[i] actually returns an array with a single element a[i])
|
||||
assert {'a': [7]} == test_table_s.get_item(Key={'p': p}, ConsistentRead=True, ProjectionExpression='a[0]')['Item']
|
||||
assert {'a': [42]} == test_table_s.get_item(Key={'p': p}, ConsistentRead=True, ProjectionExpression='a[1]')['Item']
|
||||
# If the index is beyond the length of the array, such as a[2] or a[999],
|
||||
# we expect to get back an empty Item - not an error, and not missing Item.
|
||||
assert {} == test_table_s.get_item(Key={'p': p}, ConsistentRead=True, ProjectionExpression='a[2]')['Item']
|
||||
assert {} == test_table_s.get_item(Key={'p': p}, ConsistentRead=True, ProjectionExpression='a[999]')['Item']
|
||||
# If the index is so high that it can't be parsed as an integer, it isn't
|
||||
# silently ignored like 999 above, but causes a parse error. DynamoDB
|
||||
# reports: "Invalid ProjectionExpression: List index is not within the
|
||||
# allowable range; index: [99999999999999]". After fixing #25947,
|
||||
# Alternator reports: "Failed parsing ProjectionExpression
|
||||
# 'a[99999999999999]': list index out of integer range".
|
||||
with pytest.raises(ClientError, match='ValidationException.*ProjectionExpression.*index'):
|
||||
test_table_s.get_item(Key={'p': p}, ConsistentRead=True, ProjectionExpression='a[99999999999999]')['Item']
|
||||
# Trying to use a negative number as an index, like a[-1], is just a
|
||||
# syntax error - the parser expects to see digits, not "-".
|
||||
with pytest.raises(ClientError, match='ValidationException.*ProjectionExpression.*[Ss]yntax error'):
|
||||
test_table_s.get_item(Key={'p': p}, ConsistentRead=True, ProjectionExpression='a[-1]')['Item']
|
||||
# A completely missing index - a[] - is also a syntax error:
|
||||
with pytest.raises(ClientError, match='ValidationException.*ProjectionExpression.*[Ss]yntax error'):
|
||||
test_table_s.get_item(Key={'p': p}, ConsistentRead=True, ProjectionExpression='a[]')['Item']
|
||||
|
||||
Reference in New Issue
Block a user