Skip to content

Commit

Permalink
#2078 - allow nested list indices in expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
raduendv committed Feb 27, 2025
1 parent ef40d0c commit 5cf0511
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 9 deletions.
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": "#2078 - allow nested list indices in expressions",
"collapse": true,
"modules": [
"feature/dynamodb/expression"
]
}
56 changes: 47 additions & 9 deletions feature/dynamodb/expression/operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,17 @@ func (nb NameBuilder) AppendName(field NameBuilder) NameBuilder {
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]
for v != "" {
if end := strings.Index(v, "]"); end != -1 || end == len(v)-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 = v[end+1:]
}
}
}
Expand Down Expand Up @@ -603,10 +606,45 @@ 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) {
c -= '0'
if c < 0 || c > 9 {
return Operand{}, newInvalidParameterError("BuildOperand", "NameBuilder")
}
}
}

if word[len(word)-1] == ']' {
for j, char := range word {
if char == '[' {
Expand Down
90 changes: 90 additions & 0 deletions feature/dynamodb/expression/operand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,96 @@ 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,
},
}

for _, c := range cases {
Expand Down

0 comments on commit 5cf0511

Please sign in to comment.