Skip to content

Commit 2fbbed2

Browse files
committed
added ability for callbacks to have time.Time as arguments
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
1 parent ed69081 commit 2fbbed2

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

callback.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@ import (
2828
"fmt"
2929
"math"
3030
"reflect"
31+
"strings"
3132
"sync"
33+
"time"
3234
"unsafe"
3335
)
3436

37+
var timeKind = reflect.TypeOf(time.Now()).Kind()
38+
3539
//export callbackTrampoline
3640
func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
3741
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
@@ -242,11 +246,40 @@ func callbackArg(typ reflect.Type) (callbackArgConverter, error) {
242246
case reflect.Float32:
243247
c := callbackArgCast{callbackArgFloat64, typ}
244248
return c.Run, nil
249+
case timeKind:
250+
return callbackArgTime, nil
245251
default:
246252
return nil, fmt.Errorf("don't know how to convert to %s", typ)
247253
}
248254
}
249255

256+
func callbackArgTime(v *C.sqlite3_value) (reflect.Value, error) {
257+
if C.sqlite3_value_type(v) != C.SQLITE_TEXT {
258+
return reflect.Value{}, fmt.Errorf("argument must be an TEXT")
259+
}
260+
var t time.Time
261+
var err error
262+
c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v)))
263+
tv := C.GoString(c)
264+
s := strings.TrimSuffix(tv, "Z")
265+
for _, format := range SQLiteTimestampFormats {
266+
if timeVal, err := time.ParseInLocation(format, s, time.UTC); err == nil {
267+
t = timeVal
268+
break
269+
}
270+
}
271+
if err != nil {
272+
// The column is a time value, so return the zero time on parse failure.
273+
t = time.Time{}
274+
}
275+
/*
276+
if rc.s.c.loc != nil {
277+
t = t.In(rc.s.c.loc)
278+
}
279+
*/
280+
return reflect.ValueOf(t), nil
281+
}
282+
250283
func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) {
251284
var args []reflect.Value
252285

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

367+
func callbackRetTime(ctx *C.sqlite3_context, v reflect.Value) error {
368+
rv := v.Interface().(time.Time)
369+
C._sqlite3_result_text(ctx, C.CString(rv.Format(SQLiteTimestampFormats[0])))
370+
return nil
371+
}
372+
334373
func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
335374
switch typ.Kind() {
336375
case reflect.Slice:
@@ -344,6 +383,8 @@ func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
344383
return callbackRetInteger, nil
345384
case reflect.Float32, reflect.Float64:
346385
return callbackRetFloat, nil
386+
case timeKind:
387+
return callbackRetTime, nil
347388
default:
348389
return nil, fmt.Errorf("don't know how to convert to %s", typ)
349390
}

callback_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"math"
66
"reflect"
77
"testing"
8+
"time"
89
)
910

1011
func TestCallbackArgCast(t *testing.T) {
@@ -66,6 +67,7 @@ func TestCallbackConverters(t *testing.T) {
6667
{uint(0), false},
6768
{float64(0), false},
6869
{float32(0), false},
70+
{time.Now(), false},
6971

7072
{func() {}, true},
7173
{complex64(complex(0, 0)), true},

0 commit comments

Comments
 (0)