Skip to content

Commit cba271c

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 3aefd9f commit cba271c

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

callback.go

Lines changed: 49 additions & 2 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 timetype = reflect.TypeOf(time.Now())
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]
@@ -243,8 +247,40 @@ func callbackArg(typ reflect.Type) (callbackArgConverter, error) {
243247
c := callbackArgCast{callbackArgFloat64, typ}
244248
return c.Run, nil
245249
default:
246-
return nil, fmt.Errorf("don't know how to convert to %s", typ)
250+
switch typ {
251+
case timetype:
252+
return callbackArgTime, nil
253+
default:
254+
return nil, fmt.Errorf("don't know how to convert to %s", typ)
255+
}
256+
}
257+
}
258+
259+
func callbackArgTime(v *C.sqlite3_value) (reflect.Value, error) {
260+
if C.sqlite3_value_type(v) != C.SQLITE_TEXT {
261+
return reflect.Value{}, fmt.Errorf("argument must be an TEXT")
262+
}
263+
var t time.Time
264+
var err error
265+
c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v)))
266+
tv := C.GoString(c)
267+
s := strings.TrimSuffix(tv, "Z")
268+
for _, format := range SQLiteTimestampFormats {
269+
if timeVal, err := time.ParseInLocation(format, s, time.UTC); err == nil {
270+
t = timeVal
271+
break
272+
}
247273
}
274+
if err != nil {
275+
// The column is a time value, so return the zero time on parse failure.
276+
t = time.Time{}
277+
}
278+
/*
279+
if rc.s.c.loc != nil {
280+
t = t.In(rc.s.c.loc)
281+
}
282+
*/
283+
return reflect.ValueOf(t), nil
248284
}
249285

250286
func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) {
@@ -335,6 +371,12 @@ func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error {
335371
return nil
336372
}
337373

374+
func callbackRetTime(ctx *C.sqlite3_context, v reflect.Value) error {
375+
rv := v.Interface().(time.Time)
376+
C._sqlite3_result_text(ctx, C.CString(rv.Format(SQLiteTimestampFormats[0])))
377+
return nil
378+
}
379+
338380
func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
339381
switch typ.Kind() {
340382
case reflect.Interface:
@@ -355,7 +397,12 @@ func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
355397
case reflect.Float32, reflect.Float64:
356398
return callbackRetFloat, nil
357399
default:
358-
return nil, fmt.Errorf("don't know how to convert to %s", typ)
400+
switch typ {
401+
case timetype:
402+
return callbackRetTime, nil
403+
default:
404+
return nil, fmt.Errorf("don't know how to convert to %s", typ)
405+
}
359406
}
360407
}
361408

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)