dynamo-browse/internal/dynamo-browse/models/queryexpr/is.go

173 lines
3.9 KiB
Go
Raw Normal View History

package queryexpr
import (
"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"
"github.com/lmika/audax/internal/dynamo-browse/models/attrutils"
"reflect"
"strings"
)
type isTypeInfo struct {
isAny bool
attributeType expression.DynamoDBAttributeType
goType reflect.Type
}
var validIsTypeNames = map[string]isTypeInfo{
"ANY": {isAny: true},
"B": {
attributeType: expression.Binary,
goType: reflect.TypeOf(&types.AttributeValueMemberB{}),
},
"BOOL": {
attributeType: expression.Boolean,
goType: reflect.TypeOf(&types.AttributeValueMemberBOOL{}),
},
"S": {
attributeType: expression.String,
goType: reflect.TypeOf(&types.AttributeValueMemberS{}),
},
"N": {
attributeType: expression.Number,
goType: reflect.TypeOf(&types.AttributeValueMemberN{}),
},
"NULL": {
attributeType: expression.Null,
goType: reflect.TypeOf(&types.AttributeValueMemberNULL{}),
},
"L": {
attributeType: expression.List,
goType: reflect.TypeOf(&types.AttributeValueMemberL{}),
},
"M": {
attributeType: expression.Map,
goType: reflect.TypeOf(&types.AttributeValueMemberM{}),
},
"BS": {
attributeType: expression.BinarySet,
goType: reflect.TypeOf(&types.AttributeValueMemberBS{}),
},
"NS": {
attributeType: expression.NumberSet,
goType: reflect.TypeOf(&types.AttributeValueMemberNS{}),
},
"SS": {
attributeType: expression.StringSet,
goType: reflect.TypeOf(&types.AttributeValueMemberSS{}),
},
}
func (a *astIsOp) evalToIR(info *models.TableInfo) (irAtom, error) {
leftIR, err := a.Ref.evalToIR(info)
if err != nil {
return nil, err
}
if a.Value == nil {
return leftIR, nil
}
nameIR, isNameIR := leftIR.(irNamePath)
if !isNameIR {
return nil, OperandNotANameError(a.Ref.String())
}
rightIR, err := a.Value.evalToIR(info)
if err != nil {
return nil, err
}
valueIR, isValueIR := rightIR.(irValue)
if !isValueIR {
return nil, ValueMustBeLiteralError{}
}
strValue, isStringValue := valueIR.goValue().(string)
if !isStringValue {
return nil, ValueMustBeStringError{}
}
typeInfo, isValidType := validIsTypeNames[strings.ToUpper(strValue)]
if !isValidType {
return nil, InvalidTypeForIsError{TypeName: strValue}
}
var ir = irIs{name: nameIR, typeInfo: typeInfo}
if a.HasNot {
if typeInfo.isAny {
ir.hasNot = true
} else {
return &irBoolNot{atom: ir}, nil
}
}
return ir, nil
}
func (a *astIsOp) evalItem(item models.Item) (types.AttributeValue, error) {
ref, err := a.Ref.evalItem(item)
if err != nil {
return nil, err
}
if a.Value == nil {
return ref, nil
}
expTypeVal, err := a.Value.evalItem(item)
if err != nil {
return nil, err
}
str, canToStr := attrutils.AttributeToString(expTypeVal)
if !canToStr {
return nil, ValueMustBeStringError{}
}
typeInfo, hasTypeInfo := validIsTypeNames[strings.ToUpper(str)]
if !hasTypeInfo {
return nil, InvalidTypeForIsError{TypeName: str}
}
var resultOfIs bool
if typeInfo.isAny {
resultOfIs = ref != nil
} else {
refType := reflect.TypeOf(ref)
resultOfIs = typeInfo.goType.AssignableTo(refType)
}
if a.HasNot {
resultOfIs = !resultOfIs
}
return &types.AttributeValueMemberBOOL{Value: resultOfIs}, nil
}
func (a *astIsOp) String() string {
var sb strings.Builder
sb.WriteString(a.Ref.String())
if a.Value != nil {
sb.WriteString(" is ")
if a.HasNot {
sb.WriteString("not ")
}
sb.WriteString(a.Value.String())
}
return sb.String()
}
type irIs struct {
name nameIRAtom
hasNot bool
typeInfo isTypeInfo
}
func (i irIs) calcQueryForScan(info *models.TableInfo) (expression.ConditionBuilder, error) {
nb := i.name.calcName(info)
if i.typeInfo.isAny {
if i.hasNot {
return expression.AttributeNotExists(nb), nil
}
return expression.AttributeExists(nb), nil
}
return expression.AttributeType(nb, i.typeInfo.attributeType), nil
}