S3 Select: Add parser support for lists. (#8329)

This commit is contained in:
Klaus Post
2019-10-06 07:52:45 -07:00
committed by Harshavardhana
parent e85df07518
commit 002ac82631
10 changed files with 487 additions and 63 deletions

View File

@@ -17,6 +17,7 @@
package sql
import (
"encoding/json"
"errors"
"fmt"
"math"
@@ -46,6 +47,14 @@ type Value struct {
value interface{}
}
// MarshalJSON provides json marshaling of values.
func (v Value) MarshalJSON() ([]byte, error) {
if b, ok := v.ToBytes(); ok {
return b, nil
}
return json.Marshal(v.value)
}
// GetTypeString returns a string representation for vType
func (v Value) GetTypeString() string {
switch v.value.(type) {
@@ -63,6 +72,8 @@ func (v Value) GetTypeString() string {
return "TIMESTAMP"
case []byte:
return "BYTES"
case []Value:
return "ARRAY"
}
return "--"
}
@@ -80,6 +91,17 @@ func (v Value) Repr() string {
return fmt.Sprintf("\"%s\":%s", x, v.GetTypeString())
case []byte:
return fmt.Sprintf("\"%s\":BYTES", string(x))
case []Value:
var s strings.Builder
s.WriteByte('[')
for i, v := range x {
s.WriteString(v.Repr())
if i < len(x)-1 {
s.WriteByte(',')
}
}
s.WriteString("]:ARRAY")
return s.String()
default:
return fmt.Sprintf("%v:INVALID", v.value)
}
@@ -120,6 +142,11 @@ func FromBytes(b []byte) *Value {
return &Value{value: b}
}
// FromArray creates a Value from an array of values.
func FromArray(a []Value) *Value {
return &Value{value: a}
}
// ToFloat works for int and float values
func (v Value) ToFloat() (val float64, ok bool) {
switch x := v.value.(type) {
@@ -167,6 +194,8 @@ func (v Value) SameTypeAs(b Value) (ok bool) {
_, ok = b.value.(time.Time)
case []byte:
_, ok = b.value.([]byte)
case []Value:
_, ok = b.value.([]Value)
default:
ok = reflect.TypeOf(v.value) == reflect.TypeOf(b.value)
}
@@ -192,6 +221,12 @@ func (v Value) ToBytes() (val []byte, ok bool) {
return
}
// ToArray returns the value if it is a slice of values.
func (v Value) ToArray() (val []Value, ok bool) {
val, ok = v.value.([]Value)
return
}
// IsNull - checks if value is missing.
func (v Value) IsNull() bool {
switch v.value.(type) {
@@ -201,6 +236,12 @@ func (v Value) IsNull() bool {
return false
}
// IsArray returns whether the value is an array.
func (v Value) IsArray() (ok bool) {
_, ok = v.value.([]Value)
return ok
}
func (v Value) isNumeric() bool {
switch v.value.(type) {
case int64, float64:
@@ -255,6 +296,10 @@ func (v Value) CSVString() string {
return FormatSQLTimestamp(x)
case []byte:
return string(x)
case []Value:
b, _ := json.Marshal(x)
return string(b)
default:
return "CSV serialization not implemented for this type"
}
@@ -311,6 +356,19 @@ func (v *Value) compareOp(op string, a *Value) (res bool, err error) {
return false, err
}
// Check if either is nil
if v.IsNull() || a.IsNull() {
// If one is, both must be.
return boolCompare(op, v.IsNull(), a.IsNull())
}
// Check array values
aArr, aOK := a.ToArray()
vArr, vOK := v.ToArray()
if aOK && vOK {
return arrayCompare(op, aArr, vArr)
}
isNumeric := v.isNumeric() && a.isNumeric()
if isNumeric {
intV, ok1i := v.ToInt()
@@ -725,6 +783,32 @@ func boolCompare(op string, left, right bool) (bool, error) {
}
}
func arrayCompare(op string, left, right []Value) (bool, error) {
switch op {
case opEq:
if len(left) != len(right) {
return false, nil
}
for i, l := range left {
eq, err := l.compareOp(op, &right[i])
if !eq || err != nil {
return eq, err
}
}
return true, nil
case opIneq:
for i, l := range left {
eq, err := l.compareOp(op, &right[i])
if eq || err != nil {
return eq, err
}
}
return false, nil
default:
return false, errCmpInvalidBoolOperator
}
}
func timestampCompare(op string, left, right time.Time) bool {
switch op {
case opLt: