Added sub references to the query expression (#46)
These are the `thing[subref]` construct. Subrefs can either be a string or an integer. At the moment, multiple sub references, such as `thing[1][2][3]` doesn't work. This is because the SDK does not properly handle this when creating the actual expression.
This commit is contained in:
parent
348251c1cf
commit
7caf905c82
20
go.mod
20
go.mod
|
@ -5,12 +5,12 @@ go 1.18
|
|||
require (
|
||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5
|
||||
github.com/asdine/storm v2.1.2+incompatible
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.5
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.4
|
||||
github.com/aws/aws-sdk-go-v2/config v1.13.1
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.8.0
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.9.4
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.10
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.15.7
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.12
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.39
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.18.3
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.24.0
|
||||
github.com/brianvoe/gofakeit/v6 v6.15.0
|
||||
|
@ -37,16 +37,16 @@ require (
|
|||
github.com/Sereal/Sereal v0.0.0-20220220040404-e0d1e550e879 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 // indirect
|
||||
github.com/aws/smithy-go v1.11.3 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
|
||||
github.com/cloudcmds/tamarin v1.0.0 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
|
|
20
go.sum
20
go.sum
|
@ -29,34 +29,52 @@ github.com/aws/aws-sdk-go-v2 v1.13.0/go.mod h1:L6+ZpqHaLbAaxsqV0L4cvxZY7QupWJB4f
|
|||
github.com/aws/aws-sdk-go-v2 v1.16.1/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.5 h1:Ah9h1TZD9E2S1LzHpViBO3Jz9FPL5+rmflmb8hXirtI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.5/go.mod h1:Wh7MEsmEApyL5hrWzpDkba4gwAPc5/piwLVLFnCxp48=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.13.1 h1:yLv8bfNoT4r+UvUKQKqRtdnvuWGMK5a82l4ru9Jvnuo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.13.1/go.mod h1:Ba5Z4yL/UGbjQUzsiaN378YobhFo0MLfueXGiOsYtEs=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.8.0 h1:8Ow0WcyDesGNL0No11jcgb1JAtE+WtubqXjgxau+S0o=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.8.0/go.mod h1:gnMo58Vwx3Mu7hj1wpcG8DI0s57c9o42UQ6wgTQT5to=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.9.4 h1:EoyeSOfbSuKh+bQIDoZaVJjON6PF+dsSn5w1RhIpMD0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.9.4/go.mod h1:bfCL7OwZS6owS06pahfGxhcgpLWj2W1sQASoYRuenag=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.12 h1:ama2cD4WaH6+8Gq/M/g+ZumPmmqCyanr+6Sm+iJVxfA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.12/go.mod h1:tPnUO5mS3JThpwfq4Q8iPd745s7yh6fGPqDUEBw+Wv4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.10 h1:IBIZfpnWCTTQhH/bMvDcCMw10BtLBPYO30Ev8MLXMTY=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.10/go.mod h1:RL7aJOwlWj2N6wkE4nKR1S5M4iGph+xSu7JovwNYpyU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.39 h1:PhgfvgqwMFQKwOcxLV7V3lNDVnR3ZUWzoB6T9oCFpR4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.39/go.mod h1:/GkvC7uHpK50ilKkKx9I2gZiI/ieZbKjS2aah1rT9uE=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 h1:NITDuUZO34mqtOwFWZiXo7yAHj7kf+XPE+EiKuCBNUI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0/go.mod h1:I6/fHT/fH460v09eg2gVrd8B/IqskhNdpcLH0WNO3QI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4/go.mod h1:XHgQ7Hz2WY2GAn//UXHofLfPXWh+s62MbMOijrg12Lw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.8/go.mod h1:LnTQMTqbKsbtt+UI5+wPsB7jedW+2ZgozoPG8k6cMxg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12 h1:Zt7DDk5V7SyQULUUwIKzsROtVzp/kVvcz15uQx/Tkow=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12/go.mod h1:Afj/U8svX6sJ77Q+FPWMzabJ9QjbwP32YlopgKALUpg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0/go.mod h1:BsCSJHx5DnDXIrOcqB8KN1/B+hXLG/bi4Y6Vjcx/x9E=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.2/go.mod h1:1x4ZP3Z8odssdhuLI+/1Tqw6Pt/VAaP4Tr8EUxHvPXE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 h1:eeXdGVtXEe+2Jc49+/vAzna3FAQnUD4AagAw8tzbmfc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6/go.mod h1:FwpAKI+FBPIELJIdmQzlLtRe8LQSOreMcM2wBsPMvvc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 h1:ixotxbfTCFpqbuwFv/RcZwyzhkxPSYDYEMcj4niB5Uk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5/go.mod h1:R3sWUqPcfXSiF/LSFJhjyJmpg9uV6yP2yv3YZZjldVI=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.15.7 h1:Ls6kDGWNr3wxE8JypXgTTonHpQ1eRVCGNqaFHY2UASw=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.15.7/go.mod h1:+v2jeT4/39fCXUQ0ZfHQHMMiJljnmiuj16F03uAd9DY=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.18.3 h1:MxOpCZ+o9+AIeQHi2ocW7H4D7p0LhEkmetETVvDnkvg=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.18.3/go.mod h1:nkpC9xkh+3vdxmhqN8Ac10pgV14DsJDLzUsV2CcS+44=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.13.7 h1:o2HKntJx3vr3y11NK58RA6tYKZKQo5PWWt/bs0rWR0U=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.13.7/go.mod h1:FAVtDKEl/8WxRDQ33e2fz16RO1t4zeEwWIU5kR29xXs=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.3 h1:B+bkmCnNJi194pu9aTtYUe8f4EPXafC+xfU+zciVxdg=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.3/go.mod h1:bRphLmXQD9Ux4jLcFEwyrWdmuPTj2Lh8VGl9wILuJII=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.2 h1:T/ywkX1ed+TsZVQccu/8rRJGxKZF/t0Ivgrb4MHTSeo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.2/go.mod h1:RnloUnyZ4KN9JStGY1LuQ7Wzqh7V0f8FinmRdHYtuaA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.6 h1:JGrc3+kkyr848/wpG2+kWuzHK3H4Fyxj2jnXj8ijQ/Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.6/go.mod h1:zwvTysbXES8GDwFcwCPB8NkC+bCdio1abH+E+BRe/xg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.22 h1:6zEryIiJOSk5/OcVHzkPDwzNBQ2atYCTShyA7TqkuxA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.22/go.mod h1:moeOz5SKfY0p6pNIChdPIQdfaUfWI67+OVe0/r6+aGY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 h1:4QAOB3KrvI1ApJK14sliGr3Ie2pjyvNypn/lfzDHfUw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0/go.mod h1:K/qPe6AP2TGYv4l6n7c88zh9jWBDf6nHhvg1fx/EWfU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0 h1:dzWS4r8E9bA0TesHM40FSAtedwpTVCuTsLI8EziSqyk=
|
||||
|
@ -71,6 +89,8 @@ github.com/aws/smithy-go v1.10.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiA
|
|||
github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
|
||||
github.com/aws/smithy-go v1.11.3 h1:DQixirEFM9IaKxX1olZ3ke3nvxRS2xMDteKIDWxozW8=
|
||||
github.com/aws/smithy-go v1.11.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/brianvoe/gofakeit/v6 v6.15.0 h1:lJPGJZ2/07TRGDazyTzD5b18N3y4tmmJpdhCUw18FlI=
|
||||
|
|
|
@ -55,8 +55,14 @@ type astIsOp struct {
|
|||
}
|
||||
|
||||
type astSubRef struct {
|
||||
Ref *astFunctionCall `parser:"@@"`
|
||||
Quals []string `parser:"('.' @Ident)*"`
|
||||
Ref *astFunctionCall `parser:"@@"`
|
||||
SubRefs []*astSubRefType `parser:"@@*"`
|
||||
//Quals []string `parser:"('.' @Ident)*"`
|
||||
}
|
||||
|
||||
type astSubRefType struct {
|
||||
DotQual string `parser:"'.' @Ident"`
|
||||
SubIndex *astExpr `parser:"| '[' @@ ']'"`
|
||||
}
|
||||
|
||||
type astFunctionCall struct {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package queryexpr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression"
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/lmika/audax/internal/dynamo-browse/models"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (dt *astRef) evalToIR(ctx *evalContext, info *models.TableInfo) (irAtom, error) {
|
||||
|
@ -43,7 +45,7 @@ func (a *astRef) String() string {
|
|||
|
||||
type irNamePath struct {
|
||||
name string
|
||||
quals []string
|
||||
quals []any
|
||||
}
|
||||
|
||||
func (i irNamePath) calcQueryForScan(info *models.TableInfo) (expression.ConditionBuilder, error) {
|
||||
|
@ -62,9 +64,16 @@ func (i irNamePath) keyName() string {
|
|||
}
|
||||
|
||||
func (i irNamePath) calcName(info *models.TableInfo) expression.NameBuilder {
|
||||
nb := expression.Name(i.name)
|
||||
var fullName strings.Builder
|
||||
fullName.WriteString(i.name)
|
||||
|
||||
for _, qual := range i.quals {
|
||||
nb = nb.AppendName(expression.Name(qual))
|
||||
switch v := qual.(type) {
|
||||
case string:
|
||||
fullName.WriteString("." + v)
|
||||
case int:
|
||||
fullName.WriteString(fmt.Sprintf("[%v]", qual))
|
||||
}
|
||||
}
|
||||
return nb
|
||||
return expression.NameNoDotSplit(fullName.String())
|
||||
}
|
||||
|
|
|
@ -34,6 +34,13 @@ func (n ValueNotAMapError) Error() string {
|
|||
return fmt.Sprintf("%v: name is not a map", strings.Join(n, "."))
|
||||
}
|
||||
|
||||
// ValueNotAListError is return if the given name is not a map
|
||||
type ValueNotAListError []string
|
||||
|
||||
func (n ValueNotAListError) Error() string {
|
||||
return fmt.Sprintf("%v: name is not a list", strings.Join(n, "."))
|
||||
}
|
||||
|
||||
// ValuesNotComparable indicates that two values are not comparable
|
||||
type ValuesNotComparable struct {
|
||||
Left, Right types.AttributeValue
|
||||
|
@ -123,3 +130,10 @@ type MissingPlaceholderError struct {
|
|||
func (e MissingPlaceholderError) Error() string {
|
||||
return "undefined placeholder '" + e.Placeholder + "'"
|
||||
}
|
||||
|
||||
type ValueNotUsableAsASubref struct {
|
||||
}
|
||||
|
||||
func (e ValueNotUsableAsASubref) Error() string {
|
||||
return "value cannot be used as a subref"
|
||||
}
|
||||
|
|
|
@ -111,6 +111,13 @@ func TestModExpr_Query(t *testing.T) {
|
|||
exprNameIsString(0, 0, "pk", "prefix"),
|
||||
exprNameIsString(1, 1, "sk", "another"),
|
||||
),
|
||||
|
||||
// Querying the index
|
||||
scanCase("when request pk is fixed",
|
||||
`pk="prefix"`,
|
||||
`#0 = :0`,
|
||||
exprNameIsString(0, 0, "pk", "prefix"),
|
||||
),
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
|
@ -271,17 +278,27 @@ func TestModExpr_Query(t *testing.T) {
|
|||
exprValueIsNumber(0, "131"),
|
||||
),
|
||||
|
||||
// Dots
|
||||
scanCase("with the dot", `this.value = "something"`, `#0.#1 = :0`,
|
||||
// Sub refs
|
||||
scanCase("with index", `this[2] = "something"`, `#0[2] = :0`,
|
||||
exprName(0, "this"),
|
||||
exprName(1, "value"),
|
||||
exprValueIsString(0, "something"),
|
||||
),
|
||||
scanCase("with multiple dots", `this.that.other.value = "else"`, `#0.#1.#2.#3 = :0`,
|
||||
exprName(0, "this"),
|
||||
exprName(1, "that"),
|
||||
exprName(2, "other"),
|
||||
exprName(3, "value"),
|
||||
scanCase("with the dot", `this.value = "something"`, `#0 = :0`,
|
||||
exprName(0, "this.value"),
|
||||
exprValueIsString(0, "something"),
|
||||
),
|
||||
/*
|
||||
scanCase("with multiple indices", `this[2][3] = "something"`, `#0[2][3] = :0`,
|
||||
exprName(0, "this"),
|
||||
exprValueIsString(0, "something"),
|
||||
),
|
||||
scanCase("with multiple indices with paren", `((this[2])[3])[4] = "something"`, `#0[2][3][4] = :0`,
|
||||
exprName(0, "this"),
|
||||
exprValueIsString(0, "something"),
|
||||
),
|
||||
*/
|
||||
scanCase("with multiple dots", `this.that.other.value = "else"`, `#0 = :0`,
|
||||
exprName(0, "this.that.other.value"),
|
||||
exprValueIsString(0, "else"),
|
||||
),
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@ package queryexpr
|
|||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||
"github.com/lmika/audax/internal/common/sliceutils"
|
||||
"github.com/lmika/audax/internal/dynamo-browse/models"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -11,7 +13,7 @@ func (r *astSubRef) evalToIR(ctx *evalContext, info *models.TableInfo) (irAtom,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(r.Quals) == 0 {
|
||||
if len(r.SubRefs) == 0 {
|
||||
return refIR, nil
|
||||
}
|
||||
|
||||
|
@ -21,9 +23,13 @@ func (r *astSubRef) evalToIR(ctx *evalContext, info *models.TableInfo) (irAtom,
|
|||
return nil, OperandNotANameError(r.String())
|
||||
}
|
||||
|
||||
quals := make([]string, 0)
|
||||
for _, sr := range r.Quals {
|
||||
quals = append(quals, sr)
|
||||
quals := make([]any, 0)
|
||||
for _, sr := range r.SubRefs {
|
||||
sv, err := sr.evalToStrOrInt(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
quals = append(quals, sv)
|
||||
}
|
||||
return irNamePath{name: namePath.name, quals: quals}, nil
|
||||
}
|
||||
|
@ -34,29 +40,52 @@ func (r *astSubRef) evalItem(ctx *evalContext, item models.Item) (types.Attribut
|
|||
return nil, err
|
||||
}
|
||||
|
||||
for i, qualName := range r.Quals {
|
||||
var hasV bool
|
||||
|
||||
mapRes, isMapRes := res.(*types.AttributeValueMemberM)
|
||||
if !isMapRes {
|
||||
return nil, ValueNotAMapError(append([]string{r.Ref.String()}, r.Quals[:i+1]...))
|
||||
}
|
||||
|
||||
res, hasV = mapRes.Value[qualName]
|
||||
if !hasV {
|
||||
return nil, nil
|
||||
}
|
||||
res, err = r.evalSubRefs(ctx, item, res, r.SubRefs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *astSubRef) evalSubRefs(ctx *evalContext, item models.Item, res types.AttributeValue, subRefs []*astSubRefType) (types.AttributeValue, error) {
|
||||
for i, sr := range subRefs {
|
||||
sv, err := sr.evalToStrOrInt(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch val := sv.(type) {
|
||||
case string:
|
||||
var hasV bool
|
||||
mapRes, isMapRes := res.(*types.AttributeValueMemberM)
|
||||
if !isMapRes {
|
||||
return nil, newValueNotAMapError(r, subRefs[:i+1])
|
||||
}
|
||||
|
||||
res, hasV = mapRes.Value[val]
|
||||
if !hasV {
|
||||
return nil, nil
|
||||
}
|
||||
case int:
|
||||
listRes, isMapRes := res.(*types.AttributeValueMemberL)
|
||||
if !isMapRes {
|
||||
return nil, newValueNotAListError(r, subRefs[:i+1])
|
||||
}
|
||||
|
||||
// TODO - deal with index properly
|
||||
res = listRes.Value[val]
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *astSubRef) canModifyItem(ctx *evalContext, item models.Item) bool {
|
||||
return r.Ref.canModifyItem(ctx, item)
|
||||
}
|
||||
|
||||
func (r *astSubRef) setEvalItem(ctx *evalContext, item models.Item, value types.AttributeValue) error {
|
||||
if len(r.Quals) == 0 {
|
||||
if len(r.SubRefs) == 0 {
|
||||
return r.Ref.setEvalItem(ctx, item, value)
|
||||
}
|
||||
|
||||
|
@ -65,23 +94,40 @@ func (r *astSubRef) setEvalItem(ctx *evalContext, item models.Item, value types.
|
|||
return err
|
||||
}
|
||||
|
||||
for i, key := range r.Quals {
|
||||
mapItem, isMapItem := parentItem.(*types.AttributeValueMemberM)
|
||||
if !isMapItem {
|
||||
return PathNotSettableError{}
|
||||
if len(r.SubRefs) > 1 {
|
||||
parentItem, err = r.evalSubRefs(ctx, item, parentItem, r.SubRefs[0:len(r.SubRefs)-1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sv, err := r.SubRefs[len(r.SubRefs)-1].evalToStrOrInt(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch val := sv.(type) {
|
||||
case string:
|
||||
mapRes, isMapRes := parentItem.(*types.AttributeValueMemberM)
|
||||
if !isMapRes {
|
||||
return newValueNotAMapError(r, r.SubRefs)
|
||||
}
|
||||
|
||||
if isLast := i == len(r.Quals)-1; isLast {
|
||||
mapItem.Value[key] = value
|
||||
} else {
|
||||
parentItem = mapItem.Value[key]
|
||||
mapRes.Value[val] = value
|
||||
case int:
|
||||
listRes, isMapRes := parentItem.(*types.AttributeValueMemberL)
|
||||
if !isMapRes {
|
||||
return newValueNotAListError(r, r.SubRefs)
|
||||
}
|
||||
|
||||
// TODO: handle indexes
|
||||
listRes.Value[val] = value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *astSubRef) deleteAttribute(ctx *evalContext, item models.Item) error {
|
||||
if len(r.Quals) == 0 {
|
||||
if len(r.SubRefs) == 0 {
|
||||
return r.Ref.deleteAttribute(ctx, item)
|
||||
}
|
||||
|
||||
|
@ -90,17 +136,51 @@ func (r *astSubRef) deleteAttribute(ctx *evalContext, item models.Item) error {
|
|||
return err
|
||||
}
|
||||
|
||||
for i, key := range r.Quals {
|
||||
mapItem, isMapItem := parentItem.(*types.AttributeValueMemberM)
|
||||
if !isMapItem {
|
||||
return PathNotSettableError{}
|
||||
/*
|
||||
for i, key := range r.Quals {
|
||||
mapItem, isMapItem := parentItem.(*types.AttributeValueMemberM)
|
||||
if !isMapItem {
|
||||
return PathNotSettableError{}
|
||||
}
|
||||
|
||||
if isLast := i == len(r.Quals)-1; isLast {
|
||||
delete(mapItem.Value, key)
|
||||
} else {
|
||||
parentItem = mapItem.Value[key]
|
||||
}
|
||||
}
|
||||
*/
|
||||
if len(r.SubRefs) > 1 {
|
||||
parentItem, err = r.evalSubRefs(ctx, item, parentItem, r.SubRefs[0:len(r.SubRefs)-1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sv, err := r.SubRefs[len(r.SubRefs)-1].evalToStrOrInt(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch val := sv.(type) {
|
||||
case string:
|
||||
mapRes, isMapRes := parentItem.(*types.AttributeValueMemberM)
|
||||
if !isMapRes {
|
||||
return newValueNotAMapError(r, r.SubRefs)
|
||||
}
|
||||
|
||||
if isLast := i == len(r.Quals)-1; isLast {
|
||||
delete(mapItem.Value, key)
|
||||
} else {
|
||||
parentItem = mapItem.Value[key]
|
||||
delete(mapRes.Value, val)
|
||||
case int:
|
||||
listRes, isMapRes := parentItem.(*types.AttributeValueMemberL)
|
||||
if !isMapRes {
|
||||
return newValueNotAListError(r, r.SubRefs)
|
||||
}
|
||||
|
||||
// TODO: handle indexes out of bounds
|
||||
oldList := listRes.Value
|
||||
newList := append([]types.AttributeValue{}, oldList[:val]...)
|
||||
newList = append(newList, oldList[val+1:]...)
|
||||
listRes.Value = newList
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -109,10 +189,67 @@ func (r *astSubRef) String() string {
|
|||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(r.Ref.String())
|
||||
for _, q := range r.Quals {
|
||||
sb.WriteRune('.')
|
||||
sb.WriteString(q)
|
||||
for _, q := range r.SubRefs {
|
||||
switch {
|
||||
case q.DotQual != "":
|
||||
sb.WriteRune('.')
|
||||
sb.WriteString(q.DotQual)
|
||||
case q.SubIndex != nil:
|
||||
sb.WriteRune('[')
|
||||
sb.WriteString(q.SubIndex.String())
|
||||
sb.WriteRune(']')
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (sr *astSubRefType) evalToStrOrInt(ctx *evalContext, item models.Item) (any, error) {
|
||||
if sr.DotQual != "" {
|
||||
return sr.DotQual, nil
|
||||
}
|
||||
|
||||
subEvalItem, err := sr.SubIndex.evalItem(ctx, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch v := subEvalItem.(type) {
|
||||
case *types.AttributeValueMemberS:
|
||||
return v.Value, nil
|
||||
case *types.AttributeValueMemberN:
|
||||
intVal, err := strconv.Atoi(v.Value)
|
||||
if err == nil {
|
||||
return intVal, nil
|
||||
}
|
||||
flVal, err := strconv.ParseFloat(v.Value, 64)
|
||||
if err == nil {
|
||||
return int(flVal), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return nil, ValueNotUsableAsASubref{}
|
||||
}
|
||||
|
||||
func (sr *astSubRefType) string() string {
|
||||
switch {
|
||||
case sr.DotQual != "":
|
||||
return sr.DotQual
|
||||
case sr.SubIndex != nil:
|
||||
return sr.SubIndex.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func newValueNotAMapError(r *astSubRef, subRefs []*astSubRefType) ValueNotAMapError {
|
||||
subRefStrings := sliceutils.Map(subRefs, func(srt *astSubRefType) string {
|
||||
return srt.string()
|
||||
})
|
||||
return ValueNotAMapError(append([]string{r.Ref.String()}, subRefStrings...))
|
||||
}
|
||||
|
||||
func newValueNotAListError(r *astSubRef, subRefs []*astSubRefType) ValueNotAListError {
|
||||
subRefStrings := sliceutils.Map(subRefs, func(srt *astSubRefType) string {
|
||||
return srt.string()
|
||||
})
|
||||
return ValueNotAListError(append([]string{r.Ref.String()}, subRefStrings...))
|
||||
}
|
||||
|
|
|
@ -4,6 +4,12 @@ type TableInfo struct {
|
|||
Name string
|
||||
Keys KeyAttribute
|
||||
DefinedAttributes []string
|
||||
GSIs []TableGSI
|
||||
}
|
||||
|
||||
type TableGSI struct {
|
||||
Name string
|
||||
Keys KeyAttribute
|
||||
}
|
||||
|
||||
func (ti *TableInfo) Equal(other *TableInfo) bool {
|
||||
|
|
Loading…
Reference in a new issue