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 3aefd9f commit cba271c
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 2 deletions.
51 changes: 49 additions & 2 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 timetype = reflect.TypeOf(time.Now())

//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 @@ -243,8 +247,40 @@ func callbackArg(typ reflect.Type) (callbackArgConverter, error) {
c := callbackArgCast{callbackArgFloat64, typ}
return c.Run, nil
default:
return nil, fmt.Errorf("don't know how to convert to %s", typ)
switch typ {
case timetype:
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) {
Expand Down Expand Up @@ -335,6 +371,12 @@ func callbackRetNil(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.Interface:
Expand All @@ -355,7 +397,12 @@ func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
case reflect.Float32, reflect.Float64:
return callbackRetFloat, nil
default:
return nil, fmt.Errorf("don't know how to convert to %s", typ)
switch typ {
case timetype:
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 cba271c

Please sign in to comment.