Skip to content

Commit

Permalink
added ability for callbacks to have time.Time as arguments
Browse files Browse the repository at this point in the history
go-sqlite3 can store time.Time values, and also has the fetaure to
register callback function to provide additionaly functions.

However these callback cannot take the mentioned time.Time columns as
input as there is no "dynamic-casting" of these values from TEXT to
time.Time.

With this patch that become possible letting user write custom functions
to do fancy filtering these time.Time columns
  • Loading branch information
sum12 committed Jul 11, 2018
1 parent ed69081 commit 2fbbed2
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
41 changes: 41 additions & 0 deletions callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ import (
"fmt"
"math"
"reflect"
"strings"
"sync"
"time"
"unsafe"
)

var timeKind = reflect.TypeOf(time.Now()).Kind()

//export callbackTrampoline
func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
Expand Down Expand Up @@ -242,11 +246,40 @@ func callbackArg(typ reflect.Type) (callbackArgConverter, error) {
case reflect.Float32:
c := callbackArgCast{callbackArgFloat64, typ}
return c.Run, nil
case timeKind:
return callbackArgTime, nil
default:
return nil, fmt.Errorf("don't know how to convert to %s", typ)
}
}

func callbackArgTime(v *C.sqlite3_value) (reflect.Value, error) {
if C.sqlite3_value_type(v) != C.SQLITE_TEXT {
return reflect.Value{}, fmt.Errorf("argument must be an TEXT")
}
var t time.Time
var err error
c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v)))
tv := C.GoString(c)
s := strings.TrimSuffix(tv, "Z")
for _, format := range SQLiteTimestampFormats {
if timeVal, err := time.ParseInLocation(format, s, time.UTC); err == nil {
t = timeVal
break
}
}
if err != nil {
// The column is a time value, so return the zero time on parse failure.
t = time.Time{}
}
/*
if rc.s.c.loc != nil {
t = t.In(rc.s.c.loc)
}
*/
return reflect.ValueOf(t), nil
}

func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) {
var args []reflect.Value

Expand Down Expand Up @@ -331,6 +364,12 @@ func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error {
return nil
}

func callbackRetTime(ctx *C.sqlite3_context, v reflect.Value) error {
rv := v.Interface().(time.Time)
C._sqlite3_result_text(ctx, C.CString(rv.Format(SQLiteTimestampFormats[0])))
return nil
}

func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
switch typ.Kind() {
case reflect.Slice:
Expand All @@ -344,6 +383,8 @@ func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
return callbackRetInteger, nil
case reflect.Float32, reflect.Float64:
return callbackRetFloat, nil
case timeKind:
return callbackRetTime, nil
default:
return nil, fmt.Errorf("don't know how to convert to %s", typ)
}
Expand Down
2 changes: 2 additions & 0 deletions callback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math"
"reflect"
"testing"
"time"
)

func TestCallbackArgCast(t *testing.T) {
Expand Down Expand Up @@ -66,6 +67,7 @@ func TestCallbackConverters(t *testing.T) {
{uint(0), false},
{float64(0), false},
{float32(0), false},
{time.Now(), false},

{func() {}, true},
{complex64(complex(0, 0)), true},
Expand Down

0 comments on commit 2fbbed2

Please sign in to comment.