Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2078 - allow nested list indices in expressions #3021

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changelog/2cca370d-5f01-451c-979b-b82c55dcf3ec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "2cca370d-5f01-451c-979b-b82c55dcf3ec",
"type": "bugfix",
"description": "allow nested list indices in expressions",
"collapse": false,
"modules": [
"feature/dynamodb/expression"
]
}
64 changes: 53 additions & 11 deletions feature/dynamodb/expression/operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,25 @@ func (nb NameBuilder) AppendName(field NameBuilder) NameBuilder {
if len(nb.names) != 0 && len(field.names) != 0 {
lastLeftName := len(nb.names) - 1
firstRightName := lastLeftName + 1
if v := names[firstRightName]; len(v) > 0 && v[0] == '[' {
if end := strings.Index(v, "]"); end != -1 {
names[lastLeftName] += v[0 : end+1]
names[firstRightName] = v[end+1:]
// Remove the name if it is empty after moving the index.
if len(names[firstRightName]) == 0 {
copy(names[firstRightName:], names[firstRightName+1:])
names[len(names)-1] = ""
names = names[:len(names)-1]
}

v := names[firstRightName]
end := strings.Index(v, "]")

for len(v) > 0 && v[0] == '[' && end != -1 {
names[lastLeftName] += v[0 : end+1]
names[firstRightName] = v[end+1:]

// Remove the name if it is empty after moving the index.
if len(names[firstRightName]) == 0 {
copy(names[firstRightName:], names[firstRightName+1:])
names[len(names)-1] = ""
names = names[:len(names)-1]

break
}

v = v[end+1:]
end = strings.Index(v, "]")
}
}

Expand Down Expand Up @@ -603,10 +611,44 @@ func (nb NameBuilder) BuildOperand() (Operand, error) {
return Operand{}, newInvalidParameterError("BuildOperand", "NameBuilder")
}

if idx := strings.Index(word, "]"); idx != -1 && idx != len(word)-1 {
numOpenBrackets := strings.Count(word, "[")
numCloseBrackets := strings.Count(word, "]")

// mismatched brackets
if numOpenBrackets != numCloseBrackets {
return Operand{}, newInvalidParameterError("BuildOperand", "NameBuilder")
}

openPositions := make([]int, 0, numOpenBrackets)
closePositions := make([]int, 0, numCloseBrackets)

for i, char := range word {
if char == '[' {
openPositions = append(openPositions, i)
} else if char == ']' {
closePositions = append(closePositions, i)
}
}

for idx := range closePositions {
openPosition := openPositions[idx]
closePosition := closePositions[idx]
if openPosition > closePosition {
return Operand{}, newInvalidParameterError("BuildOperand", "NameBuilder")
}

part := word[openPosition+1 : closePosition]
if len(part) == 0 {
return Operand{}, newInvalidParameterError("BuildOperand", "NameBuilder")
}

for _, c := range []byte(part) {
if c < '0' || c > '9' {
return Operand{}, newInvalidParameterError("BuildOperand", "NameBuilder")
}
}
}

if word[len(word)-1] == ']' {
for j, char := range word {
if char == '[' {
Expand Down
102 changes: 102 additions & 0 deletions feature/dynamodb/expression/operand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,108 @@ func TestBuildOperand(t *testing.T) {
expected: exprNode{},
err: invalidName,
},
{
name: "no split name name with nested indices",
input: NameNoDotSplit("foo.bar[0][0]"),
expected: exprNode{
names: []string{"foo.bar"},
fmtExpr: "$n[0][0]",
},
},
{
name: "name with nested indices and property",
input: Name("foo[1][2].bar"),
expected: exprNode{
names: []string{"foo", "bar"},
fmtExpr: "$n[1][2].$n",
},
},
{
name: "names with nested indices",
input: Name("foo[1][2].bar[3][4]"),
expected: exprNode{
names: []string{"foo", "bar"},
fmtExpr: "$n[1][2].$n[3][4]",
},
},
{
name: "very log name with nested indices",
input: Name("foo[1][2][3][4][5][6][7][8][9][10].bar[11][12][13][14][15][16][17][18]"),
expected: exprNode{
names: []string{"foo", "bar"},
fmtExpr: "$n[1][2][3][4][5][6][7][8][9][10].$n[11][12][13][14][15][16][17][18]",
},
},
{
name: "very log name with nested indices",
input: Name("foo[1][2][3][4][5][6][7][8][9][10].bar[11][12][13][14][15][16][17][18]"),
expected: exprNode{
names: []string{"foo", "bar"},
fmtExpr: "$n[1][2][3][4][5][6][7][8][9][10].$n[11][12][13][14][15][16][17][18]",
},
},
{
name: "invalid name when bracket is missing",
input: Name("foo["),
expected: exprNode{},
err: invalidName,
},
{
name: "invalid name when bracket is missing",
input: Name("foo]"),
expected: exprNode{},
err: invalidName,
},
{
name: "invalid name when ending with dot",
input: Name("foo."),
expected: exprNode{},
err: invalidName,
},
{
name: "invalid name when alpha index",
input: Name("foo[a]"),
expected: exprNode{},
err: invalidName,
},
{
name: "invalid name when weird brackets",
input: Name("foo]1["),
expected: exprNode{},
err: invalidName,
},
{
name: "no split name append name with nested list index",
input: NameNoDotSplit("foo.bar").
AppendName(Name("foo.bar")).
AppendName(Name("[0][1]")).
AppendName(Name("abc123")),
expected: exprNode{
names: []string{"foo.bar", "foo", "bar", "abc123"},
fmtExpr: "$n.$n.$n[0][1].$n",
},
},
{
name: "no split name append name with bad nested list index",
input: NameNoDotSplit("foo.bar").
AppendName(Name("foo.bar")).
AppendName(Name("[0][a]")).
AppendName(Name("abc123")),
expected: exprNode{},
err: invalidName,
},
{
name: "bad input left bracket only",
input: Name("foo").AppendName(Name("[")),
expected: exprNode{},
err: invalidName,
},
{
name: "bad input right bracket only",
input: Name("foo").AppendName(Name("]")),
expected: exprNode{},
err: invalidName,
},
}

for _, c := range cases {
Expand Down