diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 36 | ||||
-rw-r--r-- | deps.mk | 23 | ||||
-rw-r--r-- | doc/examples/fuzz/fuzz_openexec.go | 30 | ||||
-rw-r--r-- | doc/examples/json/json.go | 81 | ||||
-rw-r--r-- | doc/examples/limit/limit.go | 113 | ||||
-rw-r--r-- | doc/examples/mod_regexp/Makefile | 27 | ||||
-rw-r--r-- | doc/examples/mod_regexp/extension.go | 43 | ||||
-rw-r--r-- | doc/examples/mod_regexp/sqlite3_mod_regexp.c | 35 | ||||
-rwxr-xr-x | mkdeps.sh | 8 | ||||
-rw-r--r-- | tests/functional/extension.go | 79 | ||||
-rw-r--r-- | tests/functional/json.go | 96 | ||||
-rw-r--r-- | tests/functional/libbuild.go (renamed from tests/libbuild.go) | 0 | ||||
-rw-r--r-- | tests/functional/limit.go | 102 | ||||
-rw-r--r-- | tests/functional/streq.c | 36 |
15 files changed, 372 insertions, 340 deletions
@@ -3,3 +3,6 @@ /src/*cgo* /tests/*.a /tests/*.bin +/tests/functional/*.a +/tests/functional/*.bin +/tests/functional/*.so @@ -24,7 +24,7 @@ GOLDFLAGS = -L $(GOLIBDIR) .SUFFIXES: -.SUFFIXES: .go .a .c .o .bin .bin-check +.SUFFIXES: .go .a .c .o .so .bin .bin-check .c.o: $(CC) $(CFLAGS) -o $@ -c $< @@ -35,6 +35,9 @@ all: include deps.mk +functional-tests.a = $(functional-tests.go:.go=.a) +functional-tests.bin = $(functional-tests.go:.go=.bin) + cgo.go = \ src/_cgo_import.go \ src/_cgo_gotypes.go \ @@ -60,9 +63,10 @@ derived-assets = \ src/$(NAME).a \ tests/$(NAME).a \ tests/main.a \ - tests/libbuild.a \ tests/main.bin \ - tests/libbuild.bin \ + $(functional-tests.a) \ + $(functional-tests.bin) \ + tests/functional/streq.so \ side-assets = \ src/_cgo_export.h \ @@ -78,6 +82,8 @@ all: $(derived-assets) $(derived-assets): Makefile deps.mk $(cgo.go) $(cgo.c) $(cgo.o): src/_cgo_.o + + src/_cgo_.o: src/$(NAME).go go tool cgo --objdir $(@D) src/$(NAME).go @@ -95,30 +101,38 @@ tests/$(NAME).a: $(cgo.go) $(cgo.o) src/version.go tests/$(NAME).go tests/main.a: tests/main.go tests/$(NAME).a go tool compile $(GOCFLAGS) -o $@ -p $(*F) -I $(@D) $*.go -tests/libbuild.a: tests/libbuild.go src/$(NAME).a - go tool compile $(GOCFLAGS) -o $@ -p main -I src $*.go - tests/main.bin: tests/main.a go tool link $(GOLDFLAGS) -o $@ -L $(@D) --extldflags '$(LDLIBS)' $*.a -tests/libbuild.bin: tests/libbuild.a - go tool link $(GOLDFLAGS) -o $@ -L src --extldflags '$(LDLIBS)' $*.a - src/version.go: Makefile echo 'package $(NAME); const Version = "$(VERSION)"' > $@ +$(functional-tests.a): src/$(NAME).a + go tool compile $(GOCFLAGS) -o $@ -p main -I src $*.go + +$(functional-tests.bin): + go tool link $(GOLDFLAGS) -o $@ -L src --extldflags '$(LDLIBS)' $*.a + +tests/functional/streq.so: tests/functional/streq.c + $(CC) $(CFLAGS) $(LDFLAGS) -fPIC --shared -o $@ $*.c + + + +tests/functional/extension.bin-check: tests/functional/streq.so + LD_LIBRARY_PATH=$(@D) $(EXEC)$*.bin +functional-tests.bin-check = $(functional-tests-butone.go:.go=.bin-check) tests.bin-check = \ tests/main.bin-check \ - tests/libbuild.bin-check \ + $(functional-tests.bin-check) \ tests/main.bin-check: tests/main.bin -tests/libbuild.bin-check: tests/libbuild.bin $(tests.bin-check): $(EXEC)$*.bin check-unit: $(tests.bin-check) +check-unit: tests/functional/extension.bin-check integration-tests = \ @@ -0,0 +1,23 @@ +functional-tests.go = \ + tests/functional/extension.go \ + tests/functional/json.go \ + tests/functional/libbuild.go \ + tests/functional/limit.go \ + +functional-tests-butone.go = \ + tests/functional/json.go \ + tests/functional/libbuild.go \ + tests/functional/limit.go \ + +tests/functional/extension.a: tests/functional/extension.go +tests/functional/json.a: tests/functional/json.go +tests/functional/libbuild.a: tests/functional/libbuild.go +tests/functional/limit.a: tests/functional/limit.go +tests/functional/extension.bin: tests/functional/extension.a +tests/functional/json.bin: tests/functional/json.a +tests/functional/libbuild.bin: tests/functional/libbuild.a +tests/functional/limit.bin: tests/functional/limit.a +tests/functional/extension.bin-check: tests/functional/extension.bin +tests/functional/json.bin-check: tests/functional/json.bin +tests/functional/libbuild.bin-check: tests/functional/libbuild.bin +tests/functional/limit.bin-check: tests/functional/limit.bin diff --git a/doc/examples/fuzz/fuzz_openexec.go b/doc/examples/fuzz/fuzz_openexec.go deleted file mode 100644 index 5326044..0000000 --- a/doc/examples/fuzz/fuzz_openexec.go +++ /dev/null @@ -1,30 +0,0 @@ -package sqlite3_fuzz - -import ( - "bytes" - "database/sql" - "io/ioutil" - - _ "github.com/mattn/go-sqlite3" -) - -func FuzzOpenExec(data []byte) int { - sep := bytes.IndexByte(data, 0) - if sep <= 0 { - return 0 - } - err := ioutil.WriteFile("/tmp/fuzz.db", data[sep+1:], 0644) - if err != nil { - return 0 - } - db, err := sql.Open("sqlite3", "/tmp/fuzz.db") - if err != nil { - return 0 - } - defer db.Close() - _, err = db.Exec(string(data[:sep-1])) - if err != nil { - return 0 - } - return 1 -} diff --git a/doc/examples/json/json.go b/doc/examples/json/json.go deleted file mode 100644 index 181934b..0000000 --- a/doc/examples/json/json.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "database/sql" - "database/sql/driver" - "encoding/json" - "fmt" - _ "github.com/mattn/go-sqlite3" - "log" - "os" -) - -type Tag struct { - Name string `json:"name"` - Country string `json:"country"` -} - -func (t *Tag) Scan(value interface{}) error { - return json.Unmarshal([]byte(value.(string)), t) -} - -func (t *Tag) Value() (driver.Value, error) { - b, err := json.Marshal(t) - return string(b), err -} - -func main() { - os.Remove("./foo.db") - - db, err := sql.Open("sqlite3", "./foo.db") - if err != nil { - log.Fatal(err) - } - defer db.Close() - - _, err = db.Exec(`create table foo (tag jsonb)`) - if err != nil { - log.Fatal(err) - } - - stmt, err := db.Prepare("insert into foo(tag) values(?)") - if err != nil { - log.Fatal(err) - } - defer stmt.Close() - _, err = stmt.Exec(`{"name": "mattn", "country": "japan"}`) - if err != nil { - log.Fatal(err) - } - _, err = stmt.Exec(`{"name": "michael", "country": "usa"}`) - if err != nil { - log.Fatal(err) - } - - var country string - err = db.QueryRow("select tag->>'country' from foo where tag->>'name' = 'mattn'").Scan(&country) - if err != nil { - log.Fatal(err) - } - fmt.Println(country) - - var tag Tag - err = db.QueryRow("select tag from foo where tag->>'name' = 'mattn'").Scan(&tag) - if err != nil { - log.Fatal(err) - } - - fmt.Println(tag.Name) - - tag.Country = "日本" - _, err = db.Exec(`update foo set tag = ? where tag->>'name' == 'mattn'`, &tag) - if err != nil { - log.Fatal(err) - } - - err = db.QueryRow("select tag->>'country' from foo where tag->>'name' = 'mattn'").Scan(&country) - if err != nil { - log.Fatal(err) - } - fmt.Println(country) -} diff --git a/doc/examples/limit/limit.go b/doc/examples/limit/limit.go deleted file mode 100644 index c1adfe8..0000000 --- a/doc/examples/limit/limit.go +++ /dev/null @@ -1,113 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - "log" - "os" - "strings" - - "github.com/mattn/go-sqlite3" -) - -func createBulkInsertQuery(n int, start int) (query string, args []any) { - values := make([]string, n) - args = make([]any, n*2) - pos := 0 - for i := 0; i < n; i++ { - values[i] = "(?, ?)" - args[pos] = start + i - args[pos+1] = fmt.Sprintf("こんにちは世界%03d", i) - pos += 2 - } - query = fmt.Sprintf( - "insert into foo(id, name) values %s", - strings.Join(values, ", "), - ) - return -} - -func bulkInsert(db *sql.DB, query string, args []any) (err error) { - stmt, err := db.Prepare(query) - if err != nil { - return - } - - _, err = stmt.Exec(args...) - if err != nil { - return - } - - return -} - -func main() { - var sqlite3conn *sqlite3.SQLiteConn - sql.Register("sqlite3_with_limit", &sqlite3.SQLiteDriver{ - ConnectHook: func(conn *sqlite3.SQLiteConn) error { - sqlite3conn = conn - return nil - }, - }) - - os.Remove("./foo.db") - db, err := sql.Open("sqlite3_with_limit", "./foo.db") - if err != nil { - log.Fatal(err) - } - defer db.Close() - - sqlStmt := ` - create table foo (id integer not null primary key, name text); - delete from foo; - ` - _, err = db.Exec(sqlStmt) - if err != nil { - log.Printf("%q: %s\n", err, sqlStmt) - return - } - - if sqlite3conn == nil { - log.Fatal("not set sqlite3 connection") - } - - limitVariableNumber := sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER) - log.Printf("default SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber) - - num := 400 - query, args := createBulkInsertQuery(num, 0) - err = bulkInsert(db, query, args) - if err != nil { - log.Fatal(err) - } - - smallLimitVariableNumber := 100 - sqlite3conn.SetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER, smallLimitVariableNumber) - - limitVariableNumber = sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER) - log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber) - - query, args = createBulkInsertQuery(num, num) - err = bulkInsert(db, query, args) - if err != nil { - if err != nil { - log.Printf("expect failed since SQLITE_LIMIT_VARIABLE_NUMBER is too small: %v", err) - } - } - - bigLimitVariableNumber := 999999 - sqlite3conn.SetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER, bigLimitVariableNumber) - limitVariableNumber = sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER) - log.Printf("set SQLITE_LIMIT_VARIABLE_NUMBER: %d", bigLimitVariableNumber) - log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber) - - query, args = createBulkInsertQuery(500, num+num) - err = bulkInsert(db, query, args) - if err != nil { - if err != nil { - log.Fatal(err) - } - } - - log.Println("no error if SQLITE_LIMIT_VARIABLE_NUMBER > 999") -} diff --git a/doc/examples/mod_regexp/Makefile b/doc/examples/mod_regexp/Makefile deleted file mode 100644 index 1ef69a6..0000000 --- a/doc/examples/mod_regexp/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -ifeq ($(OS),Windows_NT) -EXE=extension.exe -LIB_EXT=dll -RM=cmd /c del -LDFLAG= -else -EXE=extension -ifeq ($(shell uname -s),Darwin) -LIB_EXT=dylib -else -LIB_EXT=so -endif -RM=rm -f -LDFLAG=-fPIC -endif -LIB=sqlite3_mod_regexp.$(LIB_EXT) - -all : $(EXE) $(LIB) - -$(EXE) : extension.go - go build $< - -$(LIB) : sqlite3_mod_regexp.c - gcc $(LDFLAG) -shared -o $@ $< -lsqlite3 -lpcre - -clean : - @-$(RM) $(EXE) $(LIB) diff --git a/doc/examples/mod_regexp/extension.go b/doc/examples/mod_regexp/extension.go deleted file mode 100644 index 61ceb55..0000000 --- a/doc/examples/mod_regexp/extension.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - "github.com/mattn/go-sqlite3" - "log" -) - -func main() { - sql.Register("sqlite3_with_extensions", - &sqlite3.SQLiteDriver{ - Extensions: []string{ - "sqlite3_mod_regexp", - }, - }) - - db, err := sql.Open("sqlite3_with_extensions", ":memory:") - if err != nil { - log.Fatal(err) - } - defer db.Close() - - // Force db to make a new connection in pool - // by putting the original in a transaction - tx, err := db.Begin() - if err != nil { - log.Fatal(err) - } - defer tx.Commit() - - // New connection works (hopefully!) - rows, err := db.Query("select 'hello world' where 'hello world' regexp '^hello.*d$'") - if err != nil { - log.Fatal(err) - } - defer rows.Close() - for rows.Next() { - var helloworld string - rows.Scan(&helloworld) - fmt.Println(helloworld) - } -} diff --git a/doc/examples/mod_regexp/sqlite3_mod_regexp.c b/doc/examples/mod_regexp/sqlite3_mod_regexp.c deleted file mode 100644 index d3ad149..0000000 --- a/doc/examples/mod_regexp/sqlite3_mod_regexp.c +++ /dev/null @@ -1,35 +0,0 @@ -#include <pcre.h> -#include <string.h> -#include <stdio.h> -#include <sqlite3ext.h> - -SQLITE_EXTENSION_INIT1 -static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) { - if (argc >= 2) { - const char *target = (const char *)sqlite3_value_text(argv[1]); - const char *pattern = (const char *)sqlite3_value_text(argv[0]); - const char* errstr = NULL; - int erroff = 0; - int vec[500]; - int n, rc; - pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL); - if (!re) { - sqlite3_result_error(context, errstr, 0); - return; - } - rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); - if (rc <= 0) { - sqlite3_result_int(context, 0); - return; - } - sqlite3_result_int(context, 1); - } -} - -#ifdef _WIN32 -__declspec(dllexport) -#endif -int sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) { - SQLITE_EXTENSION_INIT2(api); - return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, (void*)db, regexp_func, NULL, NULL); -} @@ -2,3 +2,11 @@ set -eu export LANG=POSIX.UTF-8 + + +find tests/functional/*.go | varlist 'functional-tests.go' +find tests/functional/*.go -not -name extension.go | varlist 'functional-tests-butone.go' + +find tests/functional/*.go | sed 's/^\(.*\)\.go$/\1.a:\t\1.go/' +find tests/functional/*.go | sed 's/^\(.*\)\.go$/\1.bin:\t\1.a/' +find tests/functional/*.go | sed 's/^\(.*\)\.go$/\1.bin-check:\t\1.bin/' diff --git a/tests/functional/extension.go b/tests/functional/extension.go new file mode 100644 index 0000000..81c4494 --- /dev/null +++ b/tests/functional/extension.go @@ -0,0 +1,79 @@ +package main + +import ( + "database/sql" + "fmt" + "log" + "os" + "reflect" + + "golite" +) + + + +func fatalif(err error) { + if err != nil { + log.Fatal(err) + } +} + +func eq(given any, expected any) { + if !reflect.DeepEqual(given, expected) { + fmt.Fprintf(os.Stderr, "given != expected\n") + fmt.Fprintf(os.Stderr, "given: %#v\n", given) + fmt.Fprintf(os.Stderr, "expected: %#v\n", expected) + } +} + + + +func main() { + driver := golite.SQLiteDriver{ + Extensions: []string{ + "streq", + }, + } + sql.Register("sqlite3-streq", &driver) + + db, err := sql.Open("sqlite3-streq", ":memory:") + fatalif(err) + defer db.Close() + + rows, err := db.Query(` + SELECT str, n FROM ( + SELECT 'hello' as str, 1 as n + UNION + SELECT 'not' as str, 2 as n + UNION + SELECT 'hello' as str, 3 as n + ) WHERE streq(str, 'hello'); + `) + fatalif(err) + defer rows.Close() + + type pairT struct{ + str string + id int + } + var pairs []pairT + for rows.Next() { + var pair pairT + err := rows.Scan(&pair.str, &pair.id) + fatalif(err) + pairs = append(pairs, pair) + } + fatalif(rows.Err()) + + expected := []pairT{ + pairT{ + str: "hello", + id: 1, + }, + pairT{ + str: "hello", + id: 3, + }, + } + eq(pairs, expected) +} diff --git a/tests/functional/json.go b/tests/functional/json.go new file mode 100644 index 0000000..54954f7 --- /dev/null +++ b/tests/functional/json.go @@ -0,0 +1,96 @@ +package main + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "errors" + "log" + "os" + + _ "golite" +) + +type Tag struct { + Name string `json:"name"` + Place string `json:"place"` +} + +func (t *Tag) Scan(value interface{}) error { + return json.Unmarshal([]byte(value.(string)), t) +} + +func (t *Tag) Value() (driver.Value, error) { + b, err := json.Marshal(t) + return string(b), err +} + +func main() { + os.Remove("json.db") + defer os.Remove("json.db") + + db, err := sql.Open("sqlite3", "json.db") + if err != nil { + log.Fatal(err) + } + defer db.Close() + + _, err = db.Exec(`create table myjsontable (tag jsonb)`) + if err != nil { + log.Fatal(err) + } + + stmt, err := db.Prepare("insert into myjsontable(tag) values(?)") + if err != nil { + log.Fatal(err) + } + defer stmt.Close() + + _, err = stmt.Exec(`{"name": "name1", "place": "right-here"}`) + if err != nil { + log.Fatal(err) + } + + _, err = stmt.Exec(`{"name": "michael", "place": "usa"}`) + if err != nil { + log.Fatal(err) + } + + var place string + err = db.QueryRow("select tag->>'place' from myjsontable where tag->>'name' = 'name1'").Scan(&place) + if err != nil { + log.Fatal(err) + } + + if place != "right-here" { + log.Fatal(errors.New("expected right-here, got: " + place)) + } + + var tag Tag + err = db.QueryRow("select tag from myjsontable where tag->>'name' = 'name1'").Scan(&tag) + if err != nil { + log.Fatal(err) + } + + if tag.Name != "name1" { + log.Fatal(errors.New("expected name1, got: " + tag.Name)) + } + if tag.Place != "right-here" { + log.Fatal(errors.New("expected right-here, got: " + tag.Place)) + } + + tag.Place = "日本" + _, err = db.Exec(`update myjsontable set tag = ? where tag->>'name' == 'name1'`, &tag) + if err != nil { + log.Fatal(err) + } + + err = db.QueryRow("select tag->>'place' from myjsontable where tag->>'name' = 'name1'").Scan(&place) + if err != nil { + log.Fatal(err) + } + + if place != "日本" { + log.Fatal(errors.New("expected 日本, got: " + place)) + } +} diff --git a/tests/libbuild.go b/tests/functional/libbuild.go index 3de29ae..3de29ae 100644 --- a/tests/libbuild.go +++ b/tests/functional/libbuild.go diff --git a/tests/functional/limit.go b/tests/functional/limit.go new file mode 100644 index 0000000..8c65a25 --- /dev/null +++ b/tests/functional/limit.go @@ -0,0 +1,102 @@ +package main + +import ( + "database/sql" + "fmt" + "log" + "os" + "strings" + + "golite" +) + +func createBulkInsertQuery(n int, start int) (string, []any) { + values := make([]string, n) + args := make([]any, n*2) + pos := 0 + for i := 0; i < n; i++ { + values[i] = "(?, ?)" + args[pos] = start + i + args[pos+1] = fmt.Sprintf("こんにちは世界%03d", i) + pos += 2 + } + query := fmt.Sprintf( + "insert into mylimittable(id, name) values %s", + strings.Join(values, ", "), + ) + return query, args +} + +func bulkInsert(db *sql.DB, query string, args []any) error { + stmt, err := db.Prepare(query) + if err != nil { + return err + } + + _, err = stmt.Exec(args...) + return err +} + +func main() { + const ( + num = 400 + smallLimit = 100 + bigLimit = 999999 + ) + + const SQL = ` + create table mylimittable (id integer not null primary key, name text); + delete from mylimittable; + ` + + var conn *golite.SQLiteConn + sql.Register("sqlite3_with_limit", &golite.SQLiteDriver{ + ConnectHook: func(c *golite.SQLiteConn) error { + conn = c + return nil + }, + }) + + os.Remove("limit.db") + defer os.Remove("limit.db") + db, err := sql.Open("sqlite3_with_limit", "limit.db") + if err != nil { + log.Fatal(err) + } + defer db.Close() + + _, err = db.Exec(SQL) + if err != nil { + log.Fatal(err) + } + + if conn == nil { + log.Fatal("not set sqlite3 connection") + } + + { + query, args := createBulkInsertQuery(num, 0) + err := bulkInsert(db, query, args) + if err != nil { + log.Fatal(err) + } + } + + conn.SetLimit(golite.SQLITE_LIMIT_VARIABLE_NUMBER, smallLimit) + { + query, args := createBulkInsertQuery(num, num) + err := bulkInsert(db, query, args) + if err == nil { + log.Fatal("expected failure didn't happen") + } + } + + conn.SetLimit(golite.SQLITE_LIMIT_VARIABLE_NUMBER, bigLimit) + { + query, args := createBulkInsertQuery(500, num+num) + err := bulkInsert(db, query, args) + if err != nil { + log.Fatal(err) + } + } +} diff --git a/tests/functional/streq.c b/tests/functional/streq.c new file mode 100644 index 0000000..bbf3763 --- /dev/null +++ b/tests/functional/streq.c @@ -0,0 +1,36 @@ +#include <assert.h> +#include <stdbool.h> +#include <string.h> +#include <sqlite3ext.h> + + + +SQLITE_EXTENSION_INIT1 +static void +streq(sqlite3_context *const ctx, const int argc, sqlite3_value **const argv) { + assert(argc == 2); + const char *const str1 = (const char *)sqlite3_value_text(argv[0]); + const char *const str2 = (const char *)sqlite3_value_text(argv[1]); + const bool equal = strcmp(str1, str2) == 0; + const int result = equal ? 1 : 0; + sqlite3_result_int(ctx, result); +} + +int +sqlite3_extension_init( + sqlite3 *const db, + const char *const *const errmsg, + const sqlite3_api_routines *const api +) { + SQLITE_EXTENSION_INIT2(api); + return sqlite3_create_function( + db, + "streq", + 2, + SQLITE_UTF8, + (void *)db, + streq, + NULL, + NULL + ); +} |