Add IsNullCallbackArg

This commit is contained in:
Andrew Zhang 2023-06-07 20:26:58 -05:00
parent f08f1b6b9c
commit 274eefa1e3
3 changed files with 174 additions and 103 deletions

View File

@ -409,3 +409,9 @@ func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter
return v, err
}
}
// NULL is passed into custom functions as a nil byte slice.
func IsNullCallbackArg(arg interface{}) bool {
val, ok := arg.([]byte)
return ok && val == nil
}

View File

@ -965,10 +965,12 @@ func (c *SQLiteConn) begin(ctx context.Context) (driver.Tx, error) {
// The argument is may be either in parentheses or it may be separated from
// the pragma name by an equal sign. The two syntaxes yield identical results.
// In many pragmas, the argument is a boolean. The boolean can be one of:
//
// 1 yes true on
// 0 no false off
//
// You can specify a DSN string using a URI as the filename.
//
// test.db
// file:test.db?cache=shared&mode=memory
// :memory:
@ -1000,6 +1002,7 @@ func (c *SQLiteConn) begin(ctx context.Context) (driver.Tx, error) {
// does in fact change can result in incorrect query results and/or SQLITE_CORRUPT errors.
//
// go-sqlite3 adds the following query parameters to those used by SQLite:
//
// _loc=XXX
// Specify location of time format. It's possible to specify "auto".
//
@ -1060,8 +1063,6 @@ func (c *SQLiteConn) begin(ctx context.Context) (driver.Tx, error) {
// When this pragma is on, the SQLITE_MASTER tables in which database
// can be changed using ordinary UPDATE, INSERT, and DELETE statements.
// Warning: misuse of this pragma can easily result in a corrupt database file.
//
//
func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
if C.sqlite3_threadsafe() == 0 {
return nil, errors.New("sqlite library was not compiled for thread-safe operation")

View File

@ -1439,6 +1439,70 @@ func TestFunctionRegistration(t *testing.T) {
}
}
func TestNullCallbackArg(t *testing.T) {
sql.Register("sqlite3_NullCallbackArg", &SQLiteDriver{
ConnectHook: func(conn *SQLiteConn) error {
return conn.RegisterFunc("isNullArg", IsNullCallbackArg, true)
},
})
db, err := sql.Open("sqlite3_NullCallbackArg", ":memory:")
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer db.Close()
_, err = db.Exec("CREATE TABLE test (id integer not null primary key, col_int int, col_float float, col_blob blob, col_bool bool)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
_, err = db.Exec("insert into test values (1, NULL, NULL, NULL, NULL), (2, ?, ?, ?, ?)", 1, 1.5, []byte("blob"), false)
if err != nil {
t.Fatal("Failed to insert records:", err)
}
_, err = db.Exec("insert into test values (3, NULL, ?, NULL, ?)", 1.5, true)
if err != nil {
t.Fatal("Failed to insert records:", err)
}
_, err = db.Exec("insert into test values (4, NULL, NULL, ?, NULL)", []byte{})
if err != nil {
t.Fatal("Failed to insert records:", err)
}
tests := []struct {
id int64
nullInt bool
nullFloat bool
nullBlob bool
nullBool bool
}{
{1, true, true, true, true},
{2, false, false, false, false},
{3, true, false, true, false},
{4, true, true, false, true},
}
for _, test := range tests {
var retInt, retFloat, retBlob, retBool bool
err = db.QueryRow("select isNullArg(col_int), isNullArg(col_float), isNullArg(col_blob), isNullArg(col_bool) from test where id = $1", test.id).Scan(&retInt, &retFloat, &retBlob, &retBool)
if err != nil {
t.Fatal("Query failed:", err)
}
if retInt != test.nullInt {
t.Fatalf("isNullArg returned wrong value for col_int, got %v, want %v", retInt, test.nullInt)
}
if retFloat != test.nullFloat {
t.Fatalf("isNullArg returned wrong value for col_float, got %v, want %v", retFloat, test.nullFloat)
}
if retBlob != test.nullBlob {
t.Fatalf("isNullArg returned wrong value for col_blob, got %v, want %v", retBlob, test.nullBlob)
}
if retBool != test.nullBool {
t.Fatalf("isNullArg returned wrong value for col_bool, got %v, want %v", retBool, test.nullBlob)
}
}
}
type sumAggregator int64
func (s *sumAggregator) Step(x int64) {