Add ‘purge’ SQL function

This commit is contained in:
Tobie Morgan Hitchcock 2018-04-20 21:34:54 +01:00
parent 671e59d13f
commit 7afe14ab1a
5 changed files with 166 additions and 6 deletions

View file

@ -88,6 +88,12 @@ var funcs = map[string]map[int]interface{}{
"count.if": {2: nil}, "count.if": {2: nil},
"count.not": {2: nil}, "count.not": {2: nil},
// Purge implementation
"purge": {1: nil},
"purge.if": {2: nil},
"purge.not": {2: nil},
// Json implementation // Json implementation
"json.decode": {1: nil}, "json.decode": {1: nil},
"json.encode": {1: nil}, "json.encode": {1: nil},

View file

@ -25,6 +25,12 @@ import (
var defaultTime = time.Unix(0, 0) var defaultTime = time.Unix(0, 0)
func copySlice(arr []interface{}) (out []interface{}) {
out = make([]interface{}, len(arr))
copy(out, arr)
return
}
func outputFloat(val float64) (interface{}, error) { func outputFloat(val float64) (interface{}, error) {
switch { switch {
case math.IsNaN(val): case math.IsNaN(val):
@ -176,20 +182,18 @@ func ensureDuration(val interface{}) (out time.Duration, ok bool) {
func ensureSlice(args interface{}) (out []interface{}, ok bool) { func ensureSlice(args interface{}) (out []interface{}, ok bool) {
if i, ok := args.([]interface{}); ok { if i, ok := args.([]interface{}); ok {
out = i return i, true
} else { } else {
out = []interface{}{args} return []interface{}{args}, false
} }
return out, true
} }
func ensureObject(args interface{}) (out map[string]interface{}, ok bool) { func ensureObject(args interface{}) (out map[string]interface{}, ok bool) {
if i, ok := args.(map[string]interface{}); ok { if i, ok := args.(map[string]interface{}); ok {
out = i return i, true
} else { } else {
out = map[string]interface{}{} return map[string]interface{}{}, false
} }
return out, true
} }
func ensureInts(args interface{}) (out []int64) { func ensureInts(args interface{}) (out []int64) {

View file

@ -55,6 +55,14 @@ func Run(ctx context.Context, name string, args ...interface{}) (interface{}, er
case "count.not": case "count.not":
return countNot(ctx, args...) return countNot(ctx, args...)
// Purge implementation
case "purge":
return purge(ctx, args...)
case "purge.if":
return purgeIf(ctx, args...)
case "purge.not":
return purgeNot(ctx, args...)
// Json implementation // Json implementation
case "json.decode": case "json.decode":
return jsonDecode(ctx, args...) return jsonDecode(ctx, args...)

73
util/fncs/purge.go Normal file
View file

@ -0,0 +1,73 @@
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fncs
import (
"context"
)
func purge(ctx context.Context, args ...interface{}) (interface{}, error) {
if arr, ok := ensureSlice(args[0]); ok {
arr = copySlice(arr)
for i := len(arr) - 1; i >= 0; i-- {
if arr[i] == nil {
copy(arr[i:], arr[i+1:])
arr[len(arr)-1] = nil
arr = arr[:len(arr)-1]
}
}
return arr, nil
}
if args[0] == nil {
return nil, nil
}
return args[0], nil
}
func purgeIf(ctx context.Context, args ...interface{}) (interface{}, error) {
if arr, ok := ensureSlice(args[0]); ok {
arr = copySlice(arr)
for i := len(arr) - 1; i >= 0; i-- {
if arr[i] == args[1] {
copy(arr[i:], arr[i+1:])
arr[len(arr)-1] = nil
arr = arr[:len(arr)-1]
}
}
return arr, nil
}
if args[0] == args[1] {
return nil, nil
}
return args[0], nil
}
func purgeNot(ctx context.Context, args ...interface{}) (interface{}, error) {
if arr, ok := ensureSlice(args[0]); ok {
arr = copySlice(arr)
for i := len(arr) - 1; i >= 0; i-- {
if arr[i] != args[1] {
copy(arr[i:], arr[i+1:])
arr[len(arr)-1] = nil
arr = arr[:len(arr)-1]
}
}
return arr, nil
}
if args[0] != args[1] {
return nil, nil
}
return args[0], nil
}

69
util/fncs/purge_test.go Normal file
View file

@ -0,0 +1,69 @@
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fncs
import (
"context"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestPurge(t *testing.T) {
var res interface{}
var test = []interface{}{int(1), int(2), nil, float64(3.5), "testing string"}
Convey("purge() works properly", t, func() {
res, _ = Run(context.Background(), "purge", nil)
So(res, ShouldResemble, nil)
res, _ = Run(context.Background(), "purge", "test")
So(res, ShouldResemble, "test")
res, _ = Run(context.Background(), "purge", test)
So(res, ShouldResemble, []interface{}{int(1), int(2), float64(3.5), "testing string"})
})
Convey("purge.if() works properly", t, func() {
res, _ = Run(context.Background(), "purge.if", "test", nil)
So(res, ShouldResemble, "test")
res, _ = Run(context.Background(), "purge.if", "test", "none")
So(res, ShouldResemble, "test")
res, _ = Run(context.Background(), "purge.if", "test", "test")
So(res, ShouldResemble, nil)
res, _ = Run(context.Background(), "purge.if", test, "testing string")
So(res, ShouldResemble, []interface{}{int(1), int(2), nil, float64(3.5)})
res, _ = Run(context.Background(), "purge.if", test, 3.5)
So(res, ShouldResemble, []interface{}{int(1), int(2), nil, "testing string"})
res, _ = Run(context.Background(), "purge.if", test, nil)
So(res, ShouldResemble, []interface{}{int(1), int(2), float64(3.5), "testing string"})
})
Convey("purge.not() works properly", t, func() {
res, _ = Run(context.Background(), "purge.not", "test", nil)
So(res, ShouldResemble, nil)
res, _ = Run(context.Background(), "purge.not", "test", "none")
So(res, ShouldResemble, nil)
res, _ = Run(context.Background(), "purge.not", "test", "test")
So(res, ShouldResemble, "test")
res, _ = Run(context.Background(), "purge.not", test, "testing string")
So(res, ShouldResemble, []interface{}{"testing string"})
res, _ = Run(context.Background(), "purge.not", test, 3.5)
So(res, ShouldResemble, []interface{}{float64(3.5)})
res, _ = Run(context.Background(), "purge.not", test, nil)
So(res, ShouldResemble, []interface{}{nil})
})
}