341 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
 | 
						|
//
 | 
						|
// Use of this source code is governed by an MIT-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package sqlite3
 | 
						|
 | 
						|
// You can't export a Go function to C and have definitions in the C
 | 
						|
// preamble in the same file, so we have to have callbackTrampoline in
 | 
						|
// its own file. Because we need a separate file anyway, the support
 | 
						|
// code for SQLite custom functions is in here.
 | 
						|
 | 
						|
/*
 | 
						|
#ifndef USE_LIBSQLITE3
 | 
						|
#include <sqlite3-binding.h>
 | 
						|
#else
 | 
						|
#include <sqlite3.h>
 | 
						|
#endif
 | 
						|
#include <stdlib.h>
 | 
						|
 | 
						|
void _sqlite3_result_text(sqlite3_context* ctx, const char* s);
 | 
						|
void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l);
 | 
						|
*/
 | 
						|
import "C"
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"math"
 | 
						|
	"reflect"
 | 
						|
	"sync"
 | 
						|
	"unsafe"
 | 
						|
)
 | 
						|
 | 
						|
//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]
 | 
						|
	fi := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*functionInfo)
 | 
						|
	fi.Call(ctx, args)
 | 
						|
}
 | 
						|
 | 
						|
//export stepTrampoline
 | 
						|
func stepTrampoline(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]
 | 
						|
	ai := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*aggInfo)
 | 
						|
	ai.Step(ctx, args)
 | 
						|
}
 | 
						|
 | 
						|
//export doneTrampoline
 | 
						|
func doneTrampoline(ctx *C.sqlite3_context) {
 | 
						|
	handle := uintptr(C.sqlite3_user_data(ctx))
 | 
						|
	ai := lookupHandle(handle).(*aggInfo)
 | 
						|
	ai.Done(ctx)
 | 
						|
}
 | 
						|
 | 
						|
// Use handles to avoid passing Go pointers to C.
 | 
						|
 | 
						|
type handleVal struct {
 | 
						|
	db  *SQLiteConn
 | 
						|
	val interface{}
 | 
						|
}
 | 
						|
 | 
						|
var handleLock sync.Mutex
 | 
						|
var handleVals = make(map[uintptr]handleVal)
 | 
						|
var handleIndex uintptr = 100
 | 
						|
 | 
						|
func newHandle(db *SQLiteConn, v interface{}) uintptr {
 | 
						|
	handleLock.Lock()
 | 
						|
	defer handleLock.Unlock()
 | 
						|
	i := handleIndex
 | 
						|
	handleIndex++
 | 
						|
	handleVals[i] = handleVal{db, v}
 | 
						|
	return i
 | 
						|
}
 | 
						|
 | 
						|
func lookupHandle(handle uintptr) interface{} {
 | 
						|
	handleLock.Lock()
 | 
						|
	defer handleLock.Unlock()
 | 
						|
	r, ok := handleVals[handle]
 | 
						|
	if !ok {
 | 
						|
		if handle >= 100 && handle < handleIndex {
 | 
						|
			panic("deleted handle")
 | 
						|
		} else {
 | 
						|
			panic("invalid handle")
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return r.val
 | 
						|
}
 | 
						|
 | 
						|
func deleteHandles(db *SQLiteConn) {
 | 
						|
	handleLock.Lock()
 | 
						|
	defer handleLock.Unlock()
 | 
						|
	for handle, val := range handleVals {
 | 
						|
		if val.db == db {
 | 
						|
			delete(handleVals, handle)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// This is only here so that tests can refer to it.
 | 
						|
type callbackArgRaw C.sqlite3_value
 | 
						|
 | 
						|
type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error)
 | 
						|
 | 
						|
type callbackArgCast struct {
 | 
						|
	f   callbackArgConverter
 | 
						|
	typ reflect.Type
 | 
						|
}
 | 
						|
 | 
						|
func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) {
 | 
						|
	val, err := c.f(v)
 | 
						|
	if err != nil {
 | 
						|
		return reflect.Value{}, err
 | 
						|
	}
 | 
						|
	if !val.Type().ConvertibleTo(c.typ) {
 | 
						|
		return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ)
 | 
						|
	}
 | 
						|
	return val.Convert(c.typ), nil
 | 
						|
}
 | 
						|
 | 
						|
func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) {
 | 
						|
	if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
 | 
						|
		return reflect.Value{}, fmt.Errorf("argument must be an INTEGER")
 | 
						|
	}
 | 
						|
	return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil
 | 
						|
}
 | 
						|
 | 
						|
