diff --git a/node/engine/interpreter/extension.go b/node/engine/interpreter/extension.go index a3e35f5de..d94a7ee29 100644 --- a/node/engine/interpreter/extension.go +++ b/node/engine/interpreter/extension.go @@ -85,12 +85,11 @@ func initializeExtension(ctx context.Context, svc *common.Service, db sql.DB, i } for i, v := range a { - newVal, err := newValueWithSoftCast(v, method.Returns.Fields[i].Type) + newVal, ok, err := newValueWithSoftCast(v, method.Returns.Fields[i].Type) if err != nil { return err } - - if !newVal.Type().Equals(method.Returns.Fields[i].Type) { + if !ok { return fmt.Errorf(`%w: method "%s"."%s" returned a value of type %s, but expected %s. column: "%s"`, engine.ErrExtensionInvocation, alias, lowerName, newVal.Type(), method.Returns.Fields[i].Type, method.Returns.Fields[i].Name) } diff --git a/node/engine/interpreter/interpreter.go b/node/engine/interpreter/interpreter.go index f5f9812e5..09d67c822 100644 --- a/node/engine/interpreter/interpreter.go +++ b/node/engine/interpreter/interpreter.go @@ -683,10 +683,13 @@ func (i *baseInterpreter) call(ctx *common.EngineContext, db sql.DB, namespace, } for i, arg := range args { - val, err := newValueWithSoftCast(arg, expect[i]) + val, ok, err := newValueWithSoftCast(arg, expect[i]) if err != nil { return nil, err } + if !ok { + return nil, fmt.Errorf(`%w: action "%s" expected argument %d to be of type %s, but got %s`, engine.ErrType, action, i, expect[i], val.Type()) + } argVals[i] = val } diff --git a/node/engine/interpreter/values.go b/node/engine/interpreter/values.go index fd97ae4dc..f13eda309 100644 --- a/node/engine/interpreter/values.go +++ b/node/engine/interpreter/values.go @@ -2975,25 +2975,40 @@ func makeArray(vals []scalarValue, t *types.DataType) (arrayValue, error) { // go's typing does not exactly match the engines. For example, if a 0-length // decimal array is passed, there is no way for Go to know the precision and // scale of the array. But in our interpreter, we do know this information. -func newValueWithSoftCast(v any, dt *types.DataType) (value, error) { - val, err := newValue(v) +// If it cannot be soft casted to the correct type, it will return a false. +// It will only return an error if there is an unexpected error. +// A value will always be returned if err is nil, even if ok is false. +func newValueWithSoftCast(v any, dt *types.DataType) (val value, ok bool, err error) { + val, err = newValue(v) if err != nil { - return nil, err + return nil, false, err } // if v is null or if it is a 0-length array, we need to cast it to the correct type if val.Null() { val, err = val.Cast(dt) if err != nil { - return nil, err + return nil, false, err } } if arr, ok := val.(arrayValue); ok && arr.Len() == 0 { + // if it is an array value, then the scalar type and IsArray values must match. + if arr.Type().IsArray != dt.IsArray { + return val, false, nil + } + if arr.Type().Name != dt.Name { + return val, false, nil + } + val, err = val.Cast(dt) if err != nil { - return nil, err + return nil, false, err } } - return val, nil + if !val.Type().Equals(dt) { + return val, false, nil + } + + return val, true, nil }