forked from goccy/go-zetasqlite
-
Notifications
You must be signed in to change notification settings - Fork 1
/
function_javascript.go
131 lines (127 loc) · 3.21 KB
/
function_javascript.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package internal
import (
"fmt"
"math/big"
"time"
"github.com/dop251/goja"
"github.com/goccy/go-zetasql/types"
)
func EVAL_JAVASCRIPT(code string, retType *Type, argNames []string, args []Value) (Value, error) {
vm := goja.New()
for i := 0; i < len(args); i++ {
var v interface{}
if args[i] != nil {
structV, ok := args[i].(*StructValue)
if ok {
v = structV.m
} else {
v = args[i].Interface()
}
}
if err := vm.Set(argNames[i], v); err != nil {
return nil, fmt.Errorf(
"failed to set argument variable for %s as %v",
argNames[i],
args[i],
)
}
}
evalCode := fmt.Sprintf(`
function zetasqlite_javascript_func() { %s }
zetasqlite_javascript_func();
`, code)
ret, err := vm.RunString(evalCode)
if err != nil {
return nil, fmt.Errorf("failed to evaluate javascript code %s: %w", code, err)
}
typ, err := retType.ToZetaSQLType()
if err != nil {
return nil, fmt.Errorf("failed to get return type: %w", err)
}
value, err := castJavaScriptValue(typ, ret)
if err != nil {
return nil, fmt.Errorf("failed to convert zetasqlite value from %v: %w", ret, err)
}
return value, nil
}
func castJavaScriptValue(t types.Type, v goja.Value) (Value, error) {
if v == nil {
return nil, nil
}
switch t.Kind() {
case types.INT32, types.INT64, types.UINT32, types.UINT64:
return IntValue(v.ToInteger()), nil
case types.BOOL:
return BoolValue(v.ToBoolean()), nil
case types.FLOAT, types.DOUBLE:
return FloatValue(v.ToFloat()), nil
case types.STRING, types.ENUM:
return StringValue(v.ToString().String()), nil
case types.BYTES:
return BytesValue(v.ToString().String()), nil
case types.DATE:
t, err := parseDate(v.ToString().String())
if err != nil {
return nil, err
}
return DateValue(t), nil
case types.DATETIME:
t, err := parseDatetime(v.ToString().String())
if err != nil {
return nil, err
}
return DatetimeValue(t), nil
case types.TIME:
t, err := parseTime(v.ToString().String())
if err != nil {
return nil, err
}
return TimeValue(t), nil
case types.TIMESTAMP:
t, err := parseTimestamp(v.ToString().String(), time.UTC)
if err != nil {
return nil, err
}
return TimestampValue(t), nil
case types.INTERVAL:
return parseInterval(v.ToString().String())
case types.NUMERIC:
r := new(big.Rat)
r.SetString(v.ToNumber().String())
return &NumericValue{Rat: r}, nil
case types.BIG_NUMERIC:
r := new(big.Rat)
r.SetString(v.ToNumber().String())
return &NumericValue{Rat: r}, nil
case types.JSON:
return JsonValue(v.ToString().String()), nil
case types.ARRAY:
elemType := t.AsArray().ElementType()
var ret ArrayValue
for _, vv := range v.Export().([]interface{}) {
base, err := ValueFromGoValue(vv)
if err != nil {
return nil, err
}
elem, err := CastValue(elemType, base)
if err != nil {
return nil, err
}
ret.values = append(ret.values, elem)
}
return &ret, nil
case types.STRUCT:
base, err := ValueFromGoValue(v.Export())
if err != nil {
return nil, err
}
return CastValue(t, base)
case types.GEOGRAPHY:
base, err := ValueFromGoValue(v.Export())
if err != nil {
return nil, err
}
return CastValue(t, base)
}
return nil, fmt.Errorf("unsupported cast %s from JavaScript value", t.Kind())
}