Skip to content

Regression: starting with v1.16.0 nil []byte values are not inserted as NULLs anymore

Reproduced with:

go version go1.18.2 darwin/arm64, modernc.org/sqlite@v1.16.0 and higher

Handling of nil byte slices (var x []byte) changed when inserting such values in v1.16.0. Before v1.16.0 nil byte slices were inserted like NULL values, starting with v1.16.0 they are inserted as zero-sized blobs.

Consider the following test:

func TestNullColumn(t *testing.T) {
	db, err := sql.Open("sqlite", ":memory:")
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	if _, err := db.Exec(`CREATE TABLE t1(Tags TEXT)`); err != nil {
		t.Fatal(err)
	}
	var val []byte // nil value
	_, err = db.Exec(`INSERT INTO t1(Tags) VALUES(@tags)`, sql.Named("tags", val))
	if err != nil {
		t.Fatal(err)
	}
	var res sql.NullByte
	if err = db.QueryRow(`SELECT Tags FROM t1 LIMIT 1`).Scan(&res); err != nil {
		t.Fatal(err)
	}
	if res.Valid {
		t.Fatalf("got non-NULL result: %v", res)
	}
}

It passes on v1.15.4, but fails on v1.16.0 and higher:

¶ go get modernc.org/sqlite@v1.15.4
go: downgraded modernc.org/sqlite v1.16.0 => v1.15.4
¶ go test -run ^TestNullColumn$    
PASS
ok  	package-name	0.403s


¶ go get modernc.org/sqlite@v1.16.0
go: upgraded modernc.org/sqlite v1.15.4 => v1.16.0
¶ go test -run ^TestNullColumn$    
--- FAIL: TestNullColumn (0.00s)
    err_test.go:29: sql: Scan error on column index 0, name "Tags": converting driver.Value type []uint8 ("") to a uint8: invalid syntax
FAIL
exit status 1
FAIL	package-name	0.159s

If I change the test to use the on-disk database file and later inspect it with sqlite3 cli (3.38.5):

Database created on v1.15.4 has nil []byte stored as NULL (as expected):

sqlite> select ifnull(Tags,'XXX') from t1;
ifnull(Tags,'XXX')
------------------
XXX  

v1.16.0 and higher store nil []byte as a zero-sized blob instead:

sqlite> select ifnull(Tags,'XXX') from t1;
ifnull(Tags,'XXX')
------------------
                  
sqlite> select typeof(Tags) from t1;
typeof(Tags)
------------
blob        
sqlite> select length(Tags) from t1;
length(Tags)
------------
0