func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) {
 | 
						|
	if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
 | 
						|
		return reflect.Value{}, fmt.Errorf("argument must be an INTEGER")
 | 
						|
	}
 | 
						|
	i := int64(C.sqlite3_value_int64(v))
 | 
						|
	val := false
 | 
						|
	if i != 0 {
 | 
						|
		val = true
 | 
						|
	}
 | 
						|
	return reflect.ValueOf(val), nil
 | 
						|
}
 | 
						|
 | 
						|
func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) {
 | 
						|
	if C.sqlite3_value_type(v) != C.SQLITE_FLOAT {
 | 
						|
		return reflect.Value{}, fmt.Errorf("argument must be a FLOAT")
 | 
						|
	}
 | 
						|
	return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil
 | 
						|
}
 | 
						|
 | 
						|
func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) {
 | 
						|
	switch C.sqlite3_value_type(v) {
 | 
						|
	case C.SQLITE_BLOB:
 | 
						|
		l := C.sqlite3_value_bytes(v)
 | 
						|
		p := C.sqlite3_value_blob(v)
 | 
						|
		return reflect.ValueOf(C.GoBytes(p, l)), nil
 | 
						|
	case C.SQLITE_TEXT:
 | 
						|
		l := C.sqlite3_value_bytes(v)
 | 
						|
		c := unsafe.Pointer(C.sqlite3_value_text(v))
 | 
						|
		return reflect.ValueOf(C.GoBytes(c, l)), nil
 | 
						|
	default:
 | 
						|
		return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) {
 | 
						|
	switch C.sqlite3_value_type(v) {
 | 
						|
	case C.SQLITE_BLOB:
 | 
						|
		l := C.sqlite3_value_bytes(v)
 | 
						|
		p := (*C.char)(C.sqlite3_value_blob(v))
 | 
						|
		return reflect.ValueOf(C.GoStringN(p, l)), nil
 | 
						|
	case C.SQLITE_TEXT:
 | 
						|
		c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v)))
 | 
						|
		return reflect.ValueOf(C.GoString(c)), nil
 | 
						|
	default:
 | 
						|
		return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) {
 | 
						|
	switch C.sqlite3_value_type(v) {
 | 
						|
	case C.SQLITE_INTEGER:
 | 
						|
		return callbackArgInt64(v)
 | 
						|
	case C.SQLITE_FLOAT:
 | 
						|
		return callbackArgFloat64(v)
 | 
						|
	case C.SQLITE_TEXT:
 | 
						|
		return callbackArgString(v)
 | 
						|
	case C.SQLITE_BLOB:
 | 
						|
		return callbackArgBytes(v)
 | 
						|
	case C.SQLITE_NULL:
 | 
						|
		// Interpret NULL as a nil byte slice.
 | 
						|
		var ret []byte
 | 
						|
		return reflect.ValueOf(ret), nil
 | 
						|
	default:
 | 
						|
		panic("unreachable")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func callbackArg(typ reflect.Type) (callbackArgConverter, error) {
 | 
						|
	switch typ.Kind() {
 | 
						|
	case reflect.Interface:
 | 
						|
		if typ.NumMethod() != 0 {
 | 
						|
			return nil, errors.New("the only supported interface type is interface{}")
 | 
						|
		}
 | 
						|
		return callbackArgGeneric, nil
 | 
						|
	case reflect.Slice:
 | 
						|
		if typ.Elem().Kind() != reflect.Uint8 {
 | 
						|
			return nil, errors.New("the only supported slice type is []byte")
 | 
						|
		}
 | 
						|
		return callbackArgBytes, nil
 | 
						|
	case reflect.String:
 | 
						|
		return callbackArgString, nil
 | 
						|
	case reflect.Bool:
 | 
						|
		return callbackArgBool, nil
 | 
						|
	case reflect.Int64:
 | 
						|
		return callbackArgInt64, nil
 | 
						|
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
 | 
						|
		c := callbackArgCast{callbackArgInt64, typ}
 | 
						|
		return c.Run, nil
 | 
						|
	case reflect.Float64:
 | 
						|
		return callbackArgFloat64, nil
 | 
						|
	case reflect.Float32:
 | 
						|
		c := callbackArgCast{callbackArgFloat64, typ}
 | 
						|
		return c.Run, nil
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("don't know how to convert to %s", typ)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) {
 | 
						|
	var args []reflect.Value
 | 
						|
 | 
						|
	if len(argv) < len(converters) {
 | 
						|
		return nil, fmt.Errorf("function requires at least %d arguments", len(converters))
 | 
						|
	}
 | 
						|
 | 
						|
	for i, arg := range argv[:len(converters)] {
 | 
						|
		v, err := converters[i](arg)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		args = append(args, v)
 | 
						|
	}
 | 
						|
 | 
						|
	if variadic != nil {
 | 
						|
		for _, arg := range argv[len(converters):] {
 | 
						|
			v, err := variadic(arg)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			args = append(args, v)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return args, nil
 | 
						|
}
 | 
						|
 | 
						|
type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error
 | 
						|
 | 
						|
func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error {
 | 
						|
	switch v.Type().Kind() {
 | 
						|
	case reflect.Int64:
 | 
						|
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
 | 
						|
		v = v.Convert(reflect.TypeOf(int64(0)))
 | 
						|
	case reflect.Bool:
 | 
						|
		b := v.Interface().(bool)
 | 
						|
		if b {
 | 
						|
			v = reflect.ValueOf(int64(1))
 | 
						|
		} else {
 | 
						|
			v = reflect.ValueOf(int64(0))
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return fmt.Errorf("cannot convert %s to INTEGER", v.Type())
 | 
						|
	}
 | 
						|
 | 
						|
	C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64)))
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error {
 | 
						|
	switch v.Type().Kind() {
 | 
						|
	case reflect.Float64:
 | 
						|
	case reflect.Float32:
 | 
						|
		v = v.Convert(reflect.TypeOf(float64(0)))
 | 
						|
	default:
 | 
						|
		return fmt.Errorf("cannot convert %s to FLOAT", v.Type())
 | 
						|
	}
 | 
						|
 | 
						|
	C.sqlite3_result_double(ctx, C.double(v.Interface().(float64)))
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error {
 | 
						|
	if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 {
 | 
						|
		return fmt.Errorf("cannot convert %s to BLOB", v.Type())
 | 
						|
	}
 | 
						|
	i := v.Interface()
 | 
						|
	if i == nil || len(i.([]byte)) == 0 {
 | 
						|
		C.sqlite3_result_null(ctx)
 | 
						|
	} else {
 | 
						|
		bs := i.([]byte)
 | 
						|
		C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs)))
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error {
 | 
						|
	if v.Type().Kind() != reflect.String {
 | 
						|
		return fmt.Errorf("cannot convert %s to TEXT", v.Type())
 | 
						|
	}
 | 
						|
	C._sqlite3_result_text(ctx, C.CString(v.Interface().(string)))
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
 | 
						|
	switch typ.Kind() {
 | 
						|
	case reflect.Slice:
 | 
						|
		if typ.Elem().Kind() != reflect.Uint8 {
 | 
						|
			return nil, errors.New("the only supported slice type is []byte")
 | 
						|
		}
 | 
						|
		return callbackRetBlob, nil
 | 
						|
	case reflect.String:
 | 
						|
		return callbackRetText, nil
 | 
						|
	case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
 | 
						|
		return callbackRetInteger, nil
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		return callbackRetFloat, nil
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("don't know how to convert to %s", typ)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func callbackError(ctx *C.sqlite3_context, err error) {
 | 
						|
	cstr := C.CString(err.Error())
 | 
						|
	defer C.free(unsafe.Pointer(cstr))
 | 
						|
	C.sqlite3_result_error(ctx, cstr, -1)
 | 
						|
}
 | 
						|
 | 
						|
// Test support code. Tests are not allowed to import "C", so we can't
 | 
						|
// declare any functions that use C.sqlite3_value.
 | 
						|
func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter {
 | 
						|
	return func(*C.sqlite3_value) (reflect.Value, error) {
 | 
						|
		return v, err
 | 
						|
	}
 | 
						|
}
 |