go-sqlite3-1.4.0/000077500000000000000000000000001320252770200135035ustar00rootroot00000000000000go-sqlite3-1.4.0/.gitignore000066400000000000000000000000251320252770200154700ustar00rootroot00000000000000*.db *.exe *.dll *.o go-sqlite3-1.4.0/.travis.yml000066400000000000000000000005711320252770200156170ustar00rootroot00000000000000language: go sudo: required dist: trusty env: - GOTAGS= - GOTAGS=libsqlite3 - GOTAGS=trace - GOTAGS=vtable go: - 1.7.x - 1.8.x - 1.9.x - master before_install: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/cover script: - $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx - go test -race -v . -tags "$GOTAGS" go-sqlite3-1.4.0/LICENSE000066400000000000000000000020751320252770200145140ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. go-sqlite3-1.4.0/README.md000066400000000000000000000066431320252770200147730ustar00rootroot00000000000000go-sqlite3 ========== [![GoDoc Reference](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3) [![Build Status](https://travis-ci.org/mattn/go-sqlite3.svg?branch=master)](https://travis-ci.org/mattn/go-sqlite3) [![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.svg?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-sqlite3)](https://goreportcard.com/report/github.com/mattn/go-sqlite3) Description ----------- sqlite3 driver conforming to the built-in database/sql interface Installation ------------ This package can be installed with the go get command: go get github.com/mattn/go-sqlite3 _go-sqlite3_ is *cgo* package. If you want to build your app using go-sqlite3, you need gcc. However, if you install _go-sqlite3_ with `go install github.com/mattn/go-sqlite3`, you don't need gcc to build your app anymore. Documentation ------------- API documentation can be found here: http://godoc.org/github.com/mattn/go-sqlite3 Examples can be found under the `./_example` directory FAQ --- * Want to build go-sqlite3 with libsqlite3 on my linux. Use `go build --tags "libsqlite3 linux"` * Want to build go-sqlite3 with libsqlite3 on OS X. Install sqlite3 from homebrew: `brew install sqlite3` Use `go build --tags "libsqlite3 darwin"` * Want to build go-sqlite3 with icu extension. Use `go build --tags "icu"` Available extensions: `json1`, `fts5`, `icu` * Can't build go-sqlite3 on windows 64bit. > Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit. > See: [#27](https://github.com/mattn/go-sqlite3/issues/27) * Getting insert error while query is opened. > You can pass some arguments into the connection string, for example, a URI. > See: [#39](https://github.com/mattn/go-sqlite3/issues/39) * Do you want to cross compile? mingw on Linux or Mac? > See: [#106](https://github.com/mattn/go-sqlite3/issues/106) > See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html * Want to get time.Time with current locale Use `_loc=auto` in SQLite3 filename schema like `file:foo.db?_loc=auto`. * Can I use this in multiple routines concurrently? Yes for readonly. But, No for writable. See [#50](https://github.com/mattn/go-sqlite3/issues/50), [#51](https://github.com/mattn/go-sqlite3/issues/51), [#209](https://github.com/mattn/go-sqlite3/issues/209). * Why is it racy if I use a `sql.Open("sqlite3", ":memory:")` database? Each connection to :memory: opens a brand new in-memory sql database, so if the stdlib's sql engine happens to open another connection and you've only specified ":memory:", that connection will see a brand new database. A workaround is to use "file::memory:?mode=memory&cache=shared". Every connection to this string will point to the same in-memory database. See [#204](https://github.com/mattn/go-sqlite3/issues/204) for more info. License ------- MIT: http://mattn.mit-license.org/2012 sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h The -binding suffix was added to avoid build failures under gccgo. In this repository, those files are an amalgamation of code that was copied from SQLite3. The license of that code is the same as the license of SQLite3. Author ------ Yasuhiro Matsumoto (a.k.a mattn) go-sqlite3-1.4.0/_example/000077500000000000000000000000001320252770200152755ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/custom_func/000077500000000000000000000000001320252770200176225ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/custom_func/main.go000066400000000000000000000055711320252770200211050ustar00rootroot00000000000000package main import ( "database/sql" "fmt" "log" "math" "math/rand" sqlite "github.com/mattn/go-sqlite3" ) // Computes x^y func pow(x, y int64) int64 { return int64(math.Pow(float64(x), float64(y))) } // Computes the bitwise exclusive-or of all its arguments func xor(xs ...int64) int64 { var ret int64 for _, x := range xs { ret ^= x } return ret } // Returns a random number. It's actually deterministic here because // we don't seed the RNG, but it's an example of a non-pure function // from SQLite's POV. func getrand() int64 { return rand.Int63() } // Computes the standard deviation of a GROUPed BY set of values type stddev struct { xs []int64 // Running average calculation sum int64 n int64 } func newStddev() *stddev { return &stddev{} } func (s *stddev) Step(x int64) { s.xs = append(s.xs, x) s.sum += x s.n++ } func (s *stddev) Done() float64 { mean := float64(s.sum) / float64(s.n) var sqDiff []float64 for _, x := range s.xs { sqDiff = append(sqDiff, math.Pow(float64(x)-mean, 2)) } var dev float64 for _, x := range sqDiff { dev += x } dev /= float64(len(sqDiff)) return math.Sqrt(dev) } func main() { sql.Register("sqlite3_custom", &sqlite.SQLiteDriver{ ConnectHook: func(conn *sqlite.SQLiteConn) error { if err := conn.RegisterFunc("pow", pow, true); err != nil { return err } if err := conn.RegisterFunc("xor", xor, true); err != nil { return err } if err := conn.RegisterFunc("rand", getrand, false); err != nil { return err } if err := conn.RegisterAggregator("stddev", newStddev, true); err != nil { return err } return nil }, }) db, err := sql.Open("sqlite3_custom", ":memory:") if err != nil { log.Fatal("Failed to open database:", err) } defer db.Close() var i int64 err = db.QueryRow("SELECT pow(2,3)").Scan(&i) if err != nil { log.Fatal("POW query error:", err) } fmt.Println("pow(2,3) =", i) // 8 err = db.QueryRow("SELECT xor(1,2,3,4,5,6)").Scan(&i) if err != nil { log.Fatal("XOR query error:", err) } fmt.Println("xor(1,2,3,4,5) =", i) // 7 err = db.QueryRow("SELECT rand()").Scan(&i) if err != nil { log.Fatal("RAND query error:", err) } fmt.Println("rand() =", i) // pseudorandom _, err = db.Exec("create table foo (department integer, profits integer)") if err != nil { log.Fatal("Failed to create table:", err) } _, err = db.Exec("insert into foo values (1, 10), (1, 20), (1, 45), (2, 42), (2, 115)") if err != nil { log.Fatal("Failed to insert records:", err) } rows, err := db.Query("select department, stddev(profits) from foo group by department") if err != nil { log.Fatal("STDDEV query error:", err) } defer rows.Close() for rows.Next() { var dept int64 var dev float64 if err := rows.Scan(&dept, &dev); err != nil { log.Fatal(err) } fmt.Printf("dept=%d stddev=%f\n", dept, dev) } if err := rows.Err(); err != nil { log.Fatal(err) } } go-sqlite3-1.4.0/_example/hook/000077500000000000000000000000001320252770200162355ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/hook/hook.go000066400000000000000000000031101320252770200175170ustar00rootroot00000000000000package main import ( "database/sql" "log" "os" "github.com/mattn/go-sqlite3" ) func main() { sqlite3conn := []*sqlite3.SQLiteConn{} sql.Register("sqlite3_with_hook_example", &sqlite3.SQLiteDriver{ ConnectHook: func(conn *sqlite3.SQLiteConn) error { sqlite3conn = append(sqlite3conn, conn) conn.RegisterUpdateHook(func(op int, db string, table string, rowid int64) { switch op { case sqlite3.SQLITE_INSERT: log.Println("Notified of insert on db", db, "table", table, "rowid", rowid) } }) return nil }, }) os.Remove("./foo.db") os.Remove("./bar.db") srcDb, err := sql.Open("sqlite3_with_hook_example", "./foo.db") if err != nil { log.Fatal(err) } defer srcDb.Close() srcDb.Ping() _, err = srcDb.Exec("create table foo(id int, value text)") if err != nil { log.Fatal(err) } _, err = srcDb.Exec("insert into foo values(1, 'foo')") if err != nil { log.Fatal(err) } _, err = srcDb.Exec("insert into foo values(2, 'bar')") if err != nil { log.Fatal(err) } _, err = srcDb.Query("select * from foo") if err != nil { log.Fatal(err) } destDb, err := sql.Open("sqlite3_with_hook_example", "./bar.db") if err != nil { log.Fatal(err) } defer destDb.Close() destDb.Ping() bk, err := sqlite3conn[1].Backup("main", sqlite3conn[0], "main") if err != nil { log.Fatal(err) } _, err = bk.Step(-1) if err != nil { log.Fatal(err) } _, err = destDb.Query("select * from foo") if err != nil { log.Fatal(err) } _, err = destDb.Exec("insert into foo values(3, 'bar')") if err != nil { log.Fatal(err) } bk.Finish() } go-sqlite3-1.4.0/_example/limit/000077500000000000000000000000001320252770200164135ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/limit/limit.go000066400000000000000000000051661320252770200200700ustar00rootroot00000000000000package main import ( "database/sql" "fmt" "log" "os" "strings" "github.com/mattn/go-sqlite3" ) func createBulkInsertQuery(n int, start int) (query string, args []interface{}) { values := make([]string, n) args = make([]interface{}, 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 bukInsert(db *sql.DB, query string, args []interface{}) (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 = bukInsert(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 = bukInsert(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 = bukInsert(db, query, args) if err != nil { if err != nil { log.Fatal(err) } } log.Println("no error if SQLITE_LIMIT_VARIABLE_NUMBER > 999") } go-sqlite3-1.4.0/_example/mod_regexp/000077500000000000000000000000001320252770200174265ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/mod_regexp/Makefile000066400000000000000000000005121320252770200210640ustar00rootroot00000000000000ifeq ($(OS),Windows_NT) EXE=extension.exe EXT=sqlite3_mod_regexp.dll RM=cmd /c del LDFLAG= else EXE=extension EXT=sqlite3_mod_regexp.so RM=rm LDFLAG=-fPIC endif all : $(EXE) $(EXT) $(EXE) : extension.go go build $< $(EXT) : sqlite3_mod_regexp.c gcc $(LDFLAG) -shared -o $@ $< -lsqlite3 -lpcre clean : @-$(RM) $(EXE) $(EXT) go-sqlite3-1.4.0/_example/mod_regexp/extension.go000066400000000000000000000014361320252770200217750ustar00rootroot00000000000000package 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) } } go-sqlite3-1.4.0/_example/mod_regexp/sqlite3_mod_regexp.c000066400000000000000000000017021320252770200233670ustar00rootroot00000000000000#include #include #include #include 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); rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); if (rc <= 0) { sqlite3_result_error(context, errstr, 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); } go-sqlite3-1.4.0/_example/mod_vtable/000077500000000000000000000000001320252770200174115ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/mod_vtable/Makefile000066400000000000000000000005601320252770200210520ustar00rootroot00000000000000ifeq ($(OS),Windows_NT) EXE=extension.exe EXT=sqlite3_mod_vtable.dll RM=cmd /c del LIBCURL=-lcurldll LDFLAG= else EXE=extension EXT=sqlite3_mod_vtable.so RM=rm LDFLAG=-fPIC LIBCURL=-lcurl endif all : $(EXE) $(EXT) $(EXE) : extension.go go build $< $(EXT) : sqlite3_mod_vtable.cc g++ $(LDFLAG) -shared -o $@ $< -lsqlite3 $(LIBCURL) clean : @-$(RM) $(EXE) $(EXT) go-sqlite3-1.4.0/_example/mod_vtable/extension.go000066400000000000000000000014051320252770200217540ustar00rootroot00000000000000package main import ( "database/sql" "fmt" "log" "github.com/mattn/go-sqlite3" ) func main() { sql.Register("sqlite3_with_extensions", &sqlite3.SQLiteDriver{ Extensions: []string{ "sqlite3_mod_vtable", }, }) db, err := sql.Open("sqlite3_with_extensions", ":memory:") if err != nil { log.Fatal(err) } defer db.Close() db.Exec("create virtual table repo using github(id, full_name, description, html_url)") rows, err := db.Query("select id, full_name, description, html_url from repo") if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id, fullName, description, htmlURL string rows.Scan(&id, &fullName, &description, &htmlURL) fmt.Printf("%s: %s\n\t%s\n\t%s\n\n", id, fullName, description, htmlURL) } } go-sqlite3-1.4.0/_example/mod_vtable/picojson.h000066400000000000000000000665741320252770200214300ustar00rootroot00000000000000/* * Copyright 2009-2010 Cybozu Labs, Inc. * Copyright 2011 Kazuho Oku * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY CYBOZU LABS, INC. ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL CYBOZU LABS, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of Cybozu Labs, Inc. * */ #ifndef picojson_h #define picojson_h #include #include #include #include #include #include #include #include #include #include #include #ifdef _MSC_VER #define SNPRINTF _snprintf_s #pragma warning(push) #pragma warning(disable : 4244) // conversion from int to char #else #define SNPRINTF snprintf #endif namespace picojson { enum { null_type, boolean_type, number_type, string_type, array_type, object_type }; struct null {}; class value { public: typedef std::vector array; typedef std::map object; union _storage { bool boolean_; double number_; std::string* string_; array* array_; object* object_; }; protected: int type_; _storage u_; public: value(); value(int type, bool); explicit value(bool b); explicit value(double n); explicit value(const std::string& s); explicit value(const array& a); explicit value(const object& o); explicit value(const char* s); value(const char* s, size_t len); ~value(); value(const value& x); value& operator=(const value& x); void swap(value& x); template bool is() const; template const T& get() const; template T& get(); bool evaluate_as_boolean() const; const value& get(size_t idx) const; const value& get(const std::string& key) const; bool contains(size_t idx) const; bool contains(const std::string& key) const; std::string to_str() const; template void serialize(Iter os) const; std::string serialize() const; private: template value(const T*); // intentionally defined to block implicit conversion of pointer to bool }; typedef value::array array; typedef value::object object; inline value::value() : type_(null_type) {} inline value::value(int type, bool) : type_(type) { switch (type) { #define INIT(p, v) case p##type: u_.p = v; break INIT(boolean_, false); INIT(number_, 0.0); INIT(string_, new std::string()); INIT(array_, new array()); INIT(object_, new object()); #undef INIT default: break; } } inline value::value(bool b) : type_(boolean_type) { u_.boolean_ = b; } inline value::value(double n) : type_(number_type) { u_.number_ = n; } inline value::value(const std::string& s) : type_(string_type) { u_.string_ = new std::string(s); } inline value::value(const array& a) : type_(array_type) { u_.array_ = new array(a); } inline value::value(const object& o) : type_(object_type) { u_.object_ = new object(o); } inline value::value(const char* s) : type_(string_type) { u_.string_ = new std::string(s); } inline value::value(const char* s, size_t len) : type_(string_type) { u_.string_ = new std::string(s, len); } inline value::~value() { switch (type_) { #define DEINIT(p) case p##type: delete u_.p; break DEINIT(string_); DEINIT(array_); DEINIT(object_); #undef DEINIT default: break; } } inline value::value(const value& x) : type_(x.type_) { switch (type_) { #define INIT(p, v) case p##type: u_.p = v; break INIT(string_, new std::string(*x.u_.string_)); INIT(array_, new array(*x.u_.array_)); INIT(object_, new object(*x.u_.object_)); #undef INIT default: u_ = x.u_; break; } } inline value& value::operator=(const value& x) { if (this != &x) { this->~value(); new (this) value(x); } return *this; } inline void value::swap(value& x) { std::swap(type_, x.type_); std::swap(u_, x.u_); } #define IS(ctype, jtype) \ template <> inline bool value::is() const { \ return type_ == jtype##_type; \ } IS(null, null) IS(bool, boolean) IS(int, number) IS(double, number) IS(std::string, string) IS(array, array) IS(object, object) #undef IS #define GET(ctype, var) \ template <> inline const ctype& value::get() const { \ assert("type mismatch! call vis() before get()" \ && is()); \ return var; \ } \ template <> inline ctype& value::get() { \ assert("type mismatch! call is() before get()" \ && is()); \ return var; \ } GET(bool, u_.boolean_) GET(double, u_.number_) GET(std::string, *u_.string_) GET(array, *u_.array_) GET(object, *u_.object_) #undef GET inline bool value::evaluate_as_boolean() const { switch (type_) { case null_type: return false; case boolean_type: return u_.boolean_; case number_type: return u_.number_ != 0; case string_type: return ! u_.string_->empty(); default: return true; } } inline const value& value::get(size_t idx) const { static value s_null; assert(is()); return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } inline const value& value::get(const std::string& key) const { static value s_null; assert(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } inline bool value::contains(size_t idx) const { assert(is()); return idx < u_.array_->size(); } inline bool value::contains(const std::string& key) const { assert(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end(); } inline std::string value::to_str() const { switch (type_) { case null_type: return "null"; case boolean_type: return u_.boolean_ ? "true" : "false"; case number_type: { char buf[256]; double tmp; SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); return buf; } case string_type: return *u_.string_; case array_type: return "array"; case object_type: return "object"; default: assert(0); #ifdef _MSC_VER __assume(0); #endif } return std::string(); } template void copy(const std::string& s, Iter oi) { std::copy(s.begin(), s.end(), oi); } template void serialize_str(const std::string& s, Iter oi) { *oi++ = '"'; for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { switch (*i) { #define MAP(val, sym) case val: copy(sym, oi); break MAP('"', "\\\""); MAP('\\', "\\\\"); MAP('/', "\\/"); MAP('\b', "\\b"); MAP('\f', "\\f"); MAP('\n', "\\n"); MAP('\r', "\\r"); MAP('\t', "\\t"); #undef MAP default: if ((unsigned char)*i < 0x20 || *i == 0x7f) { char buf[7]; SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff); copy(buf, buf + 6, oi); } else { *oi++ = *i; } break; } } *oi++ = '"'; } template void value::serialize(Iter oi) const { switch (type_) { case string_type: serialize_str(*u_.string_, oi); break; case array_type: { *oi++ = '['; for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) { if (i != u_.array_->begin()) { *oi++ = ','; } i->serialize(oi); } *oi++ = ']'; break; } case object_type: { *oi++ = '{'; for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) { if (i != u_.object_->begin()) { *oi++ = ','; } serialize_str(i->first, oi); *oi++ = ':'; i->second.serialize(oi); } *oi++ = '}'; break; } default: copy(to_str(), oi); break; } } inline std::string value::serialize() const { std::string s; serialize(std::back_inserter(s)); return s; } template class input { protected: Iter cur_, end_; int last_ch_; bool ungot_; int line_; public: input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {} int getc() { if (ungot_) { ungot_ = false; return last_ch_; } if (cur_ == end_) { last_ch_ = -1; return -1; } if (last_ch_ == '\n') { line_++; } last_ch_ = *cur_++ & 0xff; return last_ch_; } void ungetc() { if (last_ch_ != -1) { assert(! ungot_); ungot_ = true; } } Iter cur() const { return cur_; } int line() const { return line_; } void skip_ws() { while (1) { int ch = getc(); if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { ungetc(); break; } } } bool expect(int expect) { skip_ws(); if (getc() != expect) { ungetc(); return false; } return true; } bool match(const std::string& pattern) { for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { if (getc() != *pi) { ungetc(); return false; } } return true; } }; template inline int _parse_quadhex(input &in) { int uni_ch = 0, hex; for (int i = 0; i < 4; i++) { if ((hex = in.getc()) == -1) { return -1; } if ('0' <= hex && hex <= '9') { hex -= '0'; } else if ('A' <= hex && hex <= 'F') { hex -= 'A' - 0xa; } else if ('a' <= hex && hex <= 'f') { hex -= 'a' - 0xa; } else { in.ungetc(); return -1; } uni_ch = uni_ch * 16 + hex; } return uni_ch; } template inline bool _parse_codepoint(String& out, input& in) { int uni_ch; if ((uni_ch = _parse_quadhex(in)) == -1) { return false; } if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { if (0xdc00 <= uni_ch) { // a second 16-bit of a surrogate pair appeared return false; } // first 16-bit of surrogate pair, get the next one if (in.getc() != '\\' || in.getc() != 'u') { in.ungetc(); return false; } int second = _parse_quadhex(in); if (! (0xdc00 <= second && second <= 0xdfff)) { return false; } uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); uni_ch += 0x10000; } if (uni_ch < 0x80) { out.push_back(uni_ch); } else { if (uni_ch < 0x800) { out.push_back(0xc0 | (uni_ch >> 6)); } else { if (uni_ch < 0x10000) { out.push_back(0xe0 | (uni_ch >> 12)); } else { out.push_back(0xf0 | (uni_ch >> 18)); out.push_back(0x80 | ((uni_ch >> 12) & 0x3f)); } out.push_back(0x80 | ((uni_ch >> 6) & 0x3f)); } out.push_back(0x80 | (uni_ch & 0x3f)); } return true; } template inline bool _parse_string(String& out, input& in) { while (1) { int ch = in.getc(); if (ch < ' ') { in.ungetc(); return false; } else if (ch == '"') { return true; } else if (ch == '\\') { if ((ch = in.getc()) == -1) { return false; } switch (ch) { #define MAP(sym, val) case sym: out.push_back(val); break MAP('"', '\"'); MAP('\\', '\\'); MAP('/', '/'); MAP('b', '\b'); MAP('f', '\f'); MAP('n', '\n'); MAP('r', '\r'); MAP('t', '\t'); #undef MAP case 'u': if (! _parse_codepoint(out, in)) { return false; } break; default: return false; } } else { out.push_back(ch); } } return false; } template inline bool _parse_array(Context& ctx, input& in) { if (! ctx.parse_array_start()) { return false; } size_t idx = 0; if (in.expect(']')) { return ctx.parse_array_stop(idx); } do { if (! ctx.parse_array_item(in, idx)) { return false; } idx++; } while (in.expect(',')); return in.expect(']') && ctx.parse_array_stop(idx); } template inline bool _parse_object(Context& ctx, input& in) { if (! ctx.parse_object_start()) { return false; } if (in.expect('}')) { return true; } do { std::string key; if (! in.expect('"') || ! _parse_string(key, in) || ! in.expect(':')) { return false; } if (! ctx.parse_object_item(in, key)) { return false; } } while (in.expect(',')); return in.expect('}'); } template inline bool _parse_number(double& out, input& in) { std::string num_str; while (1) { int ch = in.getc(); if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == '.' || ch == 'e' || ch == 'E') { num_str.push_back(ch); } else { in.ungetc(); break; } } char* endp; out = strtod(num_str.c_str(), &endp); return endp == num_str.c_str() + num_str.size(); } template inline bool _parse(Context& ctx, input& in) { in.skip_ws(); int ch = in.getc(); switch (ch) { #define IS(ch, text, op) case ch: \ if (in.match(text) && op) { \ return true; \ } else { \ return false; \ } IS('n', "ull", ctx.set_null()); IS('f', "alse", ctx.set_bool(false)); IS('t', "rue", ctx.set_bool(true)); #undef IS case '"': return ctx.parse_string(in); case '[': return _parse_array(ctx, in); case '{': return _parse_object(ctx, in); default: if (('0' <= ch && ch <= '9') || ch == '-') { in.ungetc(); double f; if (_parse_number(f, in)) { ctx.set_number(f); return true; } else { return false; } } break; } in.ungetc(); return false; } class deny_parse_context { public: bool set_null() { return false; } bool set_bool(bool) { return false; } bool set_number(double) { return false; } template bool parse_string(input&) { return false; } bool parse_array_start() { return false; } template bool parse_array_item(input&, size_t) { return false; } bool parse_array_stop(size_t) { return false; } bool parse_object_start() { return false; } template bool parse_object_item(input&, const std::string&) { return false; } }; class default_parse_context { protected: value* out_; public: default_parse_context(value* out) : out_(out) {} bool set_null() { *out_ = value(); return true; } bool set_bool(bool b) { *out_ = value(b); return true; } bool set_number(double f) { *out_ = value(f); return true; } template bool parse_string(input& in) { *out_ = value(string_type, false); return _parse_string(out_->get(), in); } bool parse_array_start() { *out_ = value(array_type, false); return true; } template bool parse_array_item(input& in, size_t) { array& a = out_->get(); a.push_back(value()); default_parse_context ctx(&a.back()); return _parse(ctx, in); } bool parse_array_stop(size_t) { return true; } bool parse_object_start() { *out_ = value(object_type, false); return true; } template bool parse_object_item(input& in, const std::string& key) { object& o = out_->get(); default_parse_context ctx(&o[key]); return _parse(ctx, in); } private: default_parse_context(const default_parse_context&); default_parse_context& operator=(const default_parse_context&); }; class null_parse_context { public: struct dummy_str { void push_back(int) {} }; public: null_parse_context() {} bool set_null() { return true; } bool set_bool(bool) { return true; } bool set_number(double) { return true; } template bool parse_string(input& in) { dummy_str s; return _parse_string(s, in); } bool parse_array_start() { return true; } template bool parse_array_item(input& in, size_t) { return _parse(*this, in); } bool parse_array_stop(size_t) { return true; } bool parse_object_start() { return true; } template bool parse_object_item(input& in, const std::string&) { return _parse(*this, in); } private: null_parse_context(const null_parse_context&); null_parse_context& operator=(const null_parse_context&); }; // obsolete, use the version below template inline std::string parse(value& out, Iter& pos, const Iter& last) { std::string err; pos = parse(out, pos, last, &err); return err; } template inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) { input in(first, last); if (! _parse(ctx, in) && err != NULL) { char buf[64]; SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); *err = buf; while (1) { int ch = in.getc(); if (ch == -1 || ch == '\n') { break; } else if (ch >= ' ') { err->push_back(ch); } } } return in.cur(); } template inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) { default_parse_context ctx(&out); return _parse(ctx, first, last, err); } inline std::string parse(value& out, std::istream& is) { std::string err; parse(out, std::istreambuf_iterator(is.rdbuf()), std::istreambuf_iterator(), &err); return err; } template struct last_error_t { static std::string s; }; template std::string last_error_t::s; inline void set_last_error(const std::string& s) { last_error_t::s = s; } inline const std::string& get_last_error() { return last_error_t::s; } inline bool operator==(const value& x, const value& y) { if (x.is()) return y.is(); #define PICOJSON_CMP(type) \ if (x.is()) \ return y.is() && x.get() == y.get() PICOJSON_CMP(bool); PICOJSON_CMP(double); PICOJSON_CMP(std::string); PICOJSON_CMP(array); PICOJSON_CMP(object); #undef PICOJSON_CMP assert(0); #ifdef _MSC_VER __assume(0); #endif return false; } inline bool operator!=(const value& x, const value& y) { return ! (x == y); } } namespace std { template<> inline void swap(picojson::value& x, picojson::value& y) { x.swap(y); } } inline std::istream& operator>>(std::istream& is, picojson::value& x) { picojson::set_last_error(std::string()); std::string err = picojson::parse(x, is); if (! err.empty()) { picojson::set_last_error(err); is.setstate(std::ios::failbit); } return is; } inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) { x.serialize(std::ostream_iterator(os)); return os; } #ifdef _MSC_VER #pragma warning(pop) #endif #endif #ifdef TEST_PICOJSON #ifdef _MSC_VER #pragma warning(disable : 4127) // conditional expression is constant #endif using namespace std; static void plan(int num) { printf("1..%d\n", num); } static bool success = true; static void ok(bool b, const char* name = "") { static int n = 1; if (! b) success = false; printf("%s %d - %s\n", b ? "ok" : "ng", n++, name); } template void is(const T& x, const T& y, const char* name = "") { if (x == y) { ok(true, name); } else { ok(false, name); } } #include #include #include #include int main(void) { plan(85); // constructors #define TEST(expr, expected) \ is(picojson::value expr .serialize(), string(expected), "picojson::value" #expr) TEST( (true), "true"); TEST( (false), "false"); TEST( (42.0), "42"); TEST( (string("hello")), "\"hello\""); TEST( ("hello"), "\"hello\""); TEST( ("hello", 4), "\"hell\""); { double a = 1; for (int i = 0; i < 1024; i++) { picojson::value vi(a); std::stringstream ss; ss << vi; picojson::value vo; ss >> vo; double b = vo.get(); if ((i < 53 && a != b) || fabs(a - b) / b > 1e-8) { printf("ng i=%d a=%.18e b=%.18e\n", i, a, b); } a *= 2; } } #undef TEST #define TEST(in, type, cmp, serialize_test) { \ picojson::value v; \ const char* s = in; \ string err = picojson::parse(v, s, s + strlen(s)); \ ok(err.empty(), in " no error"); \ ok(v.is(), in " check type"); \ is(v.get(), cmp, in " correct output"); \ is(*s, '\0', in " read to eof"); \ if (serialize_test) { \ is(v.serialize(), string(in), in " serialize"); \ } \ } TEST("false", bool, false, true); TEST("true", bool, true, true); TEST("90.5", double, 90.5, false); TEST("1.7976931348623157e+308", double, DBL_MAX, false); TEST("\"hello\"", string, string("hello"), true); TEST("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"", string, string("\"\\/\b\f\n\r\t"), true); TEST("\"\\u0061\\u30af\\u30ea\\u30b9\"", string, string("a\xe3\x82\xaf\xe3\x83\xaa\xe3\x82\xb9"), false); TEST("\"\\ud840\\udc0b\"", string, string("\xf0\xa0\x80\x8b"), false); #undef TEST #define TEST(type, expr) { \ picojson::value v; \ const char *s = expr; \ string err = picojson::parse(v, s, s + strlen(s)); \ ok(err.empty(), "empty " #type " no error"); \ ok(v.is(), "empty " #type " check type"); \ ok(v.get().empty(), "check " #type " array size"); \ } TEST(array, "[]"); TEST(object, "{}"); #undef TEST { picojson::value v; const char *s = "[1,true,\"hello\"]"; string err = picojson::parse(v, s, s + strlen(s)); ok(err.empty(), "array no error"); ok(v.is(), "array check type"); is(v.get().size(), size_t(3), "check array size"); ok(v.contains(0), "check contains array[0]"); ok(v.get(0).is(), "check array[0] type"); is(v.get(0).get(), 1.0, "check array[0] value"); ok(v.contains(1), "check contains array[1]"); ok(v.get(1).is(), "check array[1] type"); ok(v.get(1).get(), "check array[1] value"); ok(v.contains(2), "check contains array[2]"); ok(v.get(2).is(), "check array[2] type"); is(v.get(2).get(), string("hello"), "check array[2] value"); ok(!v.contains(3), "check not contains array[3]"); } { picojson::value v; const char *s = "{ \"a\": true }"; string err = picojson::parse(v, s, s + strlen(s)); ok(err.empty(), "object no error"); ok(v.is(), "object check type"); is(v.get().size(), size_t(1), "check object size"); ok(v.contains("a"), "check contains property"); ok(v.get("a").is(), "check bool property exists"); is(v.get("a").get(), true, "check bool property value"); is(v.serialize(), string("{\"a\":true}"), "serialize object"); ok(!v.contains("z"), "check not contains property"); } #define TEST(json, msg) do { \ picojson::value v; \ const char *s = json; \ string err = picojson::parse(v, s, s + strlen(s)); \ is(err, string("syntax error at line " msg), msg); \ } while (0) TEST("falsoa", "1 near: oa"); TEST("{]", "1 near: ]"); TEST("\n\bbell", "2 near: bell"); TEST("\"abc\nd\"", "1 near: "); #undef TEST { picojson::value v1, v2; const char *s; string err; s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; err = picojson::parse(v1, s, s + strlen(s)); s = "{ \"d\": 2.0, \"b\": true, \"a\": [1,2,\"three\"] }"; err = picojson::parse(v2, s, s + strlen(s)); ok((v1 == v2), "check == operator in deep comparison"); } { picojson::value v1, v2; const char *s; string err; s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; err = picojson::parse(v1, s, s + strlen(s)); s = "{ \"d\": 2.0, \"a\": [1,\"three\"], \"b\": true }"; err = picojson::parse(v2, s, s + strlen(s)); ok((v1 != v2), "check != operator for array in deep comparison"); } { picojson::value v1, v2; const char *s; string err; s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; err = picojson::parse(v1, s, s + strlen(s)); s = "{ \"d\": 2.0, \"a\": [1,2,\"three\"], \"b\": false }"; err = picojson::parse(v2, s, s + strlen(s)); ok((v1 != v2), "check != operator for object in deep comparison"); } { picojson::value v1, v2; const char *s; string err; s = "{ \"b\": true, \"a\": [1,2,\"three\"], \"d\": 2 }"; err = picojson::parse(v1, s, s + strlen(s)); picojson::object& o = v1.get(); o.erase("b"); picojson::array& a = o["a"].get(); picojson::array::iterator i; i = std::remove(a.begin(), a.end(), picojson::value(std::string("three"))); a.erase(i, a.end()); s = "{ \"a\": [1,2], \"d\": 2 }"; err = picojson::parse(v2, s, s + strlen(s)); ok((v1 == v2), "check erase()"); } ok(picojson::value(3.0).serialize() == "3", "integral number should be serialized as a integer"); { const char* s = "{ \"a\": [1,2], \"d\": 2 }"; picojson::null_parse_context ctx; string err; picojson::_parse(ctx, s, s + strlen(s), &err); ok(err.empty(), "null_parse_context"); } { picojson::value v1, v2; v1 = picojson::value(true); swap(v1, v2); ok(v1.is(), "swap (null)"); ok(v2.get() == true, "swap (bool)"); v1 = picojson::value("a"); v2 = picojson::value(1.0); swap(v1, v2); ok(v1.get() == 1.0, "swap (dobule)"); ok(v2.get() == "a", "swap (string)"); v1 = picojson::value(picojson::object()); v2 = picojson::value(picojson::array()); swap(v1, v2); ok(v1.is(), "swap (array)"); ok(v2.is(), "swap (object)"); } return success ? 0 : 1; } #endif go-sqlite3-1.4.0/_example/mod_vtable/sqlite3_mod_vtable.cc000066400000000000000000000125151320252770200235040ustar00rootroot00000000000000#include #include #include #include #include #include "picojson.h" #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT #endif SQLITE_EXTENSION_INIT1; typedef struct { char* data; // response data from server size_t size; // response size of data } MEMFILE; MEMFILE* memfopen() { MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE)); if (mf) { mf->data = NULL; mf->size = 0; } return mf; } void memfclose(MEMFILE* mf) { if (mf->data) free(mf->data); free(mf); } size_t memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) { MEMFILE* mf = (MEMFILE*) stream; int block = size * nmemb; if (!mf) return block; // through if (!mf->data) mf->data = (char*) malloc(block); else mf->data = (char*) realloc(mf->data, mf->size + block); if (mf->data) { memcpy(mf->data + mf->size, ptr, block); mf->size += block; } return block; } char* memfstrdup(MEMFILE* mf) { char* buf; if (mf->size == 0) return NULL; buf = (char*) malloc(mf->size + 1); memcpy(buf, mf->data, mf->size); buf[mf->size] = 0; return buf; } static int my_connect(sqlite3 *db, void *pAux, int argc, const char * const *argv, sqlite3_vtab **ppVTab, char **c) { std::stringstream ss; ss << "CREATE TABLE " << argv[0] << "(id int, full_name text, description text, html_url text)"; int rc = sqlite3_declare_vtab(db, ss.str().c_str()); *ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab)); memset(*ppVTab, 0, sizeof(sqlite3_vtab)); return rc; } static int my_create(sqlite3 *db, void *pAux, int argc, const char * const * argv, sqlite3_vtab **ppVTab, char **c) { return my_connect(db, pAux, argc, argv, ppVTab, c); } static int my_disconnect(sqlite3_vtab *pVTab) { sqlite3_free(pVTab); return SQLITE_OK; } static int my_destroy(sqlite3_vtab *pVTab) { sqlite3_free(pVTab); return SQLITE_OK; } typedef struct { sqlite3_vtab_cursor base; int index; picojson::value* rows; } cursor; static int my_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { MEMFILE* mf; CURL* curl; char* json; CURLcode res = CURLE_OK; char error[CURL_ERROR_SIZE] = {0}; char* cert_file = getenv("SSL_CERT_FILE"); mf = memfopen(); curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.29.0"); curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repositories"); if (cert_file) curl_easy_setopt(curl, CURLOPT_CAINFO, cert_file); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error); curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); res = curl_easy_perform(curl); curl_easy_cleanup(curl); if (res != CURLE_OK) { std::cerr << error << std::endl; return SQLITE_FAIL; } picojson::value* v = new picojson::value; std::string err; picojson::parse(*v, mf->data, mf->data + mf->size, &err); memfclose(mf); if (!err.empty()) { delete v; std::cerr << err << std::endl; return SQLITE_FAIL; } cursor *c = (cursor *)sqlite3_malloc(sizeof(cursor)); c->rows = v; c->index = 0; *ppCursor = &c->base; return SQLITE_OK; } static int my_close(cursor *c) { delete c->rows; sqlite3_free(c); return SQLITE_OK; } static int my_filter(cursor *c, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) { c->index = 0; return SQLITE_OK; } static int my_next(cursor *c) { c->index++; return SQLITE_OK; } static int my_eof(cursor *c) { return c->index >= c->rows->get().size() ? 1 : 0; } static int my_column(cursor *c, sqlite3_context *ctxt, int i) { picojson::value v = c->rows->get()[c->index]; picojson::object row = v.get(); const char* p = NULL; switch (i) { case 0: p = row["id"].to_str().c_str(); break; case 1: p = row["full_name"].to_str().c_str(); break; case 2: p = row["description"].to_str().c_str(); break; case 3: p = row["html_url"].to_str().c_str(); break; } sqlite3_result_text(ctxt, strdup(p), strlen(p), free); return SQLITE_OK; } static int my_rowid(cursor *c, sqlite3_int64 *pRowid) { *pRowid = c->index; return SQLITE_OK; } static int my_bestindex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) { return SQLITE_OK; } static const sqlite3_module module = { 0, my_create, my_connect, my_bestindex, my_disconnect, my_destroy, my_open, (int (*)(sqlite3_vtab_cursor *)) my_close, (int (*)(sqlite3_vtab_cursor *, int, char const *, int, sqlite3_value **)) my_filter, (int (*)(sqlite3_vtab_cursor *)) my_next, (int (*)(sqlite3_vtab_cursor *)) my_eof, (int (*)(sqlite3_vtab_cursor *, sqlite3_context *, int)) my_column, (int (*)(sqlite3_vtab_cursor *, sqlite3_int64 *)) my_rowid, NULL, // my_update NULL, // my_begin NULL, // my_sync NULL, // my_commit NULL, // my_rollback NULL, // my_findfunction NULL, // my_rename }; static void destructor(void *arg) { return; } extern "C" { EXPORT int sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) { SQLITE_EXTENSION_INIT2(api); sqlite3_create_module_v2(db, "github", &module, NULL, destructor); return 0; } } go-sqlite3-1.4.0/_example/simple/000077500000000000000000000000001320252770200165665ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/simple/simple.go000066400000000000000000000034331320252770200204110ustar00rootroot00000000000000package main import ( "database/sql" "fmt" _ "github.com/mattn/go-sqlite3" "log" "os" ) func main() { os.Remove("./foo.db") db, err := sql.Open("sqlite3", "./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 } tx, err := db.Begin() if err != nil { log.Fatal(err) } stmt, err := tx.Prepare("insert into foo(id, name) values(?, ?)") if err != nil { log.Fatal(err) } defer stmt.Close() for i := 0; i < 100; i++ { _, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i)) if err != nil { log.Fatal(err) } } tx.Commit() rows, err := db.Query("select id, name from foo") if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id int var name string err = rows.Scan(&id, &name) if err != nil { log.Fatal(err) } fmt.Println(id, name) } err = rows.Err() if err != nil { log.Fatal(err) } stmt, err = db.Prepare("select name from foo where id = ?") if err != nil { log.Fatal(err) } defer stmt.Close() var name string err = stmt.QueryRow("3").Scan(&name) if err != nil { log.Fatal(err) } fmt.Println(name) _, err = db.Exec("delete from foo") if err != nil { log.Fatal(err) } _, err = db.Exec("insert into foo(id, name) values(1, 'foo'), (2, 'bar'), (3, 'baz')") if err != nil { log.Fatal(err) } rows, err = db.Query("select id, name from foo") if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id int var name string err = rows.Scan(&id, &name) if err != nil { log.Fatal(err) } fmt.Println(id, name) } err = rows.Err() if err != nil { log.Fatal(err) } } go-sqlite3-1.4.0/_example/trace/000077500000000000000000000000001320252770200163735ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/trace/main.go000066400000000000000000000143721320252770200176550ustar00rootroot00000000000000package main import ( "database/sql" "fmt" "log" "os" sqlite3 "github.com/mattn/go-sqlite3" ) func traceCallback(info sqlite3.TraceInfo) int { // Not very readable but may be useful; uncomment next line in case of doubt: //fmt.Printf("Trace: %#v\n", info) var dbErrText string if info.DBError.Code != 0 || info.DBError.ExtendedCode != 0 { dbErrText = fmt.Sprintf("; DB error: %#v", info.DBError) } else { dbErrText = "." } // Show the Statement-or-Trigger text in curly braces ('{', '}') // since from the *paired* ASCII characters they are // the least used in SQL syntax, therefore better visual delimiters. // Maybe show 'ExpandedSQL' the same way as 'StmtOrTrigger'. // // A known use of curly braces (outside strings) is // for ODBC escape sequences. Not likely to appear here. // // Template languages, etc. don't matter, we should see their *result* // at *this* level. // Strange curly braces in SQL code that reached the database driver // suggest that there is a bug in the application. // The braces are likely to be either template syntax or // a programming language's string interpolation syntax. var expandedText string if info.ExpandedSQL != "" { if info.ExpandedSQL == info.StmtOrTrigger { expandedText = " = exp" } else { expandedText = fmt.Sprintf(" expanded {%q}", info.ExpandedSQL) } } else { expandedText = "" } // SQLite docs as of September 6, 2016: Tracing and Profiling Functions // https://www.sqlite.org/c3ref/profile.html // // The profile callback time is in units of nanoseconds, however // the current implementation is only capable of millisecond resolution // so the six least significant digits in the time are meaningless. // Future versions of SQLite might provide greater resolution on the profiler callback. var runTimeText string if info.RunTimeNanosec == 0 { if info.EventCode == sqlite3.TraceProfile { //runTimeText = "; no time" // seems confusing runTimeText = "; time 0" // no measurement unit } else { //runTimeText = "; no time" // seems useless and confusing } } else { const nanosPerMillisec = 1000000 if info.RunTimeNanosec%nanosPerMillisec == 0 { runTimeText = fmt.Sprintf("; time %d ms", info.RunTimeNanosec/nanosPerMillisec) } else { // unexpected: better than millisecond resolution runTimeText = fmt.Sprintf("; time %d ns!!!", info.RunTimeNanosec) } } var modeText string if info.AutoCommit { modeText = "-AC-" } else { modeText = "+Tx+" } fmt.Printf("Trace: ev %d %s conn 0x%x, stmt 0x%x {%q}%s%s%s\n", info.EventCode, modeText, info.ConnHandle, info.StmtHandle, info.StmtOrTrigger, expandedText, runTimeText, dbErrText) return 0 } func main() { eventMask := sqlite3.TraceStmt | sqlite3.TraceProfile | sqlite3.TraceRow | sqlite3.TraceClose sql.Register("sqlite3_tracing", &sqlite3.SQLiteDriver{ ConnectHook: func(conn *sqlite3.SQLiteConn) error { err := conn.SetTrace(&sqlite3.TraceConfig{ Callback: traceCallback, EventMask: uint(eventMask), WantExpandedSQL: true, }) return err }, }) os.Exit(dbMain()) } // Harder to do DB work in main(). // It's better with a separate function because // 'defer' and 'os.Exit' don't go well together. // // DO NOT use 'log.Fatal...' below: remember that it's equivalent to // Print() followed by a call to os.Exit(1) --- and // we want to avoid Exit() so 'defer' can do cleanup. // Use 'log.Panic...' instead. func dbMain() int { db, err := sql.Open("sqlite3_tracing", ":memory:") if err != nil { fmt.Printf("Failed to open database: %#+v\n", err) return 1 } defer db.Close() err = db.Ping() if err != nil { log.Panic(err) } dbSetup(db) dbDoInsert(db) dbDoInsertPrepared(db) dbDoSelect(db) dbDoSelectPrepared(db) return 0 } // 'DDL' stands for "Data Definition Language": // Note: "INTEGER PRIMARY KEY NOT NULL AUTOINCREMENT" causes the error // 'near "AUTOINCREMENT": syntax error'; without "NOT NULL" it works. const tableDDL = `CREATE TABLE t1 ( id INTEGER PRIMARY KEY AUTOINCREMENT, note VARCHAR NOT NULL )` // 'DML' stands for "Data Manipulation Language": const insertDML = "INSERT INTO t1 (note) VALUES (?)" const selectDML = "SELECT id, note FROM t1 WHERE note LIKE ?" const textPrefix = "bla-1234567890-" const noteTextPattern = "%Prep%" const nGenRows = 4 // Number of Rows to Generate (for *each* approach tested) func dbSetup(db *sql.DB) { var err error _, err = db.Exec("DROP TABLE IF EXISTS t1") if err != nil { log.Panic(err) } _, err = db.Exec(tableDDL) if err != nil { log.Panic(err) } } func dbDoInsert(db *sql.DB) { const Descr = "DB-Exec" for i := 0; i < nGenRows; i++ { result, err := db.Exec(insertDML, textPrefix+Descr) if err != nil { log.Panic(err) } resultDoCheck(result, Descr, i) } } func dbDoInsertPrepared(db *sql.DB) { const Descr = "DB-Prepare" stmt, err := db.Prepare(insertDML) if err != nil { log.Panic(err) } defer stmt.Close() for i := 0; i < nGenRows; i++ { result, err := stmt.Exec(textPrefix + Descr) if err != nil { log.Panic(err) } resultDoCheck(result, Descr, i) } } func resultDoCheck(result sql.Result, callerDescr string, callIndex int) { lastID, err := result.LastInsertId() if err != nil { log.Panic(err) } nAffected, err := result.RowsAffected() if err != nil { log.Panic(err) } log.Printf("Exec result for %s (%d): ID = %d, affected = %d\n", callerDescr, callIndex, lastID, nAffected) } func dbDoSelect(db *sql.DB) { const Descr = "DB-Query" rows, err := db.Query(selectDML, noteTextPattern) if err != nil { log.Panic(err) } defer rows.Close() rowsDoFetch(rows, Descr) } func dbDoSelectPrepared(db *sql.DB) { const Descr = "DB-Prepare" stmt, err := db.Prepare(selectDML) if err != nil { log.Panic(err) } defer stmt.Close() rows, err := stmt.Query(noteTextPattern) if err != nil { log.Panic(err) } defer rows.Close() rowsDoFetch(rows, Descr) } func rowsDoFetch(rows *sql.Rows, callerDescr string) { var nRows int var id int64 var note string for rows.Next() { err := rows.Scan(&id, ¬e) if err != nil { log.Panic(err) } log.Printf("Row for %s (%d): id=%d, note=%q\n", callerDescr, nRows, id, note) nRows++ } if err := rows.Err(); err != nil { log.Panic(err) } log.Printf("Total %d rows for %s.\n", nRows, callerDescr) } go-sqlite3-1.4.0/_example/vtable/000077500000000000000000000000001320252770200165525ustar00rootroot00000000000000go-sqlite3-1.4.0/_example/vtable/main.go000066400000000000000000000015501320252770200200260ustar00rootroot00000000000000package main import ( "database/sql" "fmt" "log" "github.com/mattn/go-sqlite3" ) func main() { sql.Register("sqlite3_with_extensions", &sqlite3.SQLiteDriver{ ConnectHook: func(conn *sqlite3.SQLiteConn) error { return conn.CreateModule("github", &githubModule{}) }, }) db, err := sql.Open("sqlite3_with_extensions", ":memory:") if err != nil { log.Fatal(err) } defer db.Close() _, err = db.Exec("create virtual table repo using github(id, full_name, description, html_url)") if err != nil { log.Fatal(err) } rows, err := db.Query("select id, full_name, description, html_url from repo") if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id, fullName, description, htmlURL string rows.Scan(&id, &fullName, &description, &htmlURL) fmt.Printf("%s: %s\n\t%s\n\t%s\n\n", id, fullName, description, htmlURL) } } go-sqlite3-1.4.0/_example/vtable/vtable.go000066400000000000000000000043351320252770200203630ustar00rootroot00000000000000package main import ( "encoding/json" "fmt" "io/ioutil" "net/http" "github.com/mattn/go-sqlite3" ) type githubRepo struct { ID int `json:"id"` FullName string `json:"full_name"` Description string `json:"description"` HTMLURL string `json:"html_url"` } type githubModule struct { } func (m *githubModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) { err := c.DeclareVTab(fmt.Sprintf(` CREATE TABLE %s ( id INT, full_name TEXT, description TEXT, html_url TEXT )`, args[0])) if err != nil { return nil, err } return &ghRepoTable{}, nil } func (m *githubModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) { return m.Create(c, args) } func (m *githubModule) DestroyModule() {} type ghRepoTable struct { repos []githubRepo } func (v *ghRepoTable) Open() (sqlite3.VTabCursor, error) { resp, err := http.Get("https://api.github.com/repositories") if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var repos []githubRepo if err := json.Unmarshal(body, &repos); err != nil { return nil, err } return &ghRepoCursor{0, repos}, nil } func (v *ghRepoTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) { return &sqlite3.IndexResult{}, nil } func (v *ghRepoTable) Disconnect() error { return nil } func (v *ghRepoTable) Destroy() error { return nil } type ghRepoCursor struct { index int repos []githubRepo } func (vc *ghRepoCursor) Column(c *sqlite3.SQLiteContext, col int) error { switch col { case 0: c.ResultInt(vc.repos[vc.index].ID) case 1: c.ResultText(vc.repos[vc.index].FullName) case 2: c.ResultText(vc.repos[vc.index].Description) case 3: c.ResultText(vc.repos[vc.index].HTMLURL) } return nil } func (vc *ghRepoCursor) Filter(idxNum int, idxStr string, vals []interface{}) error { vc.index = 0 return nil } func (vc *ghRepoCursor) Next() error { vc.index++ return nil } func (vc *ghRepoCursor) EOF() bool { return vc.index >= len(vc.repos) } func (vc *ghRepoCursor) Rowid() (int64, error) { return int64(vc.index), nil } func (vc *ghRepoCursor) Close() error { return nil } go-sqlite3-1.4.0/backup.go000066400000000000000000000042171320252770200153030ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package sqlite3 /* #ifndef USE_LIBSQLITE3 #include #else #include #endif #include */ import "C" import ( "runtime" "unsafe" ) // SQLiteBackup implement interface of Backup. type SQLiteBackup struct { b *C.sqlite3_backup } // Backup make backup from src to dest. func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteBackup, error) { destptr := C.CString(dest) defer C.free(unsafe.Pointer(destptr)) srcptr := C.CString(src) defer C.free(unsafe.Pointer(srcptr)) if b := C.sqlite3_backup_init(c.db, destptr, conn.db, srcptr); b != nil { bb := &SQLiteBackup{b: b} runtime.SetFinalizer(bb, (*SQLiteBackup).Finish) return bb, nil } return nil, c.lastError() } // Step to backs up for one step. Calls the underlying `sqlite3_backup_step` // function. This function returns a boolean indicating if the backup is done // and an error signalling any other error. Done is returned if the underlying // C function returns SQLITE_DONE (Code 101) func (b *SQLiteBackup) Step(p int) (bool, error) { ret := C.sqlite3_backup_step(b.b, C.int(p)) if ret == C.SQLITE_DONE { return true, nil } else if ret != 0 && ret != C.SQLITE_LOCKED && ret != C.SQLITE_BUSY { return false, Error{Code: ErrNo(ret)} } return false, nil } // Remaining return whether have the rest for backup. func (b *SQLiteBackup) Remaining() int { return int(C.sqlite3_backup_remaining(b.b)) } // PageCount return count of pages. func (b *SQLiteBackup) PageCount() int { return int(C.sqlite3_backup_pagecount(b.b)) } // Finish close backup. func (b *SQLiteBackup) Finish() error { return b.Close() } // Close close backup. func (b *SQLiteBackup) Close() error { ret := C.sqlite3_backup_finish(b.b) // sqlite3_backup_finish() never fails, it just returns the // error code from previous operations, so clean up before // checking and returning an error b.b = nil runtime.SetFinalizer(b, nil) if ret != 0 { return Error{Code: ErrNo(ret)} } return nil } go-sqlite3-1.4.0/backup_test.go000066400000000000000000000232531320252770200163430ustar00rootroot00000000000000// Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package sqlite3 import ( "database/sql" "fmt" "os" "testing" "time" ) // The number of rows of test data to create in the source database. // Can be used to control how many pages are available to be backed up. const testRowCount = 100 // The maximum number of seconds after which the page-by-page backup is considered to have taken too long. const usePagePerStepsTimeoutSeconds = 30 // Test the backup functionality. func testBackup(t *testing.T, testRowCount int, usePerPageSteps bool) { // This function will be called multiple times. // It uses sql.Register(), which requires the name parameter value to be unique. // There does not currently appear to be a way to unregister a registered driver, however. // So generate a database driver name that will likely be unique. var driverName = fmt.Sprintf("sqlite3_testBackup_%v_%v_%v", testRowCount, usePerPageSteps, time.Now().UnixNano()) // The driver's connection will be needed in order to perform the backup. driverConns := []*SQLiteConn{} sql.Register(driverName, &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { driverConns = append(driverConns, conn) return nil }, }) // Connect to the source database. srcTempFilename := TempFilename(t) defer os.Remove(srcTempFilename) srcDb, err := sql.Open(driverName, srcTempFilename) if err != nil { t.Fatal("Failed to open the source database:", err) } defer srcDb.Close() err = srcDb.Ping() if err != nil { t.Fatal("Failed to connect to the source database:", err) } // Connect to the destination database. destTempFilename := TempFilename(t) defer os.Remove(destTempFilename) destDb, err := sql.Open(driverName, destTempFilename) if err != nil { t.Fatal("Failed to open the destination database:", err) } defer destDb.Close() err = destDb.Ping() if err != nil { t.Fatal("Failed to connect to the destination database:", err) } // Check the driver connections. if len(driverConns) != 2 { t.Fatalf("Expected 2 driver connections, but found %v.", len(driverConns)) } srcDbDriverConn := driverConns[0] if srcDbDriverConn == nil { t.Fatal("The source database driver connection is nil.") } destDbDriverConn := driverConns[1] if destDbDriverConn == nil { t.Fatal("The destination database driver connection is nil.") } // Generate some test data for the given ID. var generateTestData = func(id int) string { return fmt.Sprintf("test-%v", id) } // Populate the source database with a test table containing some test data. tx, err := srcDb.Begin() if err != nil { t.Fatal("Failed to begin a transaction when populating the source database:", err) } _, err = srcDb.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)") if err != nil { tx.Rollback() t.Fatal("Failed to create the source database \"test\" table:", err) } for id := 0; id < testRowCount; id++ { _, err = srcDb.Exec("INSERT INTO test (id, value) VALUES (?, ?)", id, generateTestData(id)) if err != nil { tx.Rollback() t.Fatal("Failed to insert a row into the source database \"test\" table:", err) } } err = tx.Commit() if err != nil { t.Fatal("Failed to populate the source database:", err) } // Confirm that the destination database is initially empty. var destTableCount int err = destDb.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").Scan(&destTableCount) if err != nil { t.Fatal("Failed to check the destination table count:", err) } if destTableCount != 0 { t.Fatalf("The destination database is not empty; %v table(s) found.", destTableCount) } // Prepare to perform the backup. backup, err := destDbDriverConn.Backup("main", srcDbDriverConn, "main") if err != nil { t.Fatal("Failed to initialize the backup:", err) } // Allow the initial page count and remaining values to be retrieved. // According to , the page count and remaining values are "... only updated by sqlite3_backup_step()." isDone, err := backup.Step(0) if err != nil { t.Fatal("Unable to perform an initial 0-page backup step:", err) } if isDone { t.Fatal("Backup is unexpectedly done.") } // Check that the page count and remaining values are reasonable. initialPageCount := backup.PageCount() if initialPageCount <= 0 { t.Fatalf("Unexpected initial page count value: %v", initialPageCount) } initialRemaining := backup.Remaining() if initialRemaining <= 0 { t.Fatalf("Unexpected initial remaining value: %v", initialRemaining) } if initialRemaining != initialPageCount { t.Fatalf("Initial remaining value differs from the initial page count value; remaining: %v; page count: %v", initialRemaining, initialPageCount) } // Perform the backup. if usePerPageSteps { var startTime = time.Now().Unix() // Test backing-up using a page-by-page approach. var latestRemaining = initialRemaining for { // Perform the backup step. isDone, err = backup.Step(1) if err != nil { t.Fatal("Failed to perform a backup step:", err) } // The page count should remain unchanged from its initial value. currentPageCount := backup.PageCount() if currentPageCount != initialPageCount { t.Fatalf("Current page count differs from the initial page count; initial page count: %v; current page count: %v", initialPageCount, currentPageCount) } // There should now be one less page remaining. currentRemaining := backup.Remaining() expectedRemaining := latestRemaining - 1 if currentRemaining != expectedRemaining { t.Fatalf("Unexpected remaining value; expected remaining value: %v; actual remaining value: %v", expectedRemaining, currentRemaining) } latestRemaining = currentRemaining if isDone { break } // Limit the runtime of the backup attempt. if (time.Now().Unix() - startTime) > usePagePerStepsTimeoutSeconds { t.Fatal("Backup is taking longer than expected.") } } } else { // Test the copying of all remaining pages. isDone, err = backup.Step(-1) if err != nil { t.Fatal("Failed to perform a backup step:", err) } if !isDone { t.Fatal("Backup is unexpectedly not done.") } } // Check that the page count and remaining values are reasonable. finalPageCount := backup.PageCount() if finalPageCount != initialPageCount { t.Fatalf("Final page count differs from the initial page count; initial page count: %v; final page count: %v", initialPageCount, finalPageCount) } finalRemaining := backup.Remaining() if finalRemaining != 0 { t.Fatalf("Unexpected remaining value: %v", finalRemaining) } // Finish the backup. err = backup.Finish() if err != nil { t.Fatal("Failed to finish backup:", err) } // Confirm that the "test" table now exists in the destination database. var doesTestTableExist bool err = destDb.QueryRow("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'test' LIMIT 1) AS test_table_exists").Scan(&doesTestTableExist) if err != nil { t.Fatal("Failed to check if the \"test\" table exists in the destination database:", err) } if !doesTestTableExist { t.Fatal("The \"test\" table could not be found in the destination database.") } // Confirm that the number of rows in the destination database's "test" table matches that of the source table. var actualTestTableRowCount int err = destDb.QueryRow("SELECT COUNT(*) FROM test").Scan(&actualTestTableRowCount) if err != nil { t.Fatal("Failed to determine the rowcount of the \"test\" table in the destination database:", err) } if testRowCount != actualTestTableRowCount { t.Fatalf("Unexpected destination \"test\" table row count; expected: %v; found: %v", testRowCount, actualTestTableRowCount) } // Check each of the rows in the destination database. for id := 0; id < testRowCount; id++ { var checkedValue string err = destDb.QueryRow("SELECT value FROM test WHERE id = ?", id).Scan(&checkedValue) if err != nil { t.Fatal("Failed to query the \"test\" table in the destination database:", err) } var expectedValue = generateTestData(id) if checkedValue != expectedValue { t.Fatalf("Unexpected value in the \"test\" table in the destination database; expected value: %v; actual value: %v", expectedValue, checkedValue) } } } func TestBackupStepByStep(t *testing.T) { testBackup(t, testRowCount, true) } func TestBackupAllRemainingPages(t *testing.T) { testBackup(t, testRowCount, false) } // Test the error reporting when preparing to perform a backup. func TestBackupError(t *testing.T) { const driverName = "sqlite3_TestBackupError" // The driver's connection will be needed in order to perform the backup. var dbDriverConn *SQLiteConn sql.Register(driverName, &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { dbDriverConn = conn return nil }, }) // Connect to the database. dbTempFilename := TempFilename(t) defer os.Remove(dbTempFilename) db, err := sql.Open(driverName, dbTempFilename) if err != nil { t.Fatal("Failed to open the database:", err) } defer db.Close() db.Ping() // Need the driver connection in order to perform the backup. if dbDriverConn == nil { t.Fatal("Failed to get the driver connection.") } // Prepare to perform the backup. // Intentionally using the same connection for both the source and destination databases, to trigger an error result. backup, err := dbDriverConn.Backup("main", dbDriverConn, "main") if err == nil { t.Fatal("Failed to get the expected error result.") } const expectedError = "source and destination must be distinct" if err.Error() != expectedError { t.Fatalf("Unexpected error message; expected value: \"%v\"; actual value: \"%v\"", expectedError, err.Error()) } if backup != nil { t.Fatal("Failed to get the expected nil backup result.") } } go-sqlite3-1.4.0/callback.go000066400000000000000000000242371320252770200155760ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // 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 #else #include #endif #include 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 C.int, argv **C.sqlite3_value) { args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(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) } //export compareTrampoline func compareTrampoline(handlePtr uintptr, la C.int, a *C.char, lb C.int, b *C.char) C.int { cmp := lookupHandle(handlePtr).(func(string, string) int) return C.int(cmp(C.GoStringN(a, la), C.GoStringN(b, lb))) } //export commitHookTrampoline func commitHookTrampoline(handle uintptr) int { callback := lookupHandle(handle).(func() int) return callback() } //export rollbackHookTrampoline func rollbackHookTrampoline(handle uintptr) { callback := lookupHandle(handle).(func()) callback() } //export updateHookTrampoline func updateHookTrampoline(handle uintptr, op int, db *C.char, table *C.char, rowid int64) { callback := lookupHandle(handle).(func(int, string, string, int64)) callback(op, C.GoString(db), C.GoString(table), rowid) } // 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 } } go-sqlite3-1.4.0/callback_test.go000066400000000000000000000055101320252770200166260ustar00rootroot00000000000000package sqlite3 import ( "errors" "math" "reflect" "testing" ) func TestCallbackArgCast(t *testing.T) { intConv := callbackSyntheticForTests(reflect.ValueOf(int64(math.MaxInt64)), nil) floatConv := callbackSyntheticForTests(reflect.ValueOf(float64(math.MaxFloat64)), nil) errConv := callbackSyntheticForTests(reflect.Value{}, errors.New("test")) tests := []struct { f callbackArgConverter o reflect.Value }{ {intConv, reflect.ValueOf(int8(-1))}, {intConv, reflect.ValueOf(int16(-1))}, {intConv, reflect.ValueOf(int32(-1))}, {intConv, reflect.ValueOf(uint8(math.MaxUint8))}, {intConv, reflect.ValueOf(uint16(math.MaxUint16))}, {intConv, reflect.ValueOf(uint32(math.MaxUint32))}, // Special case, int64->uint64 is only 1<<63 - 1, not 1<<64 - 1 {intConv, reflect.ValueOf(uint64(math.MaxInt64))}, {floatConv, reflect.ValueOf(float32(math.Inf(1)))}, } for _, test := range tests { conv := callbackArgCast{test.f, test.o.Type()} val, err := conv.Run(nil) if err != nil { t.Errorf("Couldn't convert to %s: %s", test.o.Type(), err) } else if !reflect.DeepEqual(val.Interface(), test.o.Interface()) { t.Errorf("Unexpected result from converting to %s: got %v, want %v", test.o.Type(), val.Interface(), test.o.Interface()) } } conv := callbackArgCast{errConv, reflect.TypeOf(int8(0))} _, err := conv.Run(nil) if err == nil { t.Errorf("Expected error during callbackArgCast, but got none") } } func TestCallbackConverters(t *testing.T) { tests := []struct { v interface{} err bool }{ // Unfortunately, we can't tell which converter was returned, // but we can at least check which types can be converted. {[]byte{0}, false}, {"text", false}, {true, false}, {int8(0), false}, {int16(0), false}, {int32(0), false}, {int64(0), false}, {uint8(0), false}, {uint16(0), false}, {uint32(0), false}, {uint64(0), false}, {int(0), false}, {uint(0), false}, {float64(0), false}, {float32(0), false}, {func() {}, true}, {complex64(complex(0, 0)), true}, {complex128(complex(0, 0)), true}, {struct{}{}, true}, {map[string]string{}, true}, {[]string{}, true}, {(*int8)(nil), true}, {make(chan int), true}, } for _, test := range tests { _, err := callbackArg(reflect.TypeOf(test.v)) if test.err && err == nil { t.Errorf("Expected an error when converting %s, got no error", reflect.TypeOf(test.v)) } else if !test.err && err != nil { t.Errorf("Expected converter when converting %s, got error: %s", reflect.TypeOf(test.v), err) } } for _, test := range tests { _, err := callbackRet(reflect.TypeOf(test.v)) if test.err && err == nil { t.Errorf("Expected an error when converting %s, got no error", reflect.TypeOf(test.v)) } else if !test.err && err != nil { t.Errorf("Expected converter when converting %s, got error: %s", reflect.TypeOf(test.v), err) } } } go-sqlite3-1.4.0/doc.go000066400000000000000000000061531320252770200146040ustar00rootroot00000000000000/* Package sqlite3 provides interface to SQLite3 databases. This works as a driver for database/sql. Installation go get github.com/mattn/go-sqlite3 Supported Types Currently, go-sqlite3 supports the following data types. +------------------------------+ |go | sqlite3 | |----------|-------------------| |nil | null | |int | integer | |int64 | integer | |float64 | float | |bool | integer | |[]byte | blob | |string | text | |time.Time | timestamp/datetime| +------------------------------+ SQLite3 Extension You can write your own extension module for sqlite3. For example, below is an extension for a Regexp matcher operation. #include #include #include #include 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); rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); if (rc <= 0) { sqlite3_result_error(context, errstr, 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); } It needs to be built as a so/dll shared library. And you need to register the extension module like below. sql.Register("sqlite3_with_extensions", &sqlite3.SQLiteDriver{ Extensions: []string{ "sqlite3_mod_regexp", }, }) Then, you can use this extension. rows, err := db.Query("select text from mytable where name regexp '^golang'") Connection Hook You can hook and inject your code when the connection is established. database/sql doesn't provide a way to get native go-sqlite3 interfaces. So if you want, you need to set ConnectHook and get the SQLiteConn. sql.Register("sqlite3_with_hook_example", &sqlite3.SQLiteDriver{ ConnectHook: func(conn *sqlite3.SQLiteConn) error { sqlite3conn = append(sqlite3conn, conn) return nil }, }) Go SQlite3 Extensions If you want to register Go functions as SQLite extension functions, call RegisterFunction from ConnectHook. regex = func(re, s string) (bool, error) { return regexp.MatchString(re, s) } sql.Register("sqlite3_with_go_func", &sqlite3.SQLiteDriver{ ConnectHook: func(conn *sqlite3.SQLiteConn) error { return conn.RegisterFunc("regexp", regex, true) }, }) See the documentation of RegisterFunc for more details. */ package sqlite3 go-sqlite3-1.4.0/error.go000066400000000000000000000132141320252770200151640ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package sqlite3 import "C" // ErrNo inherit errno. type ErrNo int // ErrNoMask is mask code. const ErrNoMask C.int = 0xff // ErrNoExtended is extended errno. type ErrNoExtended int // Error implement sqlite error code. type Error struct { Code ErrNo /* The error code returned by SQLite */ ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */ err string /* The error string returned by sqlite3_errmsg(), this usually contains more specific details. */ } // result codes from http://www.sqlite.org/c3ref/c_abort.html var ( ErrError = ErrNo(1) /* SQL error or missing database */ ErrInternal = ErrNo(2) /* Internal logic error in SQLite */ ErrPerm = ErrNo(3) /* Access permission denied */ ErrAbort = ErrNo(4) /* Callback routine requested an abort */ ErrBusy = ErrNo(5) /* The database file is locked */ ErrLocked = ErrNo(6) /* A table in the database is locked */ ErrNomem = ErrNo(7) /* A malloc() failed */ ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */ ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */ ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */ ErrCorrupt = ErrNo(11) /* The database disk image is malformed */ ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */ ErrFull = ErrNo(13) /* Insertion failed because database is full */ ErrCantOpen = ErrNo(14) /* Unable to open the database file */ ErrProtocol = ErrNo(15) /* Database lock protocol error */ ErrEmpty = ErrNo(16) /* Database is empty */ ErrSchema = ErrNo(17) /* The database schema changed */ ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */ ErrConstraint = ErrNo(19) /* Abort due to constraint violation */ ErrMismatch = ErrNo(20) /* Data type mismatch */ ErrMisuse = ErrNo(21) /* Library used incorrectly */ ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */ ErrAuth = ErrNo(23) /* Authorization denied */ ErrFormat = ErrNo(24) /* Auxiliary database format error */ ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */ ErrNotADB = ErrNo(26) /* File opened that is not a database file */ ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */ ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */ ) // Error return error message from errno. func (err ErrNo) Error() string { return Error{Code: err}.Error() } // Extend return extended errno. func (err ErrNo) Extend(by int) ErrNoExtended { return ErrNoExtended(int(err) | (by << 8)) } // Error return error message that is extended code. func (err ErrNoExtended) Error() string { return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error() } func (err Error) Error() string { if err.err != "" { return err.err } return errorString(err) } // result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html var ( ErrIoErrRead = ErrIoErr.Extend(1) ErrIoErrShortRead = ErrIoErr.Extend(2) ErrIoErrWrite = ErrIoErr.Extend(3) ErrIoErrFsync = ErrIoErr.Extend(4) ErrIoErrDirFsync = ErrIoErr.Extend(5) ErrIoErrTruncate = ErrIoErr.Extend(6) ErrIoErrFstat = ErrIoErr.Extend(7) ErrIoErrUnlock = ErrIoErr.Extend(8) ErrIoErrRDlock = ErrIoErr.Extend(9) ErrIoErrDelete = ErrIoErr.Extend(10) ErrIoErrBlocked = ErrIoErr.Extend(11) ErrIoErrNoMem = ErrIoErr.Extend(12) ErrIoErrAccess = ErrIoErr.Extend(13) ErrIoErrCheckReservedLock = ErrIoErr.Extend(14) ErrIoErrLock = ErrIoErr.Extend(15) ErrIoErrClose = ErrIoErr.Extend(16) ErrIoErrDirClose = ErrIoErr.Extend(17) ErrIoErrSHMOpen = ErrIoErr.Extend(18) ErrIoErrSHMSize = ErrIoErr.Extend(19) ErrIoErrSHMLock = ErrIoErr.Extend(20) ErrIoErrSHMMap = ErrIoErr.Extend(21) ErrIoErrSeek = ErrIoErr.Extend(22) ErrIoErrDeleteNoent = ErrIoErr.Extend(23) ErrIoErrMMap = ErrIoErr.Extend(24) ErrIoErrGetTempPath = ErrIoErr.Extend(25) ErrIoErrConvPath = ErrIoErr.Extend(26) ErrLockedSharedCache = ErrLocked.Extend(1) ErrBusyRecovery = ErrBusy.Extend(1) ErrBusySnapshot = ErrBusy.Extend(2) ErrCantOpenNoTempDir = ErrCantOpen.Extend(1) ErrCantOpenIsDir = ErrCantOpen.Extend(2) ErrCantOpenFullPath = ErrCantOpen.Extend(3) ErrCantOpenConvPath = ErrCantOpen.Extend(4) ErrCorruptVTab = ErrCorrupt.Extend(1) ErrReadonlyRecovery = ErrReadonly.Extend(1) ErrReadonlyCantLock = ErrReadonly.Extend(2) ErrReadonlyRollback = ErrReadonly.Extend(3) ErrReadonlyDbMoved = ErrReadonly.Extend(4) ErrAbortRollback = ErrAbort.Extend(2) ErrConstraintCheck = ErrConstraint.Extend(1) ErrConstraintCommitHook = ErrConstraint.Extend(2) ErrConstraintForeignKey = ErrConstraint.Extend(3) ErrConstraintFunction = ErrConstraint.Extend(4) ErrConstraintNotNull = ErrConstraint.Extend(5) ErrConstraintPrimaryKey = ErrConstraint.Extend(6) ErrConstraintTrigger = ErrConstraint.Extend(7) ErrConstraintUnique = ErrConstraint.Extend(8) ErrConstraintVTab = ErrConstraint.Extend(9) ErrConstraintRowID = ErrConstraint.Extend(10) ErrNoticeRecoverWAL = ErrNotice.Extend(1) ErrNoticeRecoverRollback = ErrNotice.Extend(2) ErrWarningAutoIndex = ErrWarning.Extend(1) ) go-sqlite3-1.4.0/error_test.go000066400000000000000000000125241320252770200162260ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package sqlite3 import ( "database/sql" "io/ioutil" "os" "path" "testing" ) func TestSimpleError(t *testing.T) { e := ErrError.Error() if e != "SQL logic error or missing database" { t.Error("wrong error code:" + e) } } func TestCorruptDbErrors(t *testing.T) { dirName, err := ioutil.TempDir("", "sqlite3") if err != nil { t.Fatal(err) } defer os.RemoveAll(dirName) dbFileName := path.Join(dirName, "test.db") f, err := os.Create(dbFileName) if err != nil { t.Error(err) } f.Write([]byte{1, 2, 3, 4, 5}) f.Close() db, err := sql.Open("sqlite3", dbFileName) if err == nil { _, err = db.Exec("drop table foo") } sqliteErr := err.(Error) if sqliteErr.Code != ErrNotADB { t.Error("wrong error code for corrupted DB") } if err.Error() == "" { t.Error("wrong error string for corrupted DB") } db.Close() } func TestSqlLogicErrors(t *testing.T) { dirName, err := ioutil.TempDir("", "sqlite3") if err != nil { t.Fatal(err) } defer os.RemoveAll(dirName) dbFileName := path.Join(dirName, "test.db") db, err := sql.Open("sqlite3", dbFileName) if err != nil { t.Error(err) } defer db.Close() _, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)") if err != nil { t.Error(err) } const expectedErr = "table Foo already exists" _, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)") if err.Error() != expectedErr { t.Errorf("Unexpected error: %s, expected %s", err.Error(), expectedErr) } } func TestExtendedErrorCodes_ForeignKey(t *testing.T) { dirName, err := ioutil.TempDir("", "sqlite3-err") if err != nil { t.Fatal(err) } defer os.RemoveAll(dirName) dbFileName := path.Join(dirName, "test.db") db, err := sql.Open("sqlite3", dbFileName) if err != nil { t.Error(err) } defer db.Close() _, err = db.Exec("PRAGMA foreign_keys=ON;") if err != nil { t.Errorf("PRAGMA foreign_keys=ON: %v", err) } _, err = db.Exec(`CREATE TABLE Foo ( id INTEGER PRIMARY KEY AUTOINCREMENT, value INTEGER NOT NULL, ref INTEGER NULL REFERENCES Foo (id), UNIQUE(value) );`) if err != nil { t.Error(err) } _, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (100, 100);") if err == nil { t.Error("No error!") } else { sqliteErr := err.(Error) if sqliteErr.Code != ErrConstraint { t.Errorf("Wrong basic error code: %d != %d", sqliteErr.Code, ErrConstraint) } if sqliteErr.ExtendedCode != ErrConstraintForeignKey { t.Errorf("Wrong extended error code: %d != %d", sqliteErr.ExtendedCode, ErrConstraintForeignKey) } } } func TestExtendedErrorCodes_NotNull(t *testing.T) { dirName, err := ioutil.TempDir("", "sqlite3-err") if err != nil { t.Fatal(err) } defer os.RemoveAll(dirName) dbFileName := path.Join(dirName, "test.db") db, err := sql.Open("sqlite3", dbFileName) if err != nil { t.Error(err) } defer db.Close() _, err = db.Exec("PRAGMA foreign_keys=ON;") if err != nil { t.Errorf("PRAGMA foreign_keys=ON: %v", err) } _, err = db.Exec(`CREATE TABLE Foo ( id INTEGER PRIMARY KEY AUTOINCREMENT, value INTEGER NOT NULL, ref INTEGER NULL REFERENCES Foo (id), UNIQUE(value) );`) if err != nil { t.Error(err) } res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);") if err != nil { t.Fatalf("Creating first row: %v", err) } id, err := res.LastInsertId() if err != nil { t.Fatalf("Retrieving last insert id: %v", err) } _, err = db.Exec("INSERT INTO Foo (ref) VALUES (?);", id) if err == nil { t.Error("No error!") } else { sqliteErr := err.(Error) if sqliteErr.Code != ErrConstraint { t.Errorf("Wrong basic error code: %d != %d", sqliteErr.Code, ErrConstraint) } if sqliteErr.ExtendedCode != ErrConstraintNotNull { t.Errorf("Wrong extended error code: %d != %d", sqliteErr.ExtendedCode, ErrConstraintNotNull) } } } func TestExtendedErrorCodes_Unique(t *testing.T) { dirName, err := ioutil.TempDir("", "sqlite3-err") if err != nil { t.Fatal(err) } defer os.RemoveAll(dirName) dbFileName := path.Join(dirName, "test.db") db, err := sql.Open("sqlite3", dbFileName) if err != nil { t.Error(err) } defer db.Close() _, err = db.Exec("PRAGMA foreign_keys=ON;") if err != nil { t.Errorf("PRAGMA foreign_keys=ON: %v", err) } _, err = db.Exec(`CREATE TABLE Foo ( id INTEGER PRIMARY KEY AUTOINCREMENT, value INTEGER NOT NULL, ref INTEGER NULL REFERENCES Foo (id), UNIQUE(value) );`) if err != nil { t.Error(err) } res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);") if err != nil { t.Fatalf("Creating first row: %v", err) } id, err := res.LastInsertId() if err != nil { t.Fatalf("Retrieving last insert id: %v", err) } _, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (?, 100);", id) if err == nil { t.Error("No error!") } else { sqliteErr := err.(Error) if sqliteErr.Code != ErrConstraint { t.Errorf("Wrong basic error code: %d != %d", sqliteErr.Code, ErrConstraint) } if sqliteErr.ExtendedCode != ErrConstraintUnique { t.Errorf("Wrong extended error code: %d != %d", sqliteErr.ExtendedCode, ErrConstraintUnique) } extended := sqliteErr.Code.Extend(3).Error() expected := "constraint failed" if extended != expected { t.Errorf("Wrong basic error code: %q != %q", extended, expected) } } } go-sqlite3-1.4.0/sqlite3.go000066400000000000000000001061141320252770200154210ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package sqlite3 /* #cgo CFLAGS: -std=gnu99 #cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE=1 #cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61 #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 #cgo CFLAGS: -DSQLITE_DISABLE_INTRINSIC #cgo CFLAGS: -Wno-deprecated-declarations #ifndef USE_LIBSQLITE3 #include #else #include #endif #include #include #ifdef __CYGWIN__ # include #endif #ifndef SQLITE_OPEN_READWRITE # define SQLITE_OPEN_READWRITE 0 #endif #ifndef SQLITE_OPEN_FULLMUTEX # define SQLITE_OPEN_FULLMUTEX 0 #endif #ifndef SQLITE_DETERMINISTIC # define SQLITE_DETERMINISTIC 0 #endif static int _sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) { #ifdef SQLITE_OPEN_URI return sqlite3_open_v2(filename, ppDb, flags | SQLITE_OPEN_URI, zVfs); #else return sqlite3_open_v2(filename, ppDb, flags, zVfs); #endif } static int _sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) { return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT); } static int _sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) { return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT); } #include #include static int _sqlite3_exec(sqlite3* db, const char* pcmd, long long* rowid, long long* changes) { int rv = sqlite3_exec(db, pcmd, 0, 0, 0); *rowid = (long long) sqlite3_last_insert_rowid(db); *changes = (long long) sqlite3_changes(db); return rv; } static int _sqlite3_step(sqlite3_stmt* stmt, long long* rowid, long long* changes) { int rv = sqlite3_step(stmt); sqlite3* db = sqlite3_db_handle(stmt); *rowid = (long long) sqlite3_last_insert_rowid(db); *changes = (long long) sqlite3_changes(db); return rv; } void _sqlite3_result_text(sqlite3_context* ctx, const char* s) { sqlite3_result_text(ctx, s, -1, &free); } void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l) { sqlite3_result_blob(ctx, b, l, SQLITE_TRANSIENT); } int _sqlite3_create_function( sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, uintptr_t pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ) { return sqlite3_create_function(db, zFunctionName, nArg, eTextRep, (void*) pApp, xFunc, xStep, xFinal); } void callbackTrampoline(sqlite3_context*, int, sqlite3_value**); void stepTrampoline(sqlite3_context*, int, sqlite3_value**); void doneTrampoline(sqlite3_context*); int compareTrampoline(void*, int, char*, int, char*); int commitHookTrampoline(void*); void rollbackHookTrampoline(void*); void updateHookTrampoline(void*, int, char*, char*, sqlite3_int64); #ifdef SQLITE_LIMIT_WORKER_THREADS # define _SQLITE_HAS_LIMIT # define SQLITE_LIMIT_LENGTH 0 # define SQLITE_LIMIT_SQL_LENGTH 1 # define SQLITE_LIMIT_COLUMN 2 # define SQLITE_LIMIT_EXPR_DEPTH 3 # define SQLITE_LIMIT_COMPOUND_SELECT 4 # define SQLITE_LIMIT_VDBE_OP 5 # define SQLITE_LIMIT_FUNCTION_ARG 6 # define SQLITE_LIMIT_ATTACHED 7 # define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 # define SQLITE_LIMIT_VARIABLE_NUMBER 9 # define SQLITE_LIMIT_TRIGGER_DEPTH 10 # define SQLITE_LIMIT_WORKER_THREADS 11 # else # define SQLITE_LIMIT_WORKER_THREADS 11 #endif static int _sqlite3_limit(sqlite3* db, int limitId, int newLimit) { #ifndef _SQLITE_HAS_LIMIT return -1; #else return sqlite3_limit(db, limitId, newLimit); #endif } */ import "C" import ( "database/sql" "database/sql/driver" "errors" "fmt" "io" "net/url" "reflect" "runtime" "strconv" "strings" "sync" "time" "unsafe" "golang.org/x/net/context" ) // SQLiteTimestampFormats is timestamp formats understood by both this module // and SQLite. The first format in the slice will be used when saving time // values into the database. When parsing a string from a timestamp or datetime // column, the formats are tried in order. var SQLiteTimestampFormats = []string{ // By default, store timestamps with whatever timezone they come with. // When parsed, they will be returned with the same timezone. "2006-01-02 15:04:05.999999999-07:00", "2006-01-02T15:04:05.999999999-07:00", "2006-01-02 15:04:05.999999999", "2006-01-02T15:04:05.999999999", "2006-01-02 15:04:05", "2006-01-02T15:04:05", "2006-01-02 15:04", "2006-01-02T15:04", "2006-01-02", } func init() { sql.Register("sqlite3", &SQLiteDriver{}) } // Version returns SQLite library version information. func Version() (libVersion string, libVersionNumber int, sourceID string) { libVersion = C.GoString(C.sqlite3_libversion()) libVersionNumber = int(C.sqlite3_libversion_number()) sourceID = C.GoString(C.sqlite3_sourceid()) return libVersion, libVersionNumber, sourceID } const ( SQLITE_DELETE = C.SQLITE_DELETE SQLITE_INSERT = C.SQLITE_INSERT SQLITE_UPDATE = C.SQLITE_UPDATE ) // SQLiteDriver implement sql.Driver. type SQLiteDriver struct { Extensions []string ConnectHook func(*SQLiteConn) error } // SQLiteConn implement sql.Conn. type SQLiteConn struct { mu sync.Mutex db *C.sqlite3 loc *time.Location txlock string funcs []*functionInfo aggregators []*aggInfo } // SQLiteTx implemen sql.Tx. type SQLiteTx struct { c *SQLiteConn } // SQLiteStmt implement sql.Stmt. type SQLiteStmt struct { mu sync.Mutex c *SQLiteConn s *C.sqlite3_stmt t string closed bool cls bool } // SQLiteResult implement sql.Result. type SQLiteResult struct { id int64 changes int64 } // SQLiteRows implement sql.Rows. type SQLiteRows struct { s *SQLiteStmt nc int cols []string decltype []string cls bool closed bool done chan struct{} } type functionInfo struct { f reflect.Value argConverters []callbackArgConverter variadicConverter callbackArgConverter retConverter callbackRetConverter } func (fi *functionInfo) Call(ctx *C.sqlite3_context, argv []*C.sqlite3_value) { args, err := callbackConvertArgs(argv, fi.argConverters, fi.variadicConverter) if err != nil { callbackError(ctx, err) return } ret := fi.f.Call(args) if len(ret) == 2 && ret[1].Interface() != nil { callbackError(ctx, ret[1].Interface().(error)) return } err = fi.retConverter(ctx, ret[0]) if err != nil { callbackError(ctx, err) return } } type aggInfo struct { constructor reflect.Value // Active aggregator objects for aggregations in flight. The // aggregators are indexed by a counter stored in the aggregation // user data space provided by sqlite. active map[int64]reflect.Value next int64 stepArgConverters []callbackArgConverter stepVariadicConverter callbackArgConverter doneRetConverter callbackRetConverter } func (ai *aggInfo) agg(ctx *C.sqlite3_context) (int64, reflect.Value, error) { aggIdx := (*int64)(C.sqlite3_aggregate_context(ctx, C.int(8))) if *aggIdx == 0 { *aggIdx = ai.next ret := ai.constructor.Call(nil) if len(ret) == 2 && ret[1].Interface() != nil { return 0, reflect.Value{}, ret[1].Interface().(error) } if ret[0].IsNil() { return 0, reflect.Value{}, errors.New("aggregator constructor returned nil state") } ai.next++ ai.active[*aggIdx] = ret[0] } return *aggIdx, ai.active[*aggIdx], nil } func (ai *aggInfo) Step(ctx *C.sqlite3_context, argv []*C.sqlite3_value) { _, agg, err := ai.agg(ctx) if err != nil { callbackError(ctx, err) return } args, err := callbackConvertArgs(argv, ai.stepArgConverters, ai.stepVariadicConverter) if err != nil { callbackError(ctx, err) return } ret := agg.MethodByName("Step").Call(args) if len(ret) == 1 && ret[0].Interface() != nil { callbackError(ctx, ret[0].Interface().(error)) return } } func (ai *aggInfo) Done(ctx *C.sqlite3_context) { idx, agg, err := ai.agg(ctx) if err != nil { callbackError(ctx, err) return } defer func() { delete(ai.active, idx) }() ret := agg.MethodByName("Done").Call(nil) if len(ret) == 2 && ret[1].Interface() != nil { callbackError(ctx, ret[1].Interface().(error)) return } err = ai.doneRetConverter(ctx, ret[0]) if err != nil { callbackError(ctx, err) return } } // Commit transaction. func (tx *SQLiteTx) Commit() error { _, err := tx.c.exec(context.Background(), "COMMIT", nil) if err != nil && err.(Error).Code == C.SQLITE_BUSY { // sqlite3 will leave the transaction open in this scenario. // However, database/sql considers the transaction complete once we // return from Commit() - we must clean up to honour its semantics. tx.c.exec(context.Background(), "ROLLBACK", nil) } return err } // Rollback transaction. func (tx *SQLiteTx) Rollback() error { _, err := tx.c.exec(context.Background(), "ROLLBACK", nil) return err } // RegisterCollation makes a Go function available as a collation. // // cmp receives two UTF-8 strings, a and b. The result should be 0 if // a==b, -1 if a < b, and +1 if a > b. // // cmp must always return the same result given the same // inputs. Additionally, it must have the following properties for all // strings A, B and C: if A==B then B==A; if A==B and B==C then A==C; // if AA; if A= 1 { params, err := url.ParseQuery(dsn[pos+1:]) if err != nil { return nil, err } // _loc if val := params.Get("_loc"); val != "" { if val == "auto" { loc = time.Local } else { loc, err = time.LoadLocation(val) if err != nil { return nil, fmt.Errorf("Invalid _loc: %v: %v", val, err) } } } // _busy_timeout if val := params.Get("_busy_timeout"); val != "" { iv, err := strconv.ParseInt(val, 10, 64) if err != nil { return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err) } busyTimeout = int(iv) } // _txlock if val := params.Get("_txlock"); val != "" { switch val { case "immediate": txlock = "BEGIN IMMEDIATE" case "exclusive": txlock = "BEGIN EXCLUSIVE" case "deferred": txlock = "BEGIN" default: return nil, fmt.Errorf("Invalid _txlock: %v", val) } } // _foreign_keys if val := params.Get("_foreign_keys"); val != "" { switch val { case "1": foreignKeys = 1 case "0": foreignKeys = 0 default: return nil, fmt.Errorf("Invalid _foreign_keys: %v", val) } } // _recursive_triggers if val := params.Get("_recursive_triggers"); val != "" { switch val { case "1": recursiveTriggers = 1 case "0": recursiveTriggers = 0 default: return nil, fmt.Errorf("Invalid _recursive_triggers: %v", val) } } if !strings.HasPrefix(dsn, "file:") { dsn = dsn[:pos] } } var db *C.sqlite3 name := C.CString(dsn) defer C.free(unsafe.Pointer(name)) rv := C._sqlite3_open_v2(name, &db, C.SQLITE_OPEN_FULLMUTEX| C.SQLITE_OPEN_READWRITE| C.SQLITE_OPEN_CREATE, nil) if rv != 0 { return nil, Error{Code: ErrNo(rv)} } if db == nil { return nil, errors.New("sqlite succeeded without returning a database") } rv = C.sqlite3_busy_timeout(db, C.int(busyTimeout)) if rv != C.SQLITE_OK { C.sqlite3_close_v2(db) return nil, Error{Code: ErrNo(rv)} } exec := func(s string) error { cs := C.CString(s) rv := C.sqlite3_exec(db, cs, nil, nil, nil) C.free(unsafe.Pointer(cs)) if rv != C.SQLITE_OK { return lastError(db) } return nil } if foreignKeys == 0 { if err := exec("PRAGMA foreign_keys = OFF;"); err != nil { C.sqlite3_close_v2(db) return nil, err } } else if foreignKeys == 1 { if err := exec("PRAGMA foreign_keys = ON;"); err != nil { C.sqlite3_close_v2(db) return nil, err } } if recursiveTriggers == 0 { if err := exec("PRAGMA recursive_triggers = OFF;"); err != nil { C.sqlite3_close_v2(db) return nil, err } } else if recursiveTriggers == 1 { if err := exec("PRAGMA recursive_triggers = ON;"); err != nil { C.sqlite3_close_v2(db) return nil, err } } conn := &SQLiteConn{db: db, loc: loc, txlock: txlock} if len(d.Extensions) > 0 { if err := conn.loadExtensions(d.Extensions); err != nil { conn.Close() return nil, err } } if d.ConnectHook != nil { if err := d.ConnectHook(conn); err != nil { conn.Close() return nil, err } } runtime.SetFinalizer(conn, (*SQLiteConn).Close) return conn, nil } // Close the connection. func (c *SQLiteConn) Close() error { rv := C.sqlite3_close_v2(c.db) if rv != C.SQLITE_OK { return c.lastError() } deleteHandles(c) c.mu.Lock() c.db = nil c.mu.Unlock() runtime.SetFinalizer(c, nil) return nil } func (c *SQLiteConn) dbConnOpen() bool { if c == nil { return false } c.mu.Lock() defer c.mu.Unlock() return c.db != nil } // Prepare the query string. Return a new statement. func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { return c.prepare(context.Background(), query) } func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, error) { pquery := C.CString(query) defer C.free(unsafe.Pointer(pquery)) var s *C.sqlite3_stmt var tail *C.char rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &tail) if rv != C.SQLITE_OK { return nil, c.lastError() } var t string if tail != nil && *tail != '\000' { t = strings.TrimSpace(C.GoString(tail)) } ss := &SQLiteStmt{c: c, s: s, t: t} runtime.SetFinalizer(ss, (*SQLiteStmt).Close) return ss, nil } // Run-Time Limit Categories. // See: http://www.sqlite.org/c3ref/c_limit_attached.html const ( SQLITE_LIMIT_LENGTH = C.SQLITE_LIMIT_LENGTH SQLITE_LIMIT_SQL_LENGTH = C.SQLITE_LIMIT_SQL_LENGTH SQLITE_LIMIT_COLUMN = C.SQLITE_LIMIT_COLUMN SQLITE_LIMIT_EXPR_DEPTH = C.SQLITE_LIMIT_EXPR_DEPTH SQLITE_LIMIT_COMPOUND_SELECT = C.SQLITE_LIMIT_COMPOUND_SELECT SQLITE_LIMIT_VDBE_OP = C.SQLITE_LIMIT_VDBE_OP SQLITE_LIMIT_FUNCTION_ARG = C.SQLITE_LIMIT_FUNCTION_ARG SQLITE_LIMIT_ATTACHED = C.SQLITE_LIMIT_ATTACHED SQLITE_LIMIT_LIKE_PATTERN_LENGTH = C.SQLITE_LIMIT_LIKE_PATTERN_LENGTH SQLITE_LIMIT_VARIABLE_NUMBER = C.SQLITE_LIMIT_VARIABLE_NUMBER SQLITE_LIMIT_TRIGGER_DEPTH = C.SQLITE_LIMIT_TRIGGER_DEPTH SQLITE_LIMIT_WORKER_THREADS = C.SQLITE_LIMIT_WORKER_THREADS ) // GetLimit returns the current value of a run-time limit. // See: sqlite3_limit, http://www.sqlite.org/c3ref/limit.html func (c *SQLiteConn) GetLimit(id int) int { return int(C._sqlite3_limit(c.db, C.int(id), -1)) } // SetLimit changes the value of a run-time limits. // Then this method returns the prior value of the limit. // See: sqlite3_limit, http://www.sqlite.org/c3ref/limit.html func (c *SQLiteConn) SetLimit(id int, newVal int) int { return int(C._sqlite3_limit(c.db, C.int(id), C.int(newVal))) } // Close the statement. func (s *SQLiteStmt) Close() error { s.mu.Lock() defer s.mu.Unlock() if s.closed { return nil } s.closed = true if !s.c.dbConnOpen() { return errors.New("sqlite statement with already closed database connection") } rv := C.sqlite3_finalize(s.s) s.s = nil if rv != C.SQLITE_OK { return s.c.lastError() } runtime.SetFinalizer(s, nil) return nil } // NumInput return a number of parameters. func (s *SQLiteStmt) NumInput() int { return int(C.sqlite3_bind_parameter_count(s.s)) } type bindArg struct { n int v driver.Value } var placeHolder = []byte{0} func (s *SQLiteStmt) bind(args []namedValue) error { rv := C.sqlite3_reset(s.s) if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { return s.c.lastError() } for i, v := range args { if v.Name != "" { cname := C.CString(":" + v.Name) args[i].Ordinal = int(C.sqlite3_bind_parameter_index(s.s, cname)) C.free(unsafe.Pointer(cname)) } } for _, arg := range args { n := C.int(arg.Ordinal) switch v := arg.Value.(type) { case nil: rv = C.sqlite3_bind_null(s.s, n) case string: if len(v) == 0 { rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.int(0)) } else { b := []byte(v) rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) } case int64: rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v)) case bool: if bool(v) { rv = C.sqlite3_bind_int(s.s, n, 1) } else { rv = C.sqlite3_bind_int(s.s, n, 0) } case float64: rv = C.sqlite3_bind_double(s.s, n, C.double(v)) case []byte: ln := len(v) if ln == 0 { v = placeHolder } rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(ln)) case time.Time: b := []byte(v.Format(SQLiteTimestampFormats[0])) rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) } if rv != C.SQLITE_OK { return s.c.lastError() } } return nil } // Query the statement with arguments. Return records. func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) { list := make([]namedValue, len(args)) for i, v := range args { list[i] = namedValue{ Ordinal: i + 1, Value: v, } } return s.query(context.Background(), list) } func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) { if err := s.bind(args); err != nil { return nil, err } rows := &SQLiteRows{ s: s, nc: int(C.sqlite3_column_count(s.s)), cols: nil, decltype: nil, cls: s.cls, closed: false, done: make(chan struct{}), } go func(db *C.sqlite3) { select { case <-ctx.Done(): select { case <-rows.done: default: C.sqlite3_interrupt(db) rows.Close() } case <-rows.done: } }(s.c.db) return rows, nil } // LastInsertId teturn last inserted ID. func (r *SQLiteResult) LastInsertId() (int64, error) { return r.id, nil } // RowsAffected return how many rows affected. func (r *SQLiteResult) RowsAffected() (int64, error) { return r.changes, nil } // Exec execute the statement with arguments. Return result object. func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { list := make([]namedValue, len(args)) for i, v := range args { list[i] = namedValue{ Ordinal: i + 1, Value: v, } } return s.exec(context.Background(), list) } func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) { if err := s.bind(args); err != nil { C.sqlite3_reset(s.s) C.sqlite3_clear_bindings(s.s) return nil, err } done := make(chan struct{}) defer close(done) go func(db *C.sqlite3) { select { case <-ctx.Done(): C.sqlite3_interrupt(db) case <-done: } }(s.c.db) var rowid, changes C.longlong rv := C._sqlite3_step(s.s, &rowid, &changes) if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { err := s.c.lastError() C.sqlite3_reset(s.s) C.sqlite3_clear_bindings(s.s) return nil, err } return &SQLiteResult{id: int64(rowid), changes: int64(changes)}, nil } // Close the rows. func (rc *SQLiteRows) Close() error { rc.s.mu.Lock() if rc.s.closed || rc.closed { rc.s.mu.Unlock() return nil } rc.closed = true if rc.done != nil { close(rc.done) } if rc.cls { rc.s.mu.Unlock() return rc.s.Close() } rv := C.sqlite3_reset(rc.s.s) if rv != C.SQLITE_OK { rc.s.mu.Unlock() return rc.s.c.lastError() } rc.s.mu.Unlock() return nil } // Columns return column names. func (rc *SQLiteRows) Columns() []string { rc.s.mu.Lock() defer rc.s.mu.Unlock() if rc.s.s != nil && rc.nc != len(rc.cols) { rc.cols = make([]string, rc.nc) for i := 0; i < rc.nc; i++ { rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i))) } } return rc.cols } func (rc *SQLiteRows) declTypes() []string { if rc.s.s != nil && rc.decltype == nil { rc.decltype = make([]string, rc.nc) for i := 0; i < rc.nc; i++ { rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) } } return rc.decltype } // DeclTypes return column types. func (rc *SQLiteRows) DeclTypes() []string { rc.s.mu.Lock() defer rc.s.mu.Unlock() return rc.declTypes() } // Next move cursor to next. func (rc *SQLiteRows) Next(dest []driver.Value) error { if rc.s.closed { return io.EOF } rc.s.mu.Lock() defer rc.s.mu.Unlock() rv := C.sqlite3_step(rc.s.s) if rv == C.SQLITE_DONE { return io.EOF } if rv != C.SQLITE_ROW { rv = C.sqlite3_reset(rc.s.s) if rv != C.SQLITE_OK { return rc.s.c.lastError() } return nil } rc.declTypes() for i := range dest { switch C.sqlite3_column_type(rc.s.s, C.int(i)) { case C.SQLITE_INTEGER: val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i))) switch rc.decltype[i] { case "timestamp", "datetime", "date": var t time.Time // Assume a millisecond unix timestamp if it's 13 digits -- too // large to be a reasonable timestamp in seconds. if val > 1e12 || val < -1e12 { val *= int64(time.Millisecond) // convert ms to nsec t = time.Unix(0, val) } else { t = time.Unix(val, 0) } t = t.UTC() if rc.s.c.loc != nil { t = t.In(rc.s.c.loc) } dest[i] = t case "boolean": dest[i] = val > 0 default: dest[i] = val } case C.SQLITE_FLOAT: dest[i] = float64(C.sqlite3_column_double(rc.s.s, C.int(i))) case C.SQLITE_BLOB: p := C.sqlite3_column_blob(rc.s.s, C.int(i)) if p == nil { dest[i] = nil continue } n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i))) switch dest[i].(type) { case sql.RawBytes: dest[i] = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n] default: slice := make([]byte, n) copy(slice[:], (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]) dest[i] = slice } case C.SQLITE_NULL: dest[i] = nil case C.SQLITE_TEXT: var err error var timeVal time.Time n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i))) s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n)) switch rc.decltype[i] { case "timestamp", "datetime", "date": var t time.Time s = strings.TrimSuffix(s, "Z") for _, format := range SQLiteTimestampFormats { if timeVal, err = time.ParseInLocation(format, s, time.UTC); err == nil { t = timeVal break } } if err != nil { // The column is a time value, so return the zero time on parse failure. t = time.Time{} } if rc.s.c.loc != nil { t = t.In(rc.s.c.loc) } dest[i] = t default: dest[i] = []byte(s) } } } return nil } go-sqlite3-1.4.0/sqlite3_context.go000066400000000000000000000057211320252770200171670ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package sqlite3 /* #ifndef USE_LIBSQLITE3 #include #else #include #endif #include // These wrappers are necessary because SQLITE_TRANSIENT // is a pointer constant, and cgo doesn't translate them correctly. static inline void my_result_text(sqlite3_context *ctx, char *p, int np) { sqlite3_result_text(ctx, p, np, SQLITE_TRANSIENT); } static inline void my_result_blob(sqlite3_context *ctx, void *p, int np) { sqlite3_result_blob(ctx, p, np, SQLITE_TRANSIENT); } */ import "C" import ( "math" "reflect" "unsafe" ) const i64 = unsafe.Sizeof(int(0)) > 4 // SQLiteContext behave sqlite3_context type SQLiteContext C.sqlite3_context // ResultBool sets the result of an SQL function. func (c *SQLiteContext) ResultBool(b bool) { if b { c.ResultInt(1) } else { c.ResultInt(0) } } // ResultBlob sets the result of an SQL function. // See: sqlite3_result_blob, http://sqlite.org/c3ref/result_blob.html func (c *SQLiteContext) ResultBlob(b []byte) { if i64 && len(b) > math.MaxInt32 { C.sqlite3_result_error_toobig((*C.sqlite3_context)(c)) return } var p *byte if len(b) > 0 { p = &b[0] } C.my_result_blob((*C.sqlite3_context)(c), unsafe.Pointer(p), C.int(len(b))) } // ResultDouble sets the result of an SQL function. // See: sqlite3_result_double, http://sqlite.org/c3ref/result_blob.html func (c *SQLiteContext) ResultDouble(d float64) { C.sqlite3_result_double((*C.sqlite3_context)(c), C.double(d)) } // ResultInt sets the result of an SQL function. // See: sqlite3_result_int, http://sqlite.org/c3ref/result_blob.html func (c *SQLiteContext) ResultInt(i int) { if i64 && (i > math.MaxInt32 || i < math.MinInt32) { C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) } else { C.sqlite3_result_int((*C.sqlite3_context)(c), C.int(i)) } } // ResultInt64 sets the result of an SQL function. // See: sqlite3_result_int64, http://sqlite.org/c3ref/result_blob.html func (c *SQLiteContext) ResultInt64(i int64) { C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) } // ResultNull sets the result of an SQL function. // See: sqlite3_result_null, http://sqlite.org/c3ref/result_blob.html func (c *SQLiteContext) ResultNull() { C.sqlite3_result_null((*C.sqlite3_context)(c)) } // ResultText sets the result of an SQL function. // See: sqlite3_result_text, http://sqlite.org/c3ref/result_blob.html func (c *SQLiteContext) ResultText(s string) { h := (*reflect.StringHeader)(unsafe.Pointer(&s)) cs, l := (*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len) C.my_result_text((*C.sqlite3_context)(c), cs, l) } // ResultZeroblob sets the result of an SQL function. // See: sqlite3_result_zeroblob, http://sqlite.org/c3ref/result_blob.html func (c *SQLiteContext) ResultZeroblob(n int) { C.sqlite3_result_zeroblob((*C.sqlite3_context)(c), C.int(n)) } go-sqlite3-1.4.0/sqlite3_fts3_test.go000066400000000000000000000063551320252770200174250ustar00rootroot00000000000000// Copyright (C) 2015 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package sqlite3 import ( "database/sql" "os" "testing" ) func TestFTS3(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("DROP TABLE foo") _, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts3(id INTEGER PRIMARY KEY, value TEXT)") if err != nil { t.Fatal("Failed to create table:", err) } _, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `今日の 晩御飯は 天麩羅よ`) if err != nil { t.Fatal("Failed to insert value:", err) } _, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 2, `今日は いい 天気だ`) if err != nil { t.Fatal("Failed to insert value:", err) } rows, err := db.Query("SELECT id, value FROM foo WHERE value MATCH '今日* 天*'") if err != nil { t.Fatal("Unable to query foo table:", err) } defer rows.Close() for rows.Next() { var id int var value string if err := rows.Scan(&id, &value); err != nil { t.Error("Unable to scan results:", err) continue } if id == 1 && value != `今日の 晩御飯は 天麩羅よ` { t.Error("Value for id 1 should be `今日の 晩御飯は 天麩羅よ`, but:", value) } else if id == 2 && value != `今日は いい 天気だ` { t.Error("Value for id 2 should be `今日は いい 天気だ`, but:", value) } } rows, err = db.Query("SELECT value FROM foo WHERE value MATCH '今日* 天麩羅*'") if err != nil { t.Fatal("Unable to query foo table:", err) } defer rows.Close() var value string if !rows.Next() { t.Fatal("Result should be only one") } if err := rows.Scan(&value); err != nil { t.Fatal("Unable to scan results:", err) } if value != `今日の 晩御飯は 天麩羅よ` { t.Fatal("Value should be `今日の 晩御飯は 天麩羅よ`, but:", value) } if rows.Next() { t.Fatal("Result should be only one") } } func TestFTS4(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("DROP TABLE foo") _, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts4(tokenize=unicode61, id INTEGER PRIMARY KEY, value TEXT)") switch { case err != nil && err.Error() == "unknown tokenizer: unicode61": t.Skip("FTS4 not supported") case err != nil: t.Fatal("Failed to create table:", err) } _, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `février`) if err != nil { t.Fatal("Failed to insert value:", err) } rows, err := db.Query("SELECT value FROM foo WHERE value MATCH 'fevrier'") if err != nil { t.Fatal("Unable to query foo table:", err) } defer rows.Close() var value string if !rows.Next() { t.Fatal("Result should be only one") } if err := rows.Scan(&value); err != nil { t.Fatal("Unable to scan results:", err) } if value != `février` { t.Fatal("Value should be `février`, but:", value) } if rows.Next() { t.Fatal("Result should be only one") } } go-sqlite3-1.4.0/sqlite3_fts5.go000066400000000000000000000004211320252770200163540ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build fts5 package sqlite3 /* #cgo CFLAGS: -DSQLITE_ENABLE_FTS5 #cgo LDFLAGS: -lm */ import "C" go-sqlite3-1.4.0/sqlite3_go18.go000066400000000000000000000035251320252770200162610ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build go1.8 package sqlite3 import ( "database/sql/driver" "errors" "context" ) // Ping implement Pinger. func (c *SQLiteConn) Ping(ctx context.Context) error { if c.db == nil { return errors.New("Connection was closed") } return nil } // QueryContext implement QueryerContext. func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { list := make([]namedValue, len(args)) for i, nv := range args { list[i] = namedValue(nv) } return c.query(ctx, query, list) } // ExecContext implement ExecerContext. func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { list := make([]namedValue, len(args)) for i, nv := range args { list[i] = namedValue(nv) } return c.exec(ctx, query, list) } // PrepareContext implement ConnPrepareContext. func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { return c.prepare(ctx, query) } // BeginTx implement ConnBeginTx. func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { return c.begin(ctx) } // QueryContext implement QueryerContext. func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { list := make([]namedValue, len(args)) for i, nv := range args { list[i] = namedValue(nv) } return s.query(ctx, list) } // ExecContext implement ExecerContext. func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { list := make([]namedValue, len(args)) for i, nv := range args { list[i] = namedValue(nv) } return s.exec(ctx, list) } go-sqlite3-1.4.0/sqlite3_go18_test.go000066400000000000000000000062241320252770200173170ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build go1.8 package sqlite3 import ( "context" "database/sql" "fmt" "math/rand" "os" "testing" "time" ) func TestNamedParams(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec(` create table foo (id integer, name text, extra text); `) if err != nil { t.Error("Failed to call db.Query:", err) } _, err = db.Exec(`insert into foo(id, name, extra) values(:id, :name, :name)`, sql.Named("name", "foo"), sql.Named("id", 1)) if err != nil { t.Error("Failed to call db.Exec:", err) } row := db.QueryRow(`select id, extra from foo where id = :id and extra = :extra`, sql.Named("id", 1), sql.Named("extra", "foo")) if row == nil { t.Error("Failed to call db.QueryRow") } var id int var extra string err = row.Scan(&id, &extra) if err != nil { t.Error("Failed to db.Scan:", err) } if id != 1 || extra != "foo" { t.Error("Failed to db.QueryRow: not matched results") } } var ( testTableStatements = []string{ `DROP TABLE IF EXISTS test_table`, ` CREATE TABLE IF NOT EXISTS test_table ( key1 VARCHAR(64) PRIMARY KEY, key_id VARCHAR(64) NOT NULL, key2 VARCHAR(64) NOT NULL, key3 VARCHAR(64) NOT NULL, key4 VARCHAR(64) NOT NULL, key5 VARCHAR(64) NOT NULL, key6 VARCHAR(64) NOT NULL, data BLOB NOT NULL );`, } letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ) func randStringBytes(n int) string { b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] } return string(b) } func initDatabase(t *testing.T, db *sql.DB, rowCount int64) { t.Logf("Executing db initializing statements") for _, query := range testTableStatements { _, err := db.Exec(query) if err != nil { t.Fatal(err) } } for i := int64(0); i < rowCount; i++ { query := `INSERT INTO test_table (key1, key_id, key2, key3, key4, key5, key6, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?);` args := []interface{}{ randStringBytes(50), fmt.Sprint(i), randStringBytes(50), randStringBytes(50), randStringBytes(50), randStringBytes(50), randStringBytes(50), randStringBytes(50), randStringBytes(2048), } _, err := db.Exec(query, args...) if err != nil { t.Fatal(err) } } } func TestShortTimeout(t *testing.T) { srcTempFilename := TempFilename(t) defer os.Remove(srcTempFilename) db, err := sql.Open("sqlite3", srcTempFilename) if err != nil { t.Fatal(err) } defer db.Close() initDatabase(t, db, 100) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond) defer cancel() query := `SELECT key1, key_id, key2, key3, key4, key5, key6, data FROM test_table ORDER BY key2 ASC` _, err = db.QueryContext(ctx, query) if err != nil && err != context.DeadlineExceeded { t.Fatal(err) } if ctx.Err() != nil && ctx.Err() != context.DeadlineExceeded { t.Fatal(ctx.Err()) } } go-sqlite3-1.4.0/sqlite3_icu.go000066400000000000000000000004351320252770200162600ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build icu package sqlite3 /* #cgo LDFLAGS: -licuuc -licui18n #cgo CFLAGS: -DSQLITE_ENABLE_ICU */ import "C" go-sqlite3-1.4.0/sqlite3_json1.go000066400000000000000000000004011320252770200165230ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build json1 package sqlite3 /* #cgo CFLAGS: -DSQLITE_ENABLE_JSON1 */ import "C" go-sqlite3-1.4.0/sqlite3_libsqlite3.go000066400000000000000000000005721320252770200175550ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build libsqlite3 package sqlite3 /* #cgo CFLAGS: -DUSE_LIBSQLITE3 #cgo linux LDFLAGS: -lsqlite3 #cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3 #cgo solaris LDFLAGS: -lsqlite3 */ import "C" go-sqlite3-1.4.0/sqlite3_load_extension.go000066400000000000000000000032161320252770200205130ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build !sqlite_omit_load_extension package sqlite3 /* #ifndef USE_LIBSQLITE3 #include #else #include #endif #include */ import "C" import ( "errors" "unsafe" ) func (c *SQLiteConn) loadExtensions(extensions []string) error { rv := C.sqlite3_enable_load_extension(c.db, 1) if rv != C.SQLITE_OK { return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) } for _, extension := range extensions { cext := C.CString(extension) defer C.free(unsafe.Pointer(cext)) rv = C.sqlite3_load_extension(c.db, cext, nil, nil) if rv != C.SQLITE_OK { C.sqlite3_enable_load_extension(c.db, 0) return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) } } rv = C.sqlite3_enable_load_extension(c.db, 0) if rv != C.SQLITE_OK { return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) } return nil } // LoadExtension load the sqlite3 extension. func (c *SQLiteConn) LoadExtension(lib string, entry string) error { rv := C.sqlite3_enable_load_extension(c.db, 1) if rv != C.SQLITE_OK { return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) } clib := C.CString(lib) defer C.free(unsafe.Pointer(clib)) centry := C.CString(entry) defer C.free(unsafe.Pointer(centry)) rv = C.sqlite3_load_extension(c.db, clib, centry, nil) if rv != C.SQLITE_OK { return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) } rv = C.sqlite3_enable_load_extension(c.db, 0) if rv != C.SQLITE_OK { return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) } return nil } go-sqlite3-1.4.0/sqlite3_omit_load_extension.go000066400000000000000000000011121320252770200215340ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build sqlite_omit_load_extension package sqlite3 /* #cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION */ import "C" import ( "errors" ) func (c *SQLiteConn) loadExtensions(extensions []string) error { return errors.New("Extensions have been disabled for static builds") } func (c *SQLiteConn) LoadExtension(lib string, entry string) error { return errors.New("Extensions have been disabled for static builds") } go-sqlite3-1.4.0/sqlite3_other.go000066400000000000000000000004451320252770200166220ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build !windows package sqlite3 /* #cgo CFLAGS: -I. #cgo linux LDFLAGS: -ldl #cgo solaris LDFLAGS: -lc */ import "C" go-sqlite3-1.4.0/sqlite3_test.go000066400000000000000000001427231320252770200164660ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package sqlite3 import ( "bytes" "database/sql" "database/sql/driver" "errors" "fmt" "io/ioutil" "math/rand" "net/url" "os" "reflect" "regexp" "strconv" "strings" "sync" "testing" "time" ) func TempFilename(t *testing.T) string { f, err := ioutil.TempFile("", "go-sqlite3-test-") if err != nil { t.Fatal(err) } f.Close() return f.Name() } func doTestOpen(t *testing.T, option string) (string, error) { var url string tempFilename := TempFilename(t) defer os.Remove(tempFilename) if option != "" { url = tempFilename + option } else { url = tempFilename } db, err := sql.Open("sqlite3", url) if err != nil { return "Failed to open database:", err } defer os.Remove(tempFilename) defer db.Close() _, err = db.Exec("drop table foo") _, err = db.Exec("create table foo (id integer)") if err != nil { return "Failed to create table:", err } if stat, err := os.Stat(tempFilename); err != nil || stat.IsDir() { return "Failed to create ./foo.db", nil } return "", nil } func TestOpen(t *testing.T) { cases := map[string]bool{ "": true, "?_txlock=immediate": true, "?_txlock=deferred": true, "?_txlock=exclusive": true, "?_txlock=bogus": false, } for option, expectedPass := range cases { result, err := doTestOpen(t, option) if result == "" { if !expectedPass { errmsg := fmt.Sprintf("_txlock error not caught at dbOpen with option: %s", option) t.Fatal(errmsg) } } else if expectedPass { if err == nil { t.Fatal(result) } else { t.Fatal(result, err) } } } } func TestReadonly(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db1, err := sql.Open("sqlite3", "file:"+tempFilename) if err != nil { t.Fatal(err) } db1.Exec("CREATE TABLE test (x int, y float)") db2, err := sql.Open("sqlite3", "file:"+tempFilename+"?mode=ro") if err != nil { t.Fatal(err) } _ = db2 _, err = db2.Exec("INSERT INTO test VALUES (1, 3.14)") if err == nil { t.Fatal("didn't expect INSERT into read-only database to work") } } func TestForeignKeys(t *testing.T) { cases := map[string]bool{ "?_foreign_keys=1": true, "?_foreign_keys=0": false, } for option, want := range cases { fname := TempFilename(t) uri := "file:" + fname + option db, err := sql.Open("sqlite3", uri) if err != nil { os.Remove(fname) t.Errorf("sql.Open(\"sqlite3\", %q): %v", uri, err) continue } var enabled bool err = db.QueryRow("PRAGMA foreign_keys;").Scan(&enabled) db.Close() os.Remove(fname) if err != nil { t.Errorf("query foreign_keys for %s: %v", uri, err) continue } if enabled != want { t.Errorf("\"PRAGMA foreign_keys;\" for %q = %t; want %t", uri, enabled, want) continue } } } func TestRecursiveTriggers(t *testing.T) { cases := map[string]bool{ "?_recursive_triggers=1": true, "?_recursive_triggers=0": false, } for option, want := range cases { fname := TempFilename(t) uri := "file:" + fname + option db, err := sql.Open("sqlite3", uri) if err != nil { os.Remove(fname) t.Errorf("sql.Open(\"sqlite3\", %q): %v", uri, err) continue } var enabled bool err = db.QueryRow("PRAGMA recursive_triggers;").Scan(&enabled) db.Close() os.Remove(fname) if err != nil { t.Errorf("query recursive_triggers for %s: %v", uri, err) continue } if enabled != want { t.Errorf("\"PRAGMA recursive_triggers;\" for %q = %t; want %t", uri, enabled, want) continue } } } func TestClose(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } _, err = db.Exec("drop table foo") _, err = db.Exec("create table foo (id integer)") if err != nil { t.Fatal("Failed to create table:", err) } stmt, err := db.Prepare("select id from foo where id = ?") if err != nil { t.Fatal("Failed to select records:", err) } db.Close() _, err = stmt.Exec(1) if err == nil { t.Fatal("Failed to operate closed statement") } } func TestInsert(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("drop table foo") _, err = db.Exec("create table foo (id integer)") if err != nil { t.Fatal("Failed to create table:", err) } res, err := db.Exec("insert into foo(id) values(123)") if err != nil { t.Fatal("Failed to insert record:", err) } affected, _ := res.RowsAffected() if affected != 1 { t.Fatalf("Expected %d for affected rows, but %d:", 1, affected) } rows, err := db.Query("select id from foo") if err != nil { t.Fatal("Failed to select records:", err) } defer rows.Close() rows.Next() var result int rows.Scan(&result) if result != 123 { t.Errorf("Expected %d for fetched result, but %d:", 123, result) } } func TestUpdate(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("drop table foo") _, err = db.Exec("create table foo (id integer)") if err != nil { t.Fatal("Failed to create table:", err) } res, err := db.Exec("insert into foo(id) values(123)") if err != nil { t.Fatal("Failed to insert record:", err) } expected, err := res.LastInsertId() if err != nil { t.Fatal("Failed to get LastInsertId:", err) } affected, _ := res.RowsAffected() if err != nil { t.Fatal("Failed to get RowsAffected:", err) } if affected != 1 { t.Fatalf("Expected %d for affected rows, but %d:", 1, affected) } res, err = db.Exec("update foo set id = 234") if err != nil { t.Fatal("Failed to update record:", err) } lastID, err := res.LastInsertId() if err != nil { t.Fatal("Failed to get LastInsertId:", err) } if expected != lastID { t.Errorf("Expected %q for last Id, but %q:", expected, lastID) } affected, _ = res.RowsAffected() if err != nil { t.Fatal("Failed to get RowsAffected:", err) } if affected != 1 { t.Fatalf("Expected %d for affected rows, but %d:", 1, affected) } rows, err := db.Query("select id from foo") if err != nil { t.Fatal("Failed to select records:", err) } defer rows.Close() rows.Next() var result int rows.Scan(&result) if result != 234 { t.Errorf("Expected %d for fetched result, but %d:", 234, result) } } func TestDelete(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("drop table foo") _, err = db.Exec("create table foo (id integer)") if err != nil { t.Fatal("Failed to create table:", err) } res, err := db.Exec("insert into foo(id) values(123)") if err != nil { t.Fatal("Failed to insert record:", err) } expected, err := res.LastInsertId() if err != nil { t.Fatal("Failed to get LastInsertId:", err) } affected, err := res.RowsAffected() if err != nil { t.Fatal("Failed to get RowsAffected:", err) } if affected != 1 { t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected) } res, err = db.Exec("delete from foo where id = 123") if err != nil { t.Fatal("Failed to delete record:", err) } lastID, err := res.LastInsertId() if err != nil { t.Fatal("Failed to get LastInsertId:", err) } if expected != lastID { t.Errorf("Expected %q for last Id, but %q:", expected, lastID) } affected, err = res.RowsAffected() if err != nil { t.Fatal("Failed to get RowsAffected:", err) } if affected != 1 { t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected) } rows, err := db.Query("select id from foo") if err != nil { t.Fatal("Failed to select records:", err) } defer rows.Close() if rows.Next() { t.Error("Fetched row but expected not rows") } } func TestBooleanRoundtrip(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("DROP TABLE foo") _, err = db.Exec("CREATE TABLE foo(id INTEGER, value BOOL)") if err != nil { t.Fatal("Failed to create table:", err) } _, err = db.Exec("INSERT INTO foo(id, value) VALUES(1, ?)", true) if err != nil { t.Fatal("Failed to insert true value:", err) } _, err = db.Exec("INSERT INTO foo(id, value) VALUES(2, ?)", false) if err != nil { t.Fatal("Failed to insert false value:", err) } rows, err := db.Query("SELECT id, value FROM foo") if err != nil { t.Fatal("Unable to query foo table:", err) } defer rows.Close() for rows.Next() { var id int var value bool if err := rows.Scan(&id, &value); err != nil { t.Error("Unable to scan results:", err) continue } if id == 1 && !value { t.Error("Value for id 1 should be true, not false") } else if id == 2 && value { t.Error("Value for id 2 should be false, not true") } } } func timezone(t time.Time) string { return t.Format("-07:00") } func TestTimestamp(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("DROP TABLE foo") _, err = db.Exec("CREATE TABLE foo(id INTEGER, ts timeSTAMP, dt DATETIME)") if err != nil { t.Fatal("Failed to create table:", err) } timestamp1 := time.Date(2012, time.April, 6, 22, 50, 0, 0, time.UTC) timestamp2 := time.Date(2006, time.January, 2, 15, 4, 5, 123456789, time.UTC) timestamp3 := time.Date(2012, time.November, 4, 0, 0, 0, 0, time.UTC) tzTest := time.FixedZone("TEST", -9*3600-13*60) tests := []struct { value interface{} expected time.Time }{ {"nonsense", time.Time{}}, {"0000-00-00 00:00:00", time.Time{}}, {time.Time{}.Unix(), time.Time{}}, {timestamp1, timestamp1}, {timestamp2.Unix(), timestamp2.Truncate(time.Second)}, {timestamp2.UnixNano() / int64(time.Millisecond), timestamp2.Truncate(time.Millisecond)}, {timestamp1.In(tzTest), timestamp1.In(tzTest)}, {timestamp1.Format("2006-01-02 15:04:05.000"), timestamp1}, {timestamp1.Format("2006-01-02T15:04:05.000"), timestamp1}, {timestamp1.Format("2006-01-02 15:04:05"), timestamp1}, {timestamp1.Format("2006-01-02T15:04:05"), timestamp1}, {timestamp2, timestamp2}, {"2006-01-02 15:04:05.123456789", timestamp2}, {"2006-01-02T15:04:05.123456789", timestamp2}, {"2006-01-02T05:51:05.123456789-09:13", timestamp2.In(tzTest)}, {"2012-11-04", timestamp3}, {"2012-11-04 00:00", timestamp3}, {"2012-11-04 00:00:00", timestamp3}, {"2012-11-04 00:00:00.000", timestamp3}, {"2012-11-04T00:00", timestamp3}, {"2012-11-04T00:00:00", timestamp3}, {"2012-11-04T00:00:00.000", timestamp3}, {"2006-01-02T15:04:05.123456789Z", timestamp2}, {"2012-11-04Z", timestamp3}, {"2012-11-04 00:00Z", timestamp3}, {"2012-11-04 00:00:00Z", timestamp3}, {"2012-11-04 00:00:00.000Z", timestamp3}, {"2012-11-04T00:00Z", timestamp3}, {"2012-11-04T00:00:00Z", timestamp3}, {"2012-11-04T00:00:00.000Z", timestamp3}, } for i := range tests { _, err = db.Exec("INSERT INTO foo(id, ts, dt) VALUES(?, ?, ?)", i, tests[i].value, tests[i].value) if err != nil { t.Fatal("Failed to insert timestamp:", err) } } rows, err := db.Query("SELECT id, ts, dt FROM foo ORDER BY id ASC") if err != nil { t.Fatal("Unable to query foo table:", err) } defer rows.Close() seen := 0 for rows.Next() { var id int var ts, dt time.Time if err := rows.Scan(&id, &ts, &dt); err != nil { t.Error("Unable to scan results:", err) continue } if id < 0 || id >= len(tests) { t.Error("Bad row id: ", id) continue } seen++ if !tests[id].expected.Equal(ts) { t.Errorf("Timestamp value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt) } if !tests[id].expected.Equal(dt) { t.Errorf("Datetime value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt) } if timezone(tests[id].expected) != timezone(ts) { t.Errorf("Timezone for id %v (%v) should be %v, not %v", id, tests[id].value, timezone(tests[id].expected), timezone(ts)) } if timezone(tests[id].expected) != timezone(dt) { t.Errorf("Timezone for id %v (%v) should be %v, not %v", id, tests[id].value, timezone(tests[id].expected), timezone(dt)) } } if seen != len(tests) { t.Errorf("Expected to see %d rows", len(tests)) } } func TestBoolean(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("CREATE TABLE foo(id INTEGER, fbool BOOLEAN)") if err != nil { t.Fatal("Failed to create table:", err) } bool1 := true _, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(1, ?)", bool1) if err != nil { t.Fatal("Failed to insert boolean:", err) } bool2 := false _, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(2, ?)", bool2) if err != nil { t.Fatal("Failed to insert boolean:", err) } bool3 := "nonsense" _, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(3, ?)", bool3) if err != nil { t.Fatal("Failed to insert nonsense:", err) } rows, err := db.Query("SELECT id, fbool FROM foo where fbool = ?", bool1) if err != nil { t.Fatal("Unable to query foo table:", err) } counter := 0 var id int var fbool bool for rows.Next() { if err := rows.Scan(&id, &fbool); err != nil { t.Fatal("Unable to scan results:", err) } counter++ } if counter != 1 { t.Fatalf("Expected 1 row but %v", counter) } if id != 1 && fbool != true { t.Fatalf("Value for id 1 should be %v, not %v", bool1, fbool) } rows, err = db.Query("SELECT id, fbool FROM foo where fbool = ?", bool2) if err != nil { t.Fatal("Unable to query foo table:", err) } counter = 0 for rows.Next() { if err := rows.Scan(&id, &fbool); err != nil { t.Fatal("Unable to scan results:", err) } counter++ } if counter != 1 { t.Fatalf("Expected 1 row but %v", counter) } if id != 2 && fbool != false { t.Fatalf("Value for id 2 should be %v, not %v", bool2, fbool) } // make sure "nonsense" triggered an error rows, err = db.Query("SELECT id, fbool FROM foo where id=?;", 3) if err != nil { t.Fatal("Unable to query foo table:", err) } rows.Next() err = rows.Scan(&id, &fbool) if err == nil { t.Error("Expected error from \"nonsense\" bool") } } func TestFloat32(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("CREATE TABLE foo(id INTEGER)") if err != nil { t.Fatal("Failed to create table:", err) } _, err = db.Exec("INSERT INTO foo(id) VALUES(null)") if err != nil { t.Fatal("Failed to insert null:", err) } rows, err := db.Query("SELECT id FROM foo") if err != nil { t.Fatal("Unable to query foo table:", err) } if !rows.Next() { t.Fatal("Unable to query results:", err) } var id interface{} if err := rows.Scan(&id); err != nil { t.Fatal("Unable to scan results:", err) } if id != nil { t.Error("Expected nil but not") } } func TestNull(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() rows, err := db.Query("SELECT 3.141592") if err != nil { t.Fatal("Unable to query foo table:", err) } if !rows.Next() { t.Fatal("Unable to query results:", err) } var v interface{} if err := rows.Scan(&v); err != nil { t.Fatal("Unable to scan results:", err) } f, ok := v.(float64) if !ok { t.Error("Expected float but not") } if f != 3.141592 { t.Error("Expected 3.141592 but not") } } func TestTransaction(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("CREATE TABLE foo(id INTEGER)") if err != nil { t.Fatal("Failed to create table:", err) } tx, err := db.Begin() if err != nil { t.Fatal("Failed to begin transaction:", err) } _, err = tx.Exec("INSERT INTO foo(id) VALUES(1)") if err != nil { t.Fatal("Failed to insert null:", err) } rows, err := tx.Query("SELECT id from foo") if err != nil { t.Fatal("Unable to query foo table:", err) } err = tx.Rollback() if err != nil { t.Fatal("Failed to rollback transaction:", err) } if rows.Next() { t.Fatal("Unable to query results:", err) } tx, err = db.Begin() if err != nil { t.Fatal("Failed to begin transaction:", err) } _, err = tx.Exec("INSERT INTO foo(id) VALUES(1)") if err != nil { t.Fatal("Failed to insert null:", err) } err = tx.Commit() if err != nil { t.Fatal("Failed to commit transaction:", err) } rows, err = tx.Query("SELECT id from foo") if err == nil { t.Fatal("Expected failure to query") } } func TestWAL(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() if _, err = db.Exec("PRAGMA journal_mode=WAL;"); err != nil { t.Fatal("Failed to Exec PRAGMA journal_mode:", err) } if _, err = db.Exec("PRAGMA locking_mode=EXCLUSIVE;"); err != nil { t.Fatal("Failed to Exec PRAGMA locking_mode:", err) } if _, err = db.Exec("CREATE TABLE test (id SERIAL, user TEXT NOT NULL, name TEXT NOT NULL);"); err != nil { t.Fatal("Failed to Exec CREATE TABLE:", err) } if _, err = db.Exec("INSERT INTO test (user, name) VALUES ('user','name');"); err != nil { t.Fatal("Failed to Exec INSERT:", err) } trans, err := db.Begin() if err != nil { t.Fatal("Failed to Begin:", err) } s, err := trans.Prepare("INSERT INTO test (user, name) VALUES (?, ?);") if err != nil { t.Fatal("Failed to Prepare:", err) } var count int if err = trans.QueryRow("SELECT count(user) FROM test;").Scan(&count); err != nil { t.Fatal("Failed to QueryRow:", err) } if _, err = s.Exec("bbbb", "aaaa"); err != nil { t.Fatal("Failed to Exec prepared statement:", err) } if err = s.Close(); err != nil { t.Fatal("Failed to Close prepared statement:", err) } if err = trans.Commit(); err != nil { t.Fatal("Failed to Commit:", err) } } func TestTimezoneConversion(t *testing.T) { zones := []string{"UTC", "US/Central", "US/Pacific", "Local"} for _, tz := range zones { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename+"?_loc="+url.QueryEscape(tz)) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("DROP TABLE foo") _, err = db.Exec("CREATE TABLE foo(id INTEGER, ts TIMESTAMP, dt DATETIME)") if err != nil { t.Fatal("Failed to create table:", err) } loc, err := time.LoadLocation(tz) if err != nil { t.Fatal("Failed to load location:", err) } timestamp1 := time.Date(2012, time.April, 6, 22, 50, 0, 0, time.UTC) timestamp2 := time.Date(2006, time.January, 2, 15, 4, 5, 123456789, time.UTC) timestamp3 := time.Date(2012, time.November, 4, 0, 0, 0, 0, time.UTC) tests := []struct { value interface{} expected time.Time }{ {"nonsense", time.Time{}.In(loc)}, {"0000-00-00 00:00:00", time.Time{}.In(loc)}, {timestamp1, timestamp1.In(loc)}, {timestamp1.Unix(), timestamp1.In(loc)}, {timestamp1.In(time.FixedZone("TEST", -7*3600)), timestamp1.In(loc)}, {timestamp1.Format("2006-01-02 15:04:05.000"), timestamp1.In(loc)}, {timestamp1.Format("2006-01-02T15:04:05.000"), timestamp1.In(loc)}, {timestamp1.Format("2006-01-02 15:04:05"), timestamp1.In(loc)}, {timestamp1.Format("2006-01-02T15:04:05"), timestamp1.In(loc)}, {timestamp2, timestamp2.In(loc)}, {"2006-01-02 15:04:05.123456789", timestamp2.In(loc)}, {"2006-01-02T15:04:05.123456789", timestamp2.In(loc)}, {"2012-11-04", timestamp3.In(loc)}, {"2012-11-04 00:00", timestamp3.In(loc)}, {"2012-11-04 00:00:00", timestamp3.In(loc)}, {"2012-11-04 00:00:00.000", timestamp3.In(loc)}, {"2012-11-04T00:00", timestamp3.In(loc)}, {"2012-11-04T00:00:00", timestamp3.In(loc)}, {"2012-11-04T00:00:00.000", timestamp3.In(loc)}, } for i := range tests { _, err = db.Exec("INSERT INTO foo(id, ts, dt) VALUES(?, ?, ?)", i, tests[i].value, tests[i].value) if err != nil { t.Fatal("Failed to insert timestamp:", err) } } rows, err := db.Query("SELECT id, ts, dt FROM foo ORDER BY id ASC") if err != nil { t.Fatal("Unable to query foo table:", err) } defer rows.Close() seen := 0 for rows.Next() { var id int var ts, dt time.Time if err := rows.Scan(&id, &ts, &dt); err != nil { t.Error("Unable to scan results:", err) continue } if id < 0 || id >= len(tests) { t.Error("Bad row id: ", id) continue } seen++ if !tests[id].expected.Equal(ts) { t.Errorf("Timestamp value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, ts) } if !tests[id].expected.Equal(dt) { t.Errorf("Datetime value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt) } if tests[id].expected.Location().String() != ts.Location().String() { t.Errorf("Location for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected.Location().String(), ts.Location().String()) } if tests[id].expected.Location().String() != dt.Location().String() { t.Errorf("Location for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected.Location().String(), dt.Location().String()) } } if seen != len(tests) { t.Errorf("Expected to see %d rows", len(tests)) } } } // TODO: Execer & Queryer currently disabled // https://github.com/mattn/go-sqlite3/issues/82 func TestExecer(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec(` create table foo (id integer); -- one comment insert into foo(id) values(?); insert into foo(id) values(?); insert into foo(id) values(?); -- another comment `, 1, 2, 3) if err != nil { t.Error("Failed to call db.Exec:", err) } } func TestQueryer(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec(` create table foo (id integer); `) if err != nil { t.Error("Failed to call db.Query:", err) } rows, err := db.Query(` insert into foo(id) values(?); insert into foo(id) values(?); insert into foo(id) values(?); select id from foo order by id; `, 3, 2, 1) if err != nil { t.Error("Failed to call db.Query:", err) } defer rows.Close() n := 1 if rows != nil { for rows.Next() { var id int err = rows.Scan(&id) if err != nil { t.Error("Failed to db.Query:", err) } if id != n { t.Error("Failed to db.Query: not matched results") } } } } func TestStress(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } db.Exec("CREATE TABLE foo (id int);") db.Exec("INSERT INTO foo VALUES(1);") db.Exec("INSERT INTO foo VALUES(2);") db.Close() for i := 0; i < 10000; i++ { db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } for j := 0; j < 3; j++ { rows, err := db.Query("select * from foo where id=1;") if err != nil { t.Error("Failed to call db.Query:", err) } for rows.Next() { var i int if err := rows.Scan(&i); err != nil { t.Errorf("Scan failed: %v\n", err) } } if err := rows.Err(); err != nil { t.Errorf("Post-scan failed: %v\n", err) } rows.Close() } db.Close() } } func TestDateTimeLocal(t *testing.T) { zone := "Asia/Tokyo" tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename+"?_loc="+zone) if err != nil { t.Fatal("Failed to open database:", err) } db.Exec("CREATE TABLE foo (dt datetime);") db.Exec("INSERT INTO foo VALUES('2015-03-05 15:16:17');") row := db.QueryRow("select * from foo") var d time.Time err = row.Scan(&d) if err != nil { t.Fatal("Failed to scan datetime:", err) } if d.Hour() == 15 || !strings.Contains(d.String(), "JST") { t.Fatal("Result should have timezone", d) } db.Close() db, err = sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } row = db.QueryRow("select * from foo") err = row.Scan(&d) if err != nil { t.Fatal("Failed to scan datetime:", err) } if d.UTC().Hour() != 15 || !strings.Contains(d.String(), "UTC") { t.Fatalf("Result should not have timezone %v %v", zone, d.String()) } _, err = db.Exec("DELETE FROM foo") if err != nil { t.Fatal("Failed to delete table:", err) } dt, err := time.Parse("2006/1/2 15/4/5 -0700 MST", "2015/3/5 15/16/17 +0900 JST") if err != nil { t.Fatal("Failed to parse datetime:", err) } db.Exec("INSERT INTO foo VALUES(?);", dt) db.Close() db, err = sql.Open("sqlite3", tempFilename+"?_loc="+zone) if err != nil { t.Fatal("Failed to open database:", err) } row = db.QueryRow("select * from foo") err = row.Scan(&d) if err != nil { t.Fatal("Failed to scan datetime:", err) } if d.Hour() != 15 || !strings.Contains(d.String(), "JST") { t.Fatalf("Result should have timezone %v %v", zone, d.String()) } } func TestVersion(t *testing.T) { s, n, id := Version() if s == "" || n == 0 || id == "" { t.Errorf("Version failed %q, %d, %q\n", s, n, id) } } func TestStringContainingZero(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec(` create table foo (id integer, name, extra text); `) if err != nil { t.Error("Failed to call db.Query:", err) } const text = "foo\x00bar" _, err = db.Exec(`insert into foo(id, name, extra) values($1, $2, $2)`, 1, text) if err != nil { t.Error("Failed to call db.Exec:", err) } row := db.QueryRow(`select id, extra from foo where id = $1 and extra = $2`, 1, text) if row == nil { t.Error("Failed to call db.QueryRow") } var id int var extra string err = row.Scan(&id, &extra) if err != nil { t.Error("Failed to db.Scan:", err) } if id != 1 || extra != text { t.Error("Failed to db.QueryRow: not matched results") } } const CurrentTimeStamp = "2006-01-02 15:04:05" type TimeStamp struct{ *time.Time } func (t TimeStamp) Scan(value interface{}) error { var err error switch v := value.(type) { case string: *t.Time, err = time.Parse(CurrentTimeStamp, v) case []byte: *t.Time, err = time.Parse(CurrentTimeStamp, string(v)) default: err = errors.New("invalid type for current_timestamp") } return err } func (t TimeStamp) Value() (driver.Value, error) { return t.Time.Format(CurrentTimeStamp), nil } func TestDateTimeNow(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) db, err := sql.Open("sqlite3", tempFilename) if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() var d time.Time err = db.QueryRow("SELECT datetime('now')").Scan(TimeStamp{&d}) if err != nil { t.Fatal("Failed to scan datetime:", err) } } func TestFunctionRegistration(t *testing.T) { addi8_16_32 := func(a int8, b int16) int32 { return int32(a) + int32(b) } addi64 := func(a, b int64) int64 { return a + b } addu8_16_32 := func(a uint8, b uint16) uint32 { return uint32(a) + uint32(b) } addu64 := func(a, b uint64) uint64 { return a + b } addiu := func(a int, b uint) int64 { return int64(a) + int64(b) } addf32_64 := func(a float32, b float64) float64 { return float64(a) + b } not := func(a bool) bool { return !a } regex := func(re, s string) (bool, error) { return regexp.MatchString(re, s) } generic := func(a interface{}) int64 { switch a.(type) { case int64: return 1 case float64: return 2 case []byte: return 3 case string: return 4 default: panic("unreachable") } } variadic := func(a, b int64, c ...int64) int64 { ret := a + b for _, d := range c { ret += d } return ret } variadicGeneric := func(a ...interface{}) int64 { return int64(len(a)) } sql.Register("sqlite3_FunctionRegistration", &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { if err := conn.RegisterFunc("addi8_16_32", addi8_16_32, true); err != nil { return err } if err := conn.RegisterFunc("addi64", addi64, true); err != nil { return err } if err := conn.RegisterFunc("addu8_16_32", addu8_16_32, true); err != nil { return err } if err := conn.RegisterFunc("addu64", addu64, true); err != nil { return err } if err := conn.RegisterFunc("addiu", addiu, true); err != nil { return err } if err := conn.RegisterFunc("addf32_64", addf32_64, true); err != nil { return err } if err := conn.RegisterFunc("not", not, true); err != nil { return err } if err := conn.RegisterFunc("regex", regex, true); err != nil { return err } if err := conn.RegisterFunc("generic", generic, true); err != nil { return err } if err := conn.RegisterFunc("variadic", variadic, true); err != nil { return err } if err := conn.RegisterFunc("variadicGeneric", variadicGeneric, true); err != nil { return err } return nil }, }) db, err := sql.Open("sqlite3_FunctionRegistration", ":memory:") if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() ops := []struct { query string expected interface{} }{ {"SELECT addi8_16_32(1,2)", int32(3)}, {"SELECT addi64(1,2)", int64(3)}, {"SELECT addu8_16_32(1,2)", uint32(3)}, {"SELECT addu64(1,2)", uint64(3)}, {"SELECT addiu(1,2)", int64(3)}, {"SELECT addf32_64(1.5,1.5)", float64(3)}, {"SELECT not(1)", false}, {"SELECT not(0)", true}, {`SELECT regex("^foo.*", "foobar")`, true}, {`SELECT regex("^foo.*", "barfoobar")`, false}, {"SELECT generic(1)", int64(1)}, {"SELECT generic(1.1)", int64(2)}, {`SELECT generic(NULL)`, int64(3)}, {`SELECT generic("foo")`, int64(4)}, {"SELECT variadic(1,2)", int64(3)}, {"SELECT variadic(1,2,3,4)", int64(10)}, {"SELECT variadic(1,1,1,1,1,1,1,1,1,1)", int64(10)}, {`SELECT variadicGeneric(1,"foo",2.3, NULL)`, int64(4)}, } for _, op := range ops { ret := reflect.New(reflect.TypeOf(op.expected)) err = db.QueryRow(op.query).Scan(ret.Interface()) if err != nil { t.Errorf("Query %q failed: %s", op.query, err) } else if !reflect.DeepEqual(ret.Elem().Interface(), op.expected) { t.Errorf("Query %q returned wrong value: got %v (%T), want %v (%T)", op.query, ret.Elem().Interface(), ret.Elem().Interface(), op.expected, op.expected) } } } type sumAggregator int64 func (s *sumAggregator) Step(x int64) { *s += sumAggregator(x) } func (s *sumAggregator) Done() int64 { return int64(*s) } func TestAggregatorRegistration(t *testing.T) { customSum := func() *sumAggregator { var ret sumAggregator return &ret } sql.Register("sqlite3_AggregatorRegistration", &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { if err := conn.RegisterAggregator("customSum", customSum, true); err != nil { return err } return nil }, }) db, err := sql.Open("sqlite3_AggregatorRegistration", ":memory:") if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() _, err = db.Exec("create table foo (department integer, profits integer)") if err != nil { // trace feature is not implemented t.Skip("Failed to create table:", err) } _, err = db.Exec("insert into foo values (1, 10), (1, 20), (2, 42)") if err != nil { t.Fatal("Failed to insert records:", err) } tests := []struct { dept, sum int64 }{ {1, 30}, {2, 42}, } for _, test := range tests { var ret int64 err = db.QueryRow("select customSum(profits) from foo where department = $1 group by department", test.dept).Scan(&ret) if err != nil { t.Fatal("Query failed:", err) } if ret != test.sum { t.Fatalf("Custom sum returned wrong value, got %d, want %d", ret, test.sum) } } } func rot13(r rune) rune { switch { case r >= 'A' && r <= 'Z': return 'A' + (r-'A'+13)%26 case r >= 'a' && r <= 'z': return 'a' + (r-'a'+13)%26 } return r } func TestCollationRegistration(t *testing.T) { collateRot13 := func(a, b string) int { ra, rb := strings.Map(rot13, a), strings.Map(rot13, b) return strings.Compare(ra, rb) } collateRot13Reverse := func(a, b string) int { return collateRot13(b, a) } sql.Register("sqlite3_CollationRegistration", &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { if err := conn.RegisterCollation("rot13", collateRot13); err != nil { return err } if err := conn.RegisterCollation("rot13reverse", collateRot13Reverse); err != nil { return err } return nil }, }) db, err := sql.Open("sqlite3_CollationRegistration", ":memory:") if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() populate := []string{ `CREATE TABLE test (s TEXT)`, `INSERT INTO test VALUES ("aaaa")`, `INSERT INTO test VALUES ("ffff")`, `INSERT INTO test VALUES ("qqqq")`, `INSERT INTO test VALUES ("tttt")`, `INSERT INTO test VALUES ("zzzz")`, } for _, stmt := range populate { if _, err := db.Exec(stmt); err != nil { t.Fatal("Failed to populate test DB:", err) } } ops := []struct { query string want []string }{ { "SELECT * FROM test ORDER BY s COLLATE rot13 ASC", []string{ "qqqq", "tttt", "zzzz", "aaaa", "ffff", }, }, { "SELECT * FROM test ORDER BY s COLLATE rot13 DESC", []string{ "ffff", "aaaa", "zzzz", "tttt", "qqqq", }, }, { "SELECT * FROM test ORDER BY s COLLATE rot13reverse ASC", []string{ "ffff", "aaaa", "zzzz", "tttt", "qqqq", }, }, { "SELECT * FROM test ORDER BY s COLLATE rot13reverse DESC", []string{ "qqqq", "tttt", "zzzz", "aaaa", "ffff", }, }, } for _, op := range ops { rows, err := db.Query(op.query) if err != nil { t.Fatalf("Query %q failed: %s", op.query, err) } got := []string{} defer rows.Close() for rows.Next() { var s string if err = rows.Scan(&s); err != nil { t.Fatalf("Reading row for %q: %s", op.query, err) } got = append(got, s) } if err = rows.Err(); err != nil { t.Fatalf("Reading rows for %q: %s", op.query, err) } if !reflect.DeepEqual(got, op.want) { t.Fatalf("Unexpected output from %q\ngot:\n%s\n\nwant:\n%s", op.query, strings.Join(got, "\n"), strings.Join(op.want, "\n")) } } } func TestDeclTypes(t *testing.T) { d := SQLiteDriver{} conn, err := d.Open(":memory:") if err != nil { t.Fatal("Failed to begin transaction:", err) } defer conn.Close() sqlite3conn := conn.(*SQLiteConn) _, err = sqlite3conn.Exec("create table foo (id integer not null primary key, name text)", nil) if err != nil { t.Fatal("Failed to create table:", err) } _, err = sqlite3conn.Exec("insert into foo(name) values(\"bar\")", nil) if err != nil { t.Fatal("Failed to insert:", err) } rs, err := sqlite3conn.Query("select * from foo", nil) if err != nil { t.Fatal("Failed to select:", err) } defer rs.Close() declTypes := rs.(*SQLiteRows).DeclTypes() if !reflect.DeepEqual(declTypes, []string{"integer", "text"}) { t.Fatal("Unexpected declTypes:", declTypes) } } func TestPinger(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { t.Fatal(err) } err = db.Ping() if err != nil { t.Fatal(err) } db.Close() err = db.Ping() if err == nil { t.Fatal("Should be closed") } } func TestUpdateAndTransactionHooks(t *testing.T) { var events []string var commitHookReturn = 0 sql.Register("sqlite3_UpdateHook", &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { conn.RegisterCommitHook(func() int { events = append(events, "commit") return commitHookReturn }) conn.RegisterRollbackHook(func() { events = append(events, "rollback") }) conn.RegisterUpdateHook(func(op int, db string, table string, rowid int64) { events = append(events, fmt.Sprintf("update(op=%v db=%v table=%v rowid=%v)", op, db, table, rowid)) }) return nil }, }) db, err := sql.Open("sqlite3_UpdateHook", ":memory:") if err != nil { t.Fatal("Failed to open database:", err) } defer db.Close() statements := []string{ "create table foo (id integer primary key)", "insert into foo values (9)", "update foo set id = 99 where id = 9", "delete from foo where id = 99", } for _, statement := range statements { _, err = db.Exec(statement) if err != nil { t.Fatalf("Unable to prepare test data [%v]: %v", statement, err) } } commitHookReturn = 1 _, err = db.Exec("insert into foo values (5)") if err == nil { t.Error("Commit hook failed to rollback transaction") } var expected = []string{ "commit", fmt.Sprintf("update(op=%v db=main table=foo rowid=9)", SQLITE_INSERT), "commit", fmt.Sprintf("update(op=%v db=main table=foo rowid=99)", SQLITE_UPDATE), "commit", fmt.Sprintf("update(op=%v db=main table=foo rowid=99)", SQLITE_DELETE), "commit", fmt.Sprintf("update(op=%v db=main table=foo rowid=5)", SQLITE_INSERT), "commit", "rollback", } if !reflect.DeepEqual(events, expected) { t.Errorf("Expected notifications %v but got %v", expected, events) } } func TestNilAndEmptyBytes(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { t.Fatal(err) } defer db.Close() actualNil := []byte("use this to use an actual nil not a reference to nil") emptyBytes := []byte{} for tsti, tst := range []struct { name string columnType string insertBytes []byte expectedBytes []byte }{ {"actual nil blob", "blob", actualNil, nil}, {"referenced nil blob", "blob", nil, nil}, {"empty blob", "blob", emptyBytes, emptyBytes}, {"actual nil text", "text", actualNil, nil}, {"referenced nil text", "text", nil, nil}, {"empty text", "text", emptyBytes, emptyBytes}, } { if _, err = db.Exec(fmt.Sprintf("create table tbl%d (txt %s)", tsti, tst.columnType)); err != nil { t.Fatal(tst.name, err) } if bytes.Equal(tst.insertBytes, actualNil) { if _, err = db.Exec(fmt.Sprintf("insert into tbl%d (txt) values (?)", tsti), nil); err != nil { t.Fatal(tst.name, err) } } else { if _, err = db.Exec(fmt.Sprintf("insert into tbl%d (txt) values (?)", tsti), &tst.insertBytes); err != nil { t.Fatal(tst.name, err) } } rows, err := db.Query(fmt.Sprintf("select txt from tbl%d", tsti)) if err != nil { t.Fatal(tst.name, err) } if !rows.Next() { t.Fatal(tst.name, "no rows") } var scanBytes []byte if err = rows.Scan(&scanBytes); err != nil { t.Fatal(tst.name, err) } if err = rows.Err(); err != nil { t.Fatal(tst.name, err) } if tst.expectedBytes == nil && scanBytes != nil { t.Errorf("%s: %#v != %#v", tst.name, scanBytes, tst.expectedBytes) } else if !bytes.Equal(scanBytes, tst.expectedBytes) { t.Errorf("%s: %#v != %#v", tst.name, scanBytes, tst.expectedBytes) } } } var customFunctionOnce sync.Once func BenchmarkCustomFunctions(b *testing.B) { customFunctionOnce.Do(func() { customAdd := func(a, b int64) int64 { return a + b } sql.Register("sqlite3_BenchmarkCustomFunctions", &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { // Impure function to force sqlite to reexecute it each time. if err := conn.RegisterFunc("custom_add", customAdd, false); err != nil { return err } return nil }, }) }) db, err := sql.Open("sqlite3_BenchmarkCustomFunctions", ":memory:") if err != nil { b.Fatal("Failed to open database:", err) } defer db.Close() b.ResetTimer() for i := 0; i < b.N; i++ { var i int64 err = db.QueryRow("SELECT custom_add(1,2)").Scan(&i) if err != nil { b.Fatal("Failed to run custom add:", err) } } } func TestSuite(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) d, err := sql.Open("sqlite3", tempFilename+"?_busy_timeout=99999") if err != nil { t.Fatal(err) } defer d.Close() db = &TestDB{t, d, SQLITE, sync.Once{}} testing.RunTests(func(string, string) (bool, error) { return true, nil }, tests) if !testing.Short() { for _, b := range benchmarks { fmt.Printf("%-20s", b.Name) r := testing.Benchmark(b.F) fmt.Printf("%10d %10.0f req/s\n", r.N, float64(r.N)/r.T.Seconds()) } } db.tearDown() } // Dialect is a type of dialect of databases. type Dialect int // Dialects for databases. const ( SQLITE Dialect = iota // SQLITE mean SQLite3 dialect POSTGRESQL // POSTGRESQL mean PostgreSQL dialect MYSQL // MYSQL mean MySQL dialect ) // DB provide context for the tests type TestDB struct { *testing.T *sql.DB dialect Dialect once sync.Once } var db *TestDB // the following tables will be created and dropped during the test var testTables = []string{"foo", "bar", "t", "bench"} var tests = []testing.InternalTest{ {Name: "TestResult", F: testResult}, {Name: "TestBlobs", F: testBlobs}, {Name: "TestManyQueryRow", F: testManyQueryRow}, {Name: "TestTxQuery", F: testTxQuery}, {Name: "TestPreparedStmt", F: testPreparedStmt}, } var benchmarks = []testing.InternalBenchmark{ {Name: "BenchmarkExec", F: benchmarkExec}, {Name: "BenchmarkQuery", F: benchmarkQuery}, {Name: "BenchmarkParams", F: benchmarkParams}, {Name: "BenchmarkStmt", F: benchmarkStmt}, {Name: "BenchmarkRows", F: benchmarkRows}, {Name: "BenchmarkStmtRows", F: benchmarkStmtRows}, } func (db *TestDB) mustExec(sql string, args ...interface{}) sql.Result { res, err := db.Exec(sql, args...) if err != nil { db.Fatalf("Error running %q: %v", sql, err) } return res } func (db *TestDB) tearDown() { for _, tbl := range testTables { switch db.dialect { case SQLITE: db.mustExec("drop table if exists " + tbl) case MYSQL, POSTGRESQL: db.mustExec("drop table if exists " + tbl) default: db.Fatal("unknown dialect") } } } // q replaces ? parameters if needed func (db *TestDB) q(sql string) string { switch db.dialect { case POSTGRESQL: // repace with $1, $2, .. qrx := regexp.MustCompile(`\?`) n := 0 return qrx.ReplaceAllStringFunc(sql, func(string) string { n++ return "$" + strconv.Itoa(n) }) } return sql } func (db *TestDB) blobType(size int) string { switch db.dialect { case SQLITE: return fmt.Sprintf("blob[%d]", size) case POSTGRESQL: return "bytea" case MYSQL: return fmt.Sprintf("VARBINARY(%d)", size) } panic("unknown dialect") } func (db *TestDB) serialPK() string { switch db.dialect { case SQLITE: return "integer primary key autoincrement" case POSTGRESQL: return "serial primary key" case MYSQL: return "integer primary key auto_increment" } panic("unknown dialect") } func (db *TestDB) now() string { switch db.dialect { case SQLITE: return "datetime('now')" case POSTGRESQL: return "now()" case MYSQL: return "now()" } panic("unknown dialect") } func makeBench() { if _, err := db.Exec("create table bench (n varchar(32), i integer, d double, s varchar(32), t datetime)"); err != nil { panic(err) } st, err := db.Prepare("insert into bench values (?, ?, ?, ?, ?)") if err != nil { panic(err) } defer st.Close() for i := 0; i < 100; i++ { if _, err = st.Exec(nil, i, float64(i), fmt.Sprintf("%d", i), time.Now()); err != nil { panic(err) } } } // testResult is test for result func testResult(t *testing.T) { db.tearDown() db.mustExec("create temporary table test (id " + db.serialPK() + ", name varchar(10))") for i := 1; i < 3; i++ { r := db.mustExec(db.q("insert into test (name) values (?)"), fmt.Sprintf("row %d", i)) n, err := r.RowsAffected() if err != nil { t.Fatal(err) } if n != 1 { t.Errorf("got %v, want %v", n, 1) } n, err = r.LastInsertId() if err != nil { t.Fatal(err) } if n != int64(i) { t.Errorf("got %v, want %v", n, i) } } if _, err := db.Exec("error!"); err == nil { t.Fatalf("expected error") } } // testBlobs is test for blobs func testBlobs(t *testing.T) { db.tearDown() var blob = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} db.mustExec("create table foo (id integer primary key, bar " + db.blobType(16) + ")") db.mustExec(db.q("insert into foo (id, bar) values(?,?)"), 0, blob) want := fmt.Sprintf("%x", blob) b := make([]byte, 16) err := db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&b) got := fmt.Sprintf("%x", b) if err != nil { t.Errorf("[]byte scan: %v", err) } else if got != want { t.Errorf("for []byte, got %q; want %q", got, want) } err = db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&got) want = string(blob) if err != nil { t.Errorf("string scan: %v", err) } else if got != want { t.Errorf("for string, got %q; want %q", got, want) } } // testManyQueryRow is test for many query row func testManyQueryRow(t *testing.T) { if testing.Short() { t.Log("skipping in short mode") return } db.tearDown() db.mustExec("create table foo (id integer primary key, name varchar(50))") db.mustExec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob") var name string for i := 0; i < 10000; i++ { err := db.QueryRow(db.q("select name from foo where id = ?"), 1).Scan(&name) if err != nil || name != "bob" { t.Fatalf("on query %d: err=%v, name=%q", i, err, name) } } } // testTxQuery is test for transactional query func testTxQuery(t *testing.T) { db.tearDown() tx, err := db.Begin() if err != nil { t.Fatal(err) } defer tx.Rollback() _, err = tx.Exec("create table foo (id integer primary key, name varchar(50))") if err != nil { t.Fatal(err) } _, err = tx.Exec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob") if err != nil { t.Fatal(err) } r, err := tx.Query(db.q("select name from foo where id = ?"), 1) if err != nil { t.Fatal(err) } defer r.Close() if !r.Next() { if r.Err() != nil { t.Fatal(err) } t.Fatal("expected one rows") } var name string err = r.Scan(&name) if err != nil { t.Fatal(err) } } // testPreparedStmt is test for prepared statement func testPreparedStmt(t *testing.T) { db.tearDown() db.mustExec("CREATE TABLE t (count INT)") sel, err := db.Prepare("SELECT count FROM t ORDER BY count DESC") if err != nil { t.Fatalf("prepare 1: %v", err) } ins, err := db.Prepare(db.q("INSERT INTO t (count) VALUES (?)")) if err != nil { t.Fatalf("prepare 2: %v", err) } for n := 1; n <= 3; n++ { if _, err := ins.Exec(n); err != nil { t.Fatalf("insert(%d) = %v", n, err) } } const nRuns = 10 var wg sync.WaitGroup for i := 0; i < nRuns; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 10; j++ { count := 0 if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows { t.Errorf("Query: %v", err) return } if _, err := ins.Exec(rand.Intn(100)); err != nil { t.Errorf("Insert: %v", err) return } } }() } wg.Wait() } // Benchmarks need to use panic() since b.Error errors are lost when // running via testing.Benchmark() I would like to run these via go // test -bench but calling Benchmark() from a benchmark test // currently hangs go. // benchmarkExec is benchmark for exec func benchmarkExec(b *testing.B) { for i := 0; i < b.N; i++ { if _, err := db.Exec("select 1"); err != nil { panic(err) } } } // benchmarkQuery is benchmark for query func benchmarkQuery(b *testing.B) { for i := 0; i < b.N; i++ { var n sql.NullString var i int var f float64 var s string // var t time.Time if err := db.QueryRow("select null, 1, 1.1, 'foo'").Scan(&n, &i, &f, &s); err != nil { panic(err) } } } // benchmarkParams is benchmark for params func benchmarkParams(b *testing.B) { for i := 0; i < b.N; i++ { var n sql.NullString var i int var f float64 var s string // var t time.Time if err := db.QueryRow("select ?, ?, ?, ?", nil, 1, 1.1, "foo").Scan(&n, &i, &f, &s); err != nil { panic(err) } } } // benchmarkStmt is benchmark for statement func benchmarkStmt(b *testing.B) { st, err := db.Prepare("select ?, ?, ?, ?") if err != nil { panic(err) } defer st.Close() for n := 0; n < b.N; n++ { var n sql.NullString var i int var f float64 var s string // var t time.Time if err := st.QueryRow(nil, 1, 1.1, "foo").Scan(&n, &i, &f, &s); err != nil { panic(err) } } } // benchmarkRows is benchmark for rows func benchmarkRows(b *testing.B) { db.once.Do(makeBench) for n := 0; n < b.N; n++ { var n sql.NullString var i int var f float64 var s string var t time.Time r, err := db.Query("select * from bench") if err != nil { panic(err) } for r.Next() { if err = r.Scan(&n, &i, &f, &s, &t); err != nil { panic(err) } } if err = r.Err(); err != nil { panic(err) } } } // benchmarkStmtRows is benchmark for statement rows func benchmarkStmtRows(b *testing.B) { db.once.Do(makeBench) st, err := db.Prepare("select * from bench") if err != nil { panic(err) } defer st.Close() for n := 0; n < b.N; n++ { var n sql.NullString var i int var f float64 var s string var t time.Time r, err := st.Query() if err != nil { panic(err) } for r.Next() { if err = r.Scan(&n, &i, &f, &s, &t); err != nil { panic(err) } } if err = r.Err(); err != nil { panic(err) } } } go-sqlite3-1.4.0/sqlite3_trace.go000066400000000000000000000203171320252770200165770ustar00rootroot00000000000000// Copyright (C) 2016 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build trace package sqlite3 /* #ifndef USE_LIBSQLITE3 #include #else #include #endif #include int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x); */ import "C" import ( "fmt" "strings" "sync" "unsafe" ) // Trace... constants identify the possible events causing callback invocation. // Values are same as the corresponding SQLite Trace Event Codes. const ( TraceStmt = C.SQLITE_TRACE_STMT TraceProfile = C.SQLITE_TRACE_PROFILE TraceRow = C.SQLITE_TRACE_ROW TraceClose = C.SQLITE_TRACE_CLOSE ) type TraceInfo struct { // Pack together the shorter fields, to keep the struct smaller. // On a 64-bit machine there would be padding // between EventCode and ConnHandle; having AutoCommit here is "free": EventCode uint32 AutoCommit bool ConnHandle uintptr // Usually filled, unless EventCode = TraceClose = SQLITE_TRACE_CLOSE: // identifier for a prepared statement: StmtHandle uintptr // Two strings filled when EventCode = TraceStmt = SQLITE_TRACE_STMT: // (1) either the unexpanded SQL text of the prepared statement, or // an SQL comment that indicates the invocation of a trigger; // (2) expanded SQL, if requested and if (1) is not an SQL comment. StmtOrTrigger string ExpandedSQL string // only if requested (TraceConfig.WantExpandedSQL = true) // filled when EventCode = TraceProfile = SQLITE_TRACE_PROFILE: // estimated number of nanoseconds that the prepared statement took to run: RunTimeNanosec int64 DBError Error } // TraceUserCallback gives the signature for a trace function // provided by the user (Go application programmer). // SQLite 3.14 documentation (as of September 2, 2016) // for SQL Trace Hook = sqlite3_trace_v2(): // The integer return value from the callback is currently ignored, // though this may change in future releases. Callback implementations // should return zero to ensure future compatibility. type TraceUserCallback func(TraceInfo) int type TraceConfig struct { Callback TraceUserCallback EventMask C.uint WantExpandedSQL bool } func fillDBError(dbErr *Error, db *C.sqlite3) { // See SQLiteConn.lastError(), in file 'sqlite3.go' at the time of writing (Sept 5, 2016) dbErr.Code = ErrNo(C.sqlite3_errcode(db)) dbErr.ExtendedCode = ErrNoExtended(C.sqlite3_extended_errcode(db)) dbErr.err = C.GoString(C.sqlite3_errmsg(db)) } func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) { if pStmt == nil { panic("No SQLite statement pointer in P arg of trace_v2 callback") } expSQLiteCStr := C.sqlite3_expanded_sql((*C.sqlite3_stmt)(pStmt)) if expSQLiteCStr == nil { fillDBError(&info.DBError, db) return } info.ExpandedSQL = C.GoString(expSQLiteCStr) } //export traceCallbackTrampoline func traceCallbackTrampoline( traceEventCode C.uint, // Parameter named 'C' in SQLite docs = Context given at registration: ctx unsafe.Pointer, // Parameter named 'P' in SQLite docs (Primary event data?): p unsafe.Pointer, // Parameter named 'X' in SQLite docs (eXtra event data?): xValue unsafe.Pointer) C.int { if ctx == nil { panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode)) } contextDB := (*C.sqlite3)(ctx) connHandle := uintptr(ctx) var traceConf TraceConfig var found bool if traceEventCode == TraceClose { // clean up traceMap: 'pop' means get and delete traceConf, found = popTraceMapping(connHandle) } else { traceConf, found = lookupTraceMapping(connHandle) } if !found { panic(fmt.Sprintf("Mapping not found for handle 0x%x (ev 0x%x)", connHandle, traceEventCode)) } var info TraceInfo info.EventCode = uint32(traceEventCode) info.AutoCommit = (int(C.sqlite3_get_autocommit(contextDB)) != 0) info.ConnHandle = connHandle switch traceEventCode { case TraceStmt: info.StmtHandle = uintptr(p) var xStr string if xValue != nil { xStr = C.GoString((*C.char)(xValue)) } info.StmtOrTrigger = xStr if !strings.HasPrefix(xStr, "--") { // Not SQL comment, therefore the current event // is not related to a trigger. // The user might want to receive the expanded SQL; // let's check: if traceConf.WantExpandedSQL { fillExpandedSQL(&info, contextDB, p) } } case TraceProfile: info.StmtHandle = uintptr(p) if xValue == nil { panic("NULL pointer in X arg of trace_v2 callback for SQLITE_TRACE_PROFILE event") } info.RunTimeNanosec = *(*int64)(xValue) // sample the error //TODO: is it safe? is it useful? fillDBError(&info.DBError, contextDB) case TraceRow: info.StmtHandle = uintptr(p) case TraceClose: handle := uintptr(p) if handle != info.ConnHandle { panic(fmt.Sprintf("Different conn handle 0x%x (expected 0x%x) in SQLITE_TRACE_CLOSE event.", handle, info.ConnHandle)) } default: // Pass unsupported events to the user callback (if configured); // let the user callback decide whether to panic or ignore them. } // Do not execute user callback when the event was not requested by user! // Remember that the Close event is always selected when // registering this callback trampoline with SQLite --- for cleanup. // In the future there may be more events forced to "selected" in SQLite // for the driver's needs. if traceConf.EventMask&traceEventCode == 0 { return 0 } r := 0 if traceConf.Callback != nil { r = traceConf.Callback(info) } return C.int(r) } type traceMapEntry struct { config TraceConfig } var traceMapLock sync.Mutex var traceMap = make(map[uintptr]traceMapEntry) func addTraceMapping(connHandle uintptr, traceConf TraceConfig) { traceMapLock.Lock() defer traceMapLock.Unlock() oldEntryCopy, found := traceMap[connHandle] if found { panic(fmt.Sprintf("Adding trace config %v: handle 0x%x already registered (%v).", traceConf, connHandle, oldEntryCopy.config)) } traceMap[connHandle] = traceMapEntry{config: traceConf} fmt.Printf("Added trace config %v: handle 0x%x.\n", traceConf, connHandle) } func lookupTraceMapping(connHandle uintptr) (TraceConfig, bool) { traceMapLock.Lock() defer traceMapLock.Unlock() entryCopy, found := traceMap[connHandle] return entryCopy.config, found } // 'pop' = get and delete from map before returning the value to the caller func popTraceMapping(connHandle uintptr) (TraceConfig, bool) { traceMapLock.Lock() defer traceMapLock.Unlock() entryCopy, found := traceMap[connHandle] if found { delete(traceMap, connHandle) fmt.Printf("Pop handle 0x%x: deleted trace config %v.\n", connHandle, entryCopy.config) } return entryCopy.config, found } // SetTrace installs or removes the trace callback for the given database connection. // It's not named 'RegisterTrace' because only one callback can be kept and called. // Calling SetTrace a second time on same database connection // overrides (cancels) any prior callback and all its settings: // event mask, etc. func (c *SQLiteConn) SetTrace(requested *TraceConfig) error { connHandle := uintptr(unsafe.Pointer(c.db)) _, _ = popTraceMapping(connHandle) if requested == nil { // The traceMap entry was deleted already by popTraceMapping(): // can disable all events now, no need to watch for TraceClose. err := c.setSQLiteTrace(0) return err } reqCopy := *requested // Disable potentially expensive operations // if their result will not be used. We are doing this // just in case the caller provided nonsensical input. if reqCopy.EventMask&TraceStmt == 0 { reqCopy.WantExpandedSQL = false } addTraceMapping(connHandle, reqCopy) // The callback trampoline function does cleanup on Close event, // regardless of the presence or absence of the user callback. // Therefore it needs the Close event to be selected: actualEventMask := uint(reqCopy.EventMask | TraceClose) err := c.setSQLiteTrace(actualEventMask) return err } func (c *SQLiteConn) setSQLiteTrace(sqliteEventMask uint) error { rv := C.sqlite3_trace_v2(c.db, C.uint(sqliteEventMask), (*[0]byte)(unsafe.Pointer(C.traceCallbackTrampoline)), unsafe.Pointer(c.db)) // Fourth arg is same as first: we are // passing the database connection handle as callback context. if rv != C.SQLITE_OK { return c.lastError() } return nil } go-sqlite3-1.4.0/sqlite3_type.go000066400000000000000000000026261320252770200164650ustar00rootroot00000000000000package sqlite3 /* #ifndef USE_LIBSQLITE3 #include #else #include #endif */ import "C" import ( "reflect" "time" ) // ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName. func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) } /* func (rc *SQLiteRows) ColumnTypeLength(index int) (length int64, ok bool) { return 0, false } func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { return 0, 0, false } */ // ColumnTypeNullable implement RowsColumnTypeNullable. func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { return true, true } // ColumnTypeScanType implement RowsColumnTypeScanType. func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { switch C.sqlite3_column_type(rc.s.s, C.int(i)) { case C.SQLITE_INTEGER: switch C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) { case "timestamp", "datetime", "date": return reflect.TypeOf(time.Time{}) case "boolean": return reflect.TypeOf(false) } return reflect.TypeOf(int64(0)) case C.SQLITE_FLOAT: return reflect.TypeOf(float64(0)) case C.SQLITE_BLOB: return reflect.SliceOf(reflect.TypeOf(byte(0))) case C.SQLITE_NULL: return reflect.TypeOf(nil) case C.SQLITE_TEXT: return reflect.TypeOf("") } return reflect.SliceOf(reflect.TypeOf(byte(0))) } go-sqlite3-1.4.0/sqlite3_vtable.go000066400000000000000000000424271320252770200167640ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build vtable package sqlite3 /* #cgo CFLAGS: -std=gnu99 #cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE #cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61 #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 #cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA=1 #cgo CFLAGS: -Wno-deprecated-declarations #ifndef USE_LIBSQLITE3 #include #else #include #endif #include #include #include static inline char *_sqlite3_mprintf(char *zFormat, char *arg) { return sqlite3_mprintf(zFormat, arg); } typedef struct goVTab goVTab; struct goVTab { sqlite3_vtab base; void *vTab; }; uintptr_t goMInit(void *db, void *pAux, int argc, char **argv, char **pzErr, int isCreate); static int cXInit(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr, int isCreate) { void *vTab = (void *)goMInit(db, pAux, argc, (char**)argv, pzErr, isCreate); if (!vTab || *pzErr) { return SQLITE_ERROR; } goVTab *pvTab = (goVTab *)sqlite3_malloc(sizeof(goVTab)); if (!pvTab) { *pzErr = sqlite3_mprintf("%s", "Out of memory"); return SQLITE_NOMEM; } memset(pvTab, 0, sizeof(goVTab)); pvTab->vTab = vTab; *ppVTab = (sqlite3_vtab *)pvTab; *pzErr = 0; return SQLITE_OK; } static inline int cXCreate(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 1); } static inline int cXConnect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 0); } char* goVBestIndex(void *pVTab, void *icp); static inline int cXBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *info) { char *pzErr = goVBestIndex(((goVTab*)pVTab)->vTab, info); if (pzErr) { if (pVTab->zErrMsg) sqlite3_free(pVTab->zErrMsg); pVTab->zErrMsg = pzErr; return SQLITE_ERROR; } return SQLITE_OK; } char* goVRelease(void *pVTab, int isDestroy); static int cXRelease(sqlite3_vtab *pVTab, int isDestroy) { char *pzErr = goVRelease(((goVTab*)pVTab)->vTab, isDestroy); if (pzErr) { if (pVTab->zErrMsg) sqlite3_free(pVTab->zErrMsg); pVTab->zErrMsg = pzErr; return SQLITE_ERROR; } if (pVTab->zErrMsg) sqlite3_free(pVTab->zErrMsg); sqlite3_free(pVTab); return SQLITE_OK; } static inline int cXDisconnect(sqlite3_vtab *pVTab) { return cXRelease(pVTab, 0); } static inline int cXDestroy(sqlite3_vtab *pVTab) { return cXRelease(pVTab, 1); } typedef struct goVTabCursor goVTabCursor; struct goVTabCursor { sqlite3_vtab_cursor base; void *vTabCursor; }; uintptr_t goVOpen(void *pVTab, char **pzErr); static int cXOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { void *vTabCursor = (void *)goVOpen(((goVTab*)pVTab)->vTab, &(pVTab->zErrMsg)); goVTabCursor *pCursor = (goVTabCursor *)sqlite3_malloc(sizeof(goVTabCursor)); if (!pCursor) { return SQLITE_NOMEM; } memset(pCursor, 0, sizeof(goVTabCursor)); pCursor->vTabCursor = vTabCursor; *ppCursor = (sqlite3_vtab_cursor *)pCursor; return SQLITE_OK; } static int setErrMsg(sqlite3_vtab_cursor *pCursor, char *pzErr) { if (pCursor->pVtab->zErrMsg) sqlite3_free(pCursor->pVtab->zErrMsg); pCursor->pVtab->zErrMsg = pzErr; return SQLITE_ERROR; } char* goVClose(void *pCursor); static int cXClose(sqlite3_vtab_cursor *pCursor) { char *pzErr = goVClose(((goVTabCursor*)pCursor)->vTabCursor); if (pzErr) { return setErrMsg(pCursor, pzErr); } sqlite3_free(pCursor); return SQLITE_OK; } char* goVFilter(void *pCursor, int idxNum, char* idxName, int argc, sqlite3_value **argv); static int cXFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) { char *pzErr = goVFilter(((goVTabCursor*)pCursor)->vTabCursor, idxNum, (char*)idxStr, argc, argv); if (pzErr) { return setErrMsg(pCursor, pzErr); } return SQLITE_OK; } char* goVNext(void *pCursor); static int cXNext(sqlite3_vtab_cursor *pCursor) { char *pzErr = goVNext(((goVTabCursor*)pCursor)->vTabCursor); if (pzErr) { return setErrMsg(pCursor, pzErr); } return SQLITE_OK; } int goVEof(void *pCursor); static inline int cXEof(sqlite3_vtab_cursor *pCursor) { return goVEof(((goVTabCursor*)pCursor)->vTabCursor); } char* goVColumn(void *pCursor, void *cp, int col); static int cXColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i) { char *pzErr = goVColumn(((goVTabCursor*)pCursor)->vTabCursor, ctx, i); if (pzErr) { return setErrMsg(pCursor, pzErr); } return SQLITE_OK; } char* goVRowid(void *pCursor, sqlite3_int64 *pRowid); static int cXRowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) { char *pzErr = goVRowid(((goVTabCursor*)pCursor)->vTabCursor, pRowid); if (pzErr) { return setErrMsg(pCursor, pzErr); } return SQLITE_OK; } char* goVUpdate(void *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid); static int cXUpdate(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) { char *pzErr = goVUpdate(((goVTab*)pVTab)->vTab, argc, argv, pRowid); if (pzErr) { if (pVTab->zErrMsg) sqlite3_free(pVTab->zErrMsg); pVTab->zErrMsg = pzErr; return SQLITE_ERROR; } return SQLITE_OK; } static sqlite3_module goModule = { 0, // iVersion cXCreate, // xCreate - create a table cXConnect, // xConnect - connect to an existing table cXBestIndex, // xBestIndex - Determine search strategy cXDisconnect, // xDisconnect - Disconnect from a table cXDestroy, // xDestroy - Drop a table cXOpen, // xOpen - open a cursor cXClose, // xClose - close a cursor cXFilter, // xFilter - configure scan constraints cXNext, // xNext - advance a cursor cXEof, // xEof cXColumn, // xColumn - read data cXRowid, // xRowid - read data cXUpdate, // xUpdate - write data // Not implemented 0, // xBegin - begin transaction 0, // xSync - sync transaction 0, // xCommit - commit transaction 0, // xRollback - rollback transaction 0, // xFindFunction - function overloading 0, // xRename - rename the table 0, // xSavepoint 0, // xRelease 0 // xRollbackTo }; void goMDestroy(void*); static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) { return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy); } */ import "C" import ( "fmt" "math" "reflect" "unsafe" ) type sqliteModule struct { c *SQLiteConn name string module Module } type sqliteVTab struct { module *sqliteModule vTab VTab } type sqliteVTabCursor struct { vTab *sqliteVTab vTabCursor VTabCursor } // Op is type of operations. type Op uint8 // Op mean identity of operations. const ( OpEQ Op = 2 OpGT = 4 OpLE = 8 OpLT = 16 OpGE = 32 OpMATCH = 64 OpLIKE = 65 /* 3.10.0 and later only */ OpGLOB = 66 /* 3.10.0 and later only */ OpREGEXP = 67 /* 3.10.0 and later only */ OpScanUnique = 1 /* Scan visits at most 1 row */ ) // InfoConstraint give information of constraint. type InfoConstraint struct { Column int Op Op Usable bool } // InfoOrderBy give information of order-by. type InfoOrderBy struct { Column int Desc bool } func constraints(info *C.sqlite3_index_info) []InfoConstraint { l := info.nConstraint slice := (*[1 << 30]C.struct_sqlite3_index_constraint)(unsafe.Pointer(info.aConstraint))[:l:l] cst := make([]InfoConstraint, 0, l) for _, c := range slice { var usable bool if c.usable > 0 { usable = true } cst = append(cst, InfoConstraint{ Column: int(c.iColumn), Op: Op(c.op), Usable: usable, }) } return cst } func orderBys(info *C.sqlite3_index_info) []InfoOrderBy { l := info.nOrderBy slice := (*[1 << 30]C.struct_sqlite3_index_orderby)(unsafe.Pointer(info.aOrderBy))[:l:l] ob := make([]InfoOrderBy, 0, l) for _, c := range slice { var desc bool if c.desc > 0 { desc = true } ob = append(ob, InfoOrderBy{ Column: int(c.iColumn), Desc: desc, }) } return ob } // IndexResult is a Go struct representation of what eventually ends up in the // output fields for `sqlite3_index_info` // See: https://www.sqlite.org/c3ref/index_info.html type IndexResult struct { Used []bool // aConstraintUsage IdxNum int IdxStr string AlreadyOrdered bool // orderByConsumed EstimatedCost float64 EstimatedRows float64 } // mPrintf is a utility wrapper around sqlite3_mprintf func mPrintf(format, arg string) *C.char { cf := C.CString(format) defer C.free(unsafe.Pointer(cf)) ca := C.CString(arg) defer C.free(unsafe.Pointer(ca)) return C._sqlite3_mprintf(cf, ca) } //export goMInit func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t { m := lookupHandle(uintptr(pClientData)).(*sqliteModule) if m.c.db != (*C.sqlite3)(db) { *pzErr = mPrintf("%s", "Inconsistent db handles") return 0 } args := make([]string, argc) var A []*C.char slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)} a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface() for i, s := range a.([]*C.char) { args[i] = C.GoString(s) } var vTab VTab var err error if isCreate == 1 { vTab, err = m.module.Create(m.c, args) } else { vTab, err = m.module.Connect(m.c, args) } if err != nil { *pzErr = mPrintf("%s", err.Error()) return 0 } vt := sqliteVTab{m, vTab} *pzErr = nil return C.uintptr_t(newHandle(m.c, &vt)) } //export goVRelease func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char { vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) var err error if isDestroy == 1 { err = vt.vTab.Destroy() } else { err = vt.vTab.Disconnect() } if err != nil { return mPrintf("%s", err.Error()) } return nil } //export goVOpen func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t { vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) vTabCursor, err := vt.vTab.Open() if err != nil { *pzErr = mPrintf("%s", err.Error()) return 0 } vtc := sqliteVTabCursor{vt, vTabCursor} *pzErr = nil return C.uintptr_t(newHandle(vt.module.c, &vtc)) } //export goVBestIndex func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) info := (*C.sqlite3_index_info)(icp) csts := constraints(info) res, err := vt.vTab.BestIndex(csts, orderBys(info)) if err != nil { return mPrintf("%s", err.Error()) } if len(res.Used) != len(csts) { return mPrintf("Result.Used != expected value", "") } // Get a pointer to constraint_usage struct so we can update in place. l := info.nConstraint s := (*[1 << 30]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(info.aConstraintUsage))[:l:l] index := 1 for i := C.int(0); i < info.nConstraint; i++ { if res.Used[i] { s[i].argvIndex = C.int(index) s[i].omit = C.uchar(1) index++ } } info.idxNum = C.int(res.IdxNum) idxStr := C.CString(res.IdxStr) defer C.free(unsafe.Pointer(idxStr)) info.idxStr = idxStr info.needToFreeIdxStr = C.int(0) if res.AlreadyOrdered { info.orderByConsumed = C.int(1) } info.estimatedCost = C.double(res.EstimatedCost) info.estimatedRows = C.sqlite3_int64(res.EstimatedRows) return nil } //export goVClose func goVClose(pCursor unsafe.Pointer) *C.char { vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) err := vtc.vTabCursor.Close() if err != nil { return mPrintf("%s", err.Error()) } return nil } //export goMDestroy func goMDestroy(pClientData unsafe.Pointer) { m := lookupHandle(uintptr(pClientData)).(*sqliteModule) m.module.DestroyModule() } //export goVFilter func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char { vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] vals := make([]interface{}, 0, argc) for _, v := range args { conv, err := callbackArgGeneric(v) if err != nil { return mPrintf("%s", err.Error()) } vals = append(vals, conv.Interface()) } err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals) if err != nil { return mPrintf("%s", err.Error()) } return nil } //export goVNext func goVNext(pCursor unsafe.Pointer) *C.char { vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) err := vtc.vTabCursor.Next() if err != nil { return mPrintf("%s", err.Error()) } return nil } //export goVEof func goVEof(pCursor unsafe.Pointer) C.int { vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) err := vtc.vTabCursor.EOF() if err { return 1 } return 0 } //export goVColumn func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char { vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) c := (*SQLiteContext)(cp) err := vtc.vTabCursor.Column(c, int(col)) if err != nil { return mPrintf("%s", err.Error()) } return nil } //export goVRowid func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char { vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) rowid, err := vtc.vTabCursor.Rowid() if err != nil { return mPrintf("%s", err.Error()) } *pRowid = C.sqlite3_int64(rowid) return nil } //export goVUpdate func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char { vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) var tname string if n, ok := vt.vTab.(interface { TableName() string }); ok { tname = n.TableName() + " " } err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname) if v, ok := vt.vTab.(VTabUpdater); ok { // convert argv args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] vals := make([]interface{}, 0, argc) for _, v := range args { conv, err := callbackArgGeneric(v) if err != nil { return mPrintf("%s", err.Error()) } // work around for SQLITE_NULL x := conv.Interface() if z, ok := x.([]byte); ok && z == nil { x = nil } vals = append(vals, x) } switch { case argc == 1: err = v.Delete(vals[0]) case argc > 1 && vals[0] == nil: var id int64 id, err = v.Insert(vals[1], vals[2:]) if err == nil { *pRowid = C.sqlite3_int64(id) } case argc > 1: err = v.Update(vals[1], vals[2:]) } } if err != nil { return mPrintf("%s", err.Error()) } return nil } // Module is a "virtual table module", it defines the implementation of a // virtual tables. See: http://sqlite.org/c3ref/module.html type Module interface { // http://sqlite.org/vtab.html#xcreate Create(c *SQLiteConn, args []string) (VTab, error) // http://sqlite.org/vtab.html#xconnect Connect(c *SQLiteConn, args []string) (VTab, error) // http://sqlite.org/c3ref/create_module.html DestroyModule() } // VTab describes a particular instance of the virtual table. // See: http://sqlite.org/c3ref/vtab.html type VTab interface { // http://sqlite.org/vtab.html#xbestindex BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error) // http://sqlite.org/vtab.html#xdisconnect Disconnect() error // http://sqlite.org/vtab.html#sqlite3_module.xDestroy Destroy() error // http://sqlite.org/vtab.html#xopen Open() (VTabCursor, error) } // VTabUpdater is a type that allows a VTab to be inserted, updated, or // deleted. // See: https://sqlite.org/vtab.html#xupdate type VTabUpdater interface { Delete(interface{}) error Insert(interface{}, []interface{}) (int64, error) Update(interface{}, []interface{}) error } // VTabCursor describes cursors that point into the virtual table and are used // to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html type VTabCursor interface { // http://sqlite.org/vtab.html#xclose Close() error // http://sqlite.org/vtab.html#xfilter Filter(idxNum int, idxStr string, vals []interface{}) error // http://sqlite.org/vtab.html#xnext Next() error // http://sqlite.org/vtab.html#xeof EOF() bool // http://sqlite.org/vtab.html#xcolumn Column(c *SQLiteContext, col int) error // http://sqlite.org/vtab.html#xrowid Rowid() (int64, error) } // DeclareVTab declares the Schema of a virtual table. // See: http://sqlite.org/c3ref/declare_vtab.html func (c *SQLiteConn) DeclareVTab(sql string) error { zSQL := C.CString(sql) defer C.free(unsafe.Pointer(zSQL)) rv := C.sqlite3_declare_vtab(c.db, zSQL) if rv != C.SQLITE_OK { return c.lastError() } return nil } // CreateModule registers a virtual table implementation. // See: http://sqlite.org/c3ref/create_module.html func (c *SQLiteConn) CreateModule(moduleName string, module Module) error { mname := C.CString(moduleName) defer C.free(unsafe.Pointer(mname)) udm := sqliteModule{c, moduleName, module} rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(newHandle(c, &udm))) if rv != C.SQLITE_OK { return c.lastError() } return nil } go-sqlite3-1.4.0/sqlite3_vtable_test.go000066400000000000000000000256601320252770200200230ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build vtable package sqlite3 import ( "database/sql" "errors" "fmt" "os" "reflect" "strings" "testing" ) type testModule struct { t *testing.T intarray []int } type testVTab struct { intarray []int } type testVTabCursor struct { vTab *testVTab index int } func (m testModule) Create(c *SQLiteConn, args []string) (VTab, error) { if len(args) != 6 { m.t.Fatal("six arguments expected") } if args[0] != "test" { m.t.Fatal("module name") } if args[1] != "main" { m.t.Fatal("db name") } if args[2] != "vtab" { m.t.Fatal("table name") } if args[3] != "'1'" { m.t.Fatal("first arg") } if args[4] != "2" { m.t.Fatal("second arg") } if args[5] != "three" { m.t.Fatal("third argsecond arg") } err := c.DeclareVTab("CREATE TABLE x(test TEXT)") if err != nil { return nil, err } return &testVTab{m.intarray}, nil } func (m testModule) Connect(c *SQLiteConn, args []string) (VTab, error) { return m.Create(c, args) } func (m testModule) DestroyModule() {} func (v *testVTab) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) { used := make([]bool, 0, len(cst)) for range cst { used = append(used, false) } return &IndexResult{ Used: used, IdxNum: 0, IdxStr: "test-index", AlreadyOrdered: true, EstimatedCost: 100, EstimatedRows: 200, }, nil } func (v *testVTab) Disconnect() error { return nil } func (v *testVTab) Destroy() error { return nil } func (v *testVTab) Open() (VTabCursor, error) { return &testVTabCursor{v, 0}, nil } func (vc *testVTabCursor) Close() error { return nil } func (vc *testVTabCursor) Filter(idxNum int, idxStr string, vals []interface{}) error { vc.index = 0 return nil } func (vc *testVTabCursor) Next() error { vc.index++ return nil } func (vc *testVTabCursor) EOF() bool { return vc.index >= len(vc.vTab.intarray) } func (vc *testVTabCursor) Column(c *SQLiteContext, col int) error { if col != 0 { return fmt.Errorf("column index out of bounds: %d", col) } c.ResultInt(vc.vTab.intarray[vc.index]) return nil } func (vc *testVTabCursor) Rowid() (int64, error) { return int64(vc.index), nil } func TestCreateModule(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) intarray := []int{1, 2, 3} sql.Register("sqlite3_TestCreateModule", &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { return conn.CreateModule("test", testModule{t, intarray}) }, }) db, err := sql.Open("sqlite3_TestCreateModule", tempFilename) if err != nil { t.Fatalf("could not open db: %v", err) } _, err = db.Exec("CREATE VIRTUAL TABLE vtab USING test('1', 2, three)") if err != nil { t.Fatalf("could not create vtable: %v", err) } var i, value int rows, err := db.Query("SELECT rowid, * FROM vtab WHERE test = '3'") if err != nil { t.Fatalf("couldn't select from virtual table: %v", err) } for rows.Next() { rows.Scan(&i, &value) if intarray[i] != value { t.Fatalf("want %v but %v", intarray[i], value) } } _, err = db.Exec("DROP TABLE vtab") if err != nil { t.Fatalf("couldn't drop virtual table: %v", err) } } func TestVUpdate(t *testing.T) { tempFilename := TempFilename(t) defer os.Remove(tempFilename) // create module updateMod := &vtabUpdateModule{t, make(map[string]*vtabUpdateTable)} // register module sql.Register("sqlite3_TestVUpdate", &SQLiteDriver{ ConnectHook: func(conn *SQLiteConn) error { return conn.CreateModule("updatetest", updateMod) }, }) // connect db, err := sql.Open("sqlite3_TestVUpdate", tempFilename) if err != nil { t.Fatalf("could not open db: %v", err) } // create test table _, err = db.Exec(`CREATE VIRTUAL TABLE vt USING updatetest(f1 integer, f2 text, f3 text)`) if err != nil { t.Fatalf("could not create updatetest vtable vt, got: %v", err) } // check that table is defined properly if len(updateMod.tables) != 1 { t.Fatalf("expected exactly 1 table to exist, got: %d", len(updateMod.tables)) } if _, ok := updateMod.tables["vt"]; !ok { t.Fatalf("expected table `vt` to exist in tables") } // check nothing in updatetest rows, err := db.Query(`select * from vt`) if err != nil { t.Fatalf("could not query vt, got: %v", err) } i, err := getRowCount(rows) if err != nil { t.Fatalf("expected no error, got: %v", err) } if i != 0 { t.Fatalf("expected no rows in vt, got: %d", i) } _, err = db.Exec(`delete from vt where f1 = 'yes'`) if err != nil { t.Fatalf("expected error on delete, got nil") } // test bad column name _, err = db.Exec(`insert into vt (f4) values('a')`) if err == nil { t.Fatalf("expected error on insert, got nil") } // insert to vt res, err := db.Exec(`insert into vt (f1, f2, f3) values (115, 'b', 'c'), (116, 'd', 'e')`) if err != nil { t.Fatalf("expected no error on insert, got: %v", err) } n, err := res.RowsAffected() if err != nil { t.Fatalf("expected no error, got: %v", err) } if n != 2 { t.Fatalf("expected 1 row affected, got: %d", n) } // check vt table vt := updateMod.tables["vt"] if len(vt.data) != 2 { t.Fatalf("expected table vt to have exactly 2 rows, got: %d", len(vt.data)) } if !reflect.DeepEqual(vt.data[0], []interface{}{int64(115), "b", "c"}) { t.Fatalf("expected table vt entry 0 to be [115 b c], instead: %v", vt.data[0]) } if !reflect.DeepEqual(vt.data[1], []interface{}{int64(116), "d", "e"}) { t.Fatalf("expected table vt entry 1 to be [116 d e], instead: %v", vt.data[1]) } // query vt var f1 int var f2, f3 string err = db.QueryRow(`select * from vt where f1 = 115`).Scan(&f1, &f2, &f3) if err != nil { t.Fatalf("expected no error on vt query, got: %v", err) } // check column values if f1 != 115 || f2 != "b" || f3 != "c" { t.Errorf("expected f1==115, f2==b, f3==c, got: %d, %q, %q", f1, f2, f3) } // update vt res, err = db.Exec(`update vt set f1=117, f2='f' where f3='e'`) if err != nil { t.Fatalf("expected no error, got: %v", err) } n, err = res.RowsAffected() if err != nil { t.Fatalf("expected no error, got: %v", err) } if n != 1 { t.Fatalf("expected exactly one row updated, got: %d", n) } // check vt table if len(vt.data) != 2 { t.Fatalf("expected table vt to have exactly 2 rows, got: %d", len(vt.data)) } if !reflect.DeepEqual(vt.data[0], []interface{}{int64(115), "b", "c"}) { t.Fatalf("expected table vt entry 0 to be [115 b c], instead: %v", vt.data[0]) } if !reflect.DeepEqual(vt.data[1], []interface{}{int64(117), "f", "e"}) { t.Fatalf("expected table vt entry 1 to be [117 f e], instead: %v", vt.data[1]) } // delete from vt res, err = db.Exec(`delete from vt where f1 = 117`) if err != nil { t.Fatalf("expected no error, got: %v", err) } n, err = res.RowsAffected() if err != nil { t.Fatalf("expected no error, got: %v", err) } if n != 1 { t.Fatalf("expected exactly one row deleted, got: %d", n) } // check vt table if len(vt.data) != 1 { t.Fatalf("expected table vt to have exactly 1 row, got: %d", len(vt.data)) } if !reflect.DeepEqual(vt.data[0], []interface{}{int64(115), "b", "c"}) { t.Fatalf("expected table vt entry 0 to be [115 b c], instead: %v", vt.data[0]) } // check updatetest has 1 result rows, err = db.Query(`select * from vt`) if err != nil { t.Fatalf("could not query vt, got: %v", err) } i, err = getRowCount(rows) if err != nil { t.Fatalf("expected no error, got: %v", err) } if i != 1 { t.Fatalf("expected 1 row in vt, got: %d", i) } } func getRowCount(rows *sql.Rows) (int, error) { var i int for rows.Next() { i++ } return i, nil } type vtabUpdateModule struct { t *testing.T tables map[string]*vtabUpdateTable } func (m *vtabUpdateModule) Create(c *SQLiteConn, args []string) (VTab, error) { if len(args) < 2 { return nil, errors.New("must declare at least one column") } // get database name, table name, and column declarations ... dbname, tname, decls := args[1], args[2], args[3:] // extract column names + types from parameters declarations cols, typs := make([]string, len(decls)), make([]string, len(decls)) for i := 0; i < len(decls); i++ { n, typ := decls[i], "" if j := strings.IndexAny(n, " \t\n"); j != -1 { typ, n = strings.TrimSpace(n[j+1:]), n[:j] } cols[i], typs[i] = n, typ } // declare table err := c.DeclareVTab(fmt.Sprintf(`CREATE TABLE "%s"."%s" (%s)`, dbname, tname, strings.Join(decls, ","))) if err != nil { return nil, err } // create table vtab := &vtabUpdateTable{m.t, dbname, tname, cols, typs, make([][]interface{}, 0)} m.tables[tname] = vtab return vtab, nil } func (m *vtabUpdateModule) Connect(c *SQLiteConn, args []string) (VTab, error) { return m.Create(c, args) } func (m *vtabUpdateModule) DestroyModule() {} type vtabUpdateTable struct { t *testing.T db string name string cols []string typs []string data [][]interface{} } func (t *vtabUpdateTable) Open() (VTabCursor, error) { return &vtabUpdateCursor{t, 0}, nil } func (t *vtabUpdateTable) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) { return &IndexResult{Used: make([]bool, len(cst))}, nil } func (t *vtabUpdateTable) Disconnect() error { return nil } func (t *vtabUpdateTable) Destroy() error { return nil } func (t *vtabUpdateTable) Insert(id interface{}, vals []interface{}) (int64, error) { var i int64 if id == nil { i, t.data = int64(len(t.data)), append(t.data, vals) return i, nil } var ok bool i, ok = id.(int64) if !ok { return 0, fmt.Errorf("id is invalid type: %T", id) } t.data[i] = vals return i, nil } func (t *vtabUpdateTable) Update(id interface{}, vals []interface{}) error { i, ok := id.(int64) if !ok { return fmt.Errorf("id is invalid type: %T", id) } if int(i) >= len(t.data) || i < 0 { return fmt.Errorf("invalid row id %d", i) } t.data[int(i)] = vals return nil } func (t *vtabUpdateTable) Delete(id interface{}) error { i, ok := id.(int64) if !ok { return fmt.Errorf("id is invalid type: %T", id) } if int(i) >= len(t.data) || i < 0 { return fmt.Errorf("invalid row id %d", i) } t.data = append(t.data[:i], t.data[i+1:]...) return nil } type vtabUpdateCursor struct { t *vtabUpdateTable i int } func (c *vtabUpdateCursor) Column(ctxt *SQLiteContext, col int) error { switch x := c.t.data[c.i][col].(type) { case []byte: ctxt.ResultBlob(x) case bool: ctxt.ResultBool(x) case float64: ctxt.ResultDouble(x) case int: ctxt.ResultInt(x) case int64: ctxt.ResultInt64(x) case nil: ctxt.ResultNull() case string: ctxt.ResultText(x) default: ctxt.ResultText(fmt.Sprintf("%v", x)) } return nil } func (c *vtabUpdateCursor) Filter(ixNum int, ixName string, vals []interface{}) error { return nil } func (c *vtabUpdateCursor) Next() error { c.i++ return nil } func (c *vtabUpdateCursor) EOF() bool { return c.i >= len(c.t.data) } func (c *vtabUpdateCursor) Rowid() (int64, error) { return int64(c.i), nil } func (c *vtabUpdateCursor) Close() error { return nil } go-sqlite3-1.4.0/sqlite3_windows.go000066400000000000000000000005731320252770200171750ustar00rootroot00000000000000// Copyright (C) 2014 Yasuhiro Matsumoto . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build windows package sqlite3 /* #cgo CFLAGS: -I. -fno-stack-check -fno-stack-protector -mno-stack-arg-probe #cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T #cgo LDFLAGS: -lmingwex -lmingw32 */ import "C" go-sqlite3-1.4.0/sqlite3ext.h000066400000000000000000000727131320252770200157730ustar00rootroot00000000000000#ifndef USE_LIBSQLITE3 /* ** 2006 June 7 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the SQLite interface for use by ** shared libraries that want to be imported as extensions into ** an SQLite instance. Shared libraries that intend to be loaded ** as extensions by SQLite should #include this file instead of ** sqlite3.h. */ #ifndef SQLITE3EXT_H #define SQLITE3EXT_H #include "sqlite3.h" /* ** The following structure holds pointers to all of the SQLite API ** routines. ** ** WARNING: In order to maintain backwards compatibility, add new ** interfaces to the end of this structure only. If you insert new ** interfaces in the middle of this structure, then older different ** versions of SQLite will not be able to load each other's shared ** libraries! */ struct sqlite3_api_routines { void * (*aggregate_context)(sqlite3_context*,int nBytes); int (*aggregate_count)(sqlite3_context*); int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*)); int (*bind_double)(sqlite3_stmt*,int,double); int (*bind_int)(sqlite3_stmt*,int,int); int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64); int (*bind_null)(sqlite3_stmt*,int); int (*bind_parameter_count)(sqlite3_stmt*); int (*bind_parameter_index)(sqlite3_stmt*,const char*zName); const char * (*bind_parameter_name)(sqlite3_stmt*,int); int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*)); int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*)); int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); int (*busy_timeout)(sqlite3*,int ms); int (*changes)(sqlite3*); int (*close)(sqlite3*); int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, int eTextRep,const char*)); int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, int eTextRep,const void*)); const void * (*column_blob)(sqlite3_stmt*,int iCol); int (*column_bytes)(sqlite3_stmt*,int iCol); int (*column_bytes16)(sqlite3_stmt*,int iCol); int (*column_count)(sqlite3_stmt*pStmt); const char * (*column_database_name)(sqlite3_stmt*,int); const void * (*column_database_name16)(sqlite3_stmt*,int); const char * (*column_decltype)(sqlite3_stmt*,int i); const void * (*column_decltype16)(sqlite3_stmt*,int); double (*column_double)(sqlite3_stmt*,int iCol); int (*column_int)(sqlite3_stmt*,int iCol); sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol); const char * (*column_name)(sqlite3_stmt*,int); const void * (*column_name16)(sqlite3_stmt*,int); const char * (*column_origin_name)(sqlite3_stmt*,int); const void * (*column_origin_name16)(sqlite3_stmt*,int); const char * (*column_table_name)(sqlite3_stmt*,int); const void * (*column_table_name16)(sqlite3_stmt*,int); const unsigned char * (*column_text)(sqlite3_stmt*,int iCol); const void * (*column_text16)(sqlite3_stmt*,int iCol); int (*column_type)(sqlite3_stmt*,int iCol); sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); void * (*commit_hook)(sqlite3*,int(*)(void*),void*); int (*complete)(const char*sql); int (*complete16)(const void*sql); int (*create_collation)(sqlite3*,const char*,int,void*, int(*)(void*,int,const void*,int,const void*)); int (*create_collation16)(sqlite3*,const void*,int,void*, int(*)(void*,int,const void*,int,const void*)); int (*create_function)(sqlite3*,const char*,int,int,void*, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*)); int (*create_function16)(sqlite3*,const void*,int,int,void*, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*)); int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); int (*data_count)(sqlite3_stmt*pStmt); sqlite3 * (*db_handle)(sqlite3_stmt*); int (*declare_vtab)(sqlite3*,const char*); int (*enable_shared_cache)(int); int (*errcode)(sqlite3*db); const char * (*errmsg)(sqlite3*); const void * (*errmsg16)(sqlite3*); int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**); int (*expired)(sqlite3_stmt*); int (*finalize)(sqlite3_stmt*pStmt); void (*free)(void*); void (*free_table)(char**result); int (*get_autocommit)(sqlite3*); void * (*get_auxdata)(sqlite3_context*,int); int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**); int (*global_recover)(void); void (*interruptx)(sqlite3*); sqlite_int64 (*last_insert_rowid)(sqlite3*); const char * (*libversion)(void); int (*libversion_number)(void); void *(*malloc)(int); char * (*mprintf)(const char*,...); int (*open)(const char*,sqlite3**); int (*open16)(const void*,sqlite3**); int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*); void (*progress_handler)(sqlite3*,int,int(*)(void*),void*); void *(*realloc)(void*,int); int (*reset)(sqlite3_stmt*pStmt); void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_double)(sqlite3_context*,double); void (*result_error)(sqlite3_context*,const char*,int); void (*result_error16)(sqlite3_context*,const void*,int); void (*result_int)(sqlite3_context*,int); void (*result_int64)(sqlite3_context*,sqlite_int64); void (*result_null)(sqlite3_context*); void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*)); void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); void (*result_value)(sqlite3_context*,sqlite3_value*); void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, const char*,const char*),void*); void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); char * (*snprintf)(int,char*,const char*,...); int (*step)(sqlite3_stmt*); int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, char const**,char const**,int*,int*,int*); void (*thread_cleanup)(void); int (*total_changes)(sqlite3*); void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, sqlite_int64),void*); void * (*user_data)(sqlite3_context*); const void * (*value_blob)(sqlite3_value*); int (*value_bytes)(sqlite3_value*); int (*value_bytes16)(sqlite3_value*); double (*value_double)(sqlite3_value*); int (*value_int)(sqlite3_value*); sqlite_int64 (*value_int64)(sqlite3_value*); int (*value_numeric_type)(sqlite3_value*); const unsigned char * (*value_text)(sqlite3_value*); const void * (*value_text16)(sqlite3_value*); const void * (*value_text16be)(sqlite3_value*); const void * (*value_text16le)(sqlite3_value*); int (*value_type)(sqlite3_value*); char *(*vmprintf)(const char*,va_list); /* Added ??? */ int (*overload_function)(sqlite3*, const char *zFuncName, int nArg); /* Added by 3.3.13 */ int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); int (*clear_bindings)(sqlite3_stmt*); /* Added by 3.4.1 */ int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, void (*xDestroy)(void *)); /* Added by 3.5.0 */ int (*bind_zeroblob)(sqlite3_stmt*,int,int); int (*blob_bytes)(sqlite3_blob*); int (*blob_close)(sqlite3_blob*); int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, int,sqlite3_blob**); int (*blob_read)(sqlite3_blob*,void*,int,int); int (*blob_write)(sqlite3_blob*,const void*,int,int); int (*create_collation_v2)(sqlite3*,const char*,int,void*, int(*)(void*,int,const void*,int,const void*), void(*)(void*)); int (*file_control)(sqlite3*,const char*,int,void*); sqlite3_int64 (*memory_highwater)(int); sqlite3_int64 (*memory_used)(void); sqlite3_mutex *(*mutex_alloc)(int); void (*mutex_enter)(sqlite3_mutex*); void (*mutex_free)(sqlite3_mutex*); void (*mutex_leave)(sqlite3_mutex*); int (*mutex_try)(sqlite3_mutex*); int (*open_v2)(const char*,sqlite3**,int,const char*); int (*release_memory)(int); void (*result_error_nomem)(sqlite3_context*); void (*result_error_toobig)(sqlite3_context*); int (*sleep)(int); void (*soft_heap_limit)(int); sqlite3_vfs *(*vfs_find)(const char*); int (*vfs_register)(sqlite3_vfs*,int); int (*vfs_unregister)(sqlite3_vfs*); int (*xthreadsafe)(void); void (*result_zeroblob)(sqlite3_context*,int); void (*result_error_code)(sqlite3_context*,int); int (*test_control)(int, ...); void (*randomness)(int,void*); sqlite3 *(*context_db_handle)(sqlite3_context*); int (*extended_result_codes)(sqlite3*,int); int (*limit)(sqlite3*,int,int); sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); const char *(*sql)(sqlite3_stmt*); int (*status)(int,int*,int*,int); int (*backup_finish)(sqlite3_backup*); sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); int (*backup_pagecount)(sqlite3_backup*); int (*backup_remaining)(sqlite3_backup*); int (*backup_step)(sqlite3_backup*,int); const char *(*compileoption_get)(int); int (*compileoption_used)(const char*); int (*create_function_v2)(sqlite3*,const char*,int,int,void*, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*)); int (*db_config)(sqlite3*,int,...); sqlite3_mutex *(*db_mutex)(sqlite3*); int (*db_status)(sqlite3*,int,int*,int*,int); int (*extended_errcode)(sqlite3*); void (*log)(int,const char*,...); sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); const char *(*sourceid)(void); int (*stmt_status)(sqlite3_stmt*,int,int); int (*strnicmp)(const char*,const char*,int); int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); int (*wal_autocheckpoint)(sqlite3*,int); int (*wal_checkpoint)(sqlite3*,const char*); void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); int (*vtab_config)(sqlite3*,int op,...); int (*vtab_on_conflict)(sqlite3*); /* Version 3.7.16 and later */ int (*close_v2)(sqlite3*); const char *(*db_filename)(sqlite3*,const char*); int (*db_readonly)(sqlite3*,const char*); int (*db_release_memory)(sqlite3*); const char *(*errstr)(int); int (*stmt_busy)(sqlite3_stmt*); int (*stmt_readonly)(sqlite3_stmt*); int (*stricmp)(const char*,const char*); int (*uri_boolean)(const char*,const char*,int); sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); const char *(*uri_parameter)(const char*,const char*); char *(*vsnprintf)(int,char*,const char*,va_list); int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); /* Version 3.8.7 and later */ int (*auto_extension)(void(*)(void)); int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64, void(*)(void*)); int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64, void(*)(void*),unsigned char); int (*cancel_auto_extension)(void(*)(void)); int (*load_extension)(sqlite3*,const char*,const char*,char**); void *(*malloc64)(sqlite3_uint64); sqlite3_uint64 (*msize)(void*); void *(*realloc64)(void*,sqlite3_uint64); void (*reset_auto_extension)(void); void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64, void(*)(void*)); void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64, void(*)(void*), unsigned char); int (*strglob)(const char*,const char*); /* Version 3.8.11 and later */ sqlite3_value *(*value_dup)(const sqlite3_value*); void (*value_free)(sqlite3_value*); int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64); int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64); /* Version 3.9.0 and later */ unsigned int (*value_subtype)(sqlite3_value*); void (*result_subtype)(sqlite3_context*,unsigned int); /* Version 3.10.0 and later */ int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int); int (*strlike)(const char*,const char*,unsigned int); int (*db_cacheflush)(sqlite3*); /* Version 3.12.0 and later */ int (*system_errno)(sqlite3*); /* Version 3.14.0 and later */ int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*); char *(*expanded_sql)(sqlite3_stmt*); }; /* ** This is the function signature used for all extension entry points. It ** is also defined in the file "loadext.c". */ typedef int (*sqlite3_loadext_entry)( sqlite3 *db, /* Handle to the database. */ char **pzErrMsg, /* Used to set error string on failure. */ const sqlite3_api_routines *pThunk /* Extension API function pointers. */ ); /* ** The following macros redefine the API routines so that they are ** redirected through the global sqlite3_api structure. ** ** This header file is also used by the loadext.c source file ** (part of the main SQLite library - not an extension) so that ** it can get access to the sqlite3_api_routines structure ** definition. But the main library does not want to redefine ** the API. So the redefinition macros are only valid if the ** SQLITE_CORE macros is undefined. */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) #define sqlite3_aggregate_context sqlite3_api->aggregate_context #ifndef SQLITE_OMIT_DEPRECATED #define sqlite3_aggregate_count sqlite3_api->aggregate_count #endif #define sqlite3_bind_blob sqlite3_api->bind_blob #define sqlite3_bind_double sqlite3_api->bind_double #define sqlite3_bind_int sqlite3_api->bind_int #define sqlite3_bind_int64 sqlite3_api->bind_int64 #define sqlite3_bind_null sqlite3_api->bind_null #define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count #define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index #define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name #define sqlite3_bind_text sqlite3_api->bind_text #define sqlite3_bind_text16 sqlite3_api->bind_text16 #define sqlite3_bind_value sqlite3_api->bind_value #define sqlite3_busy_handler sqlite3_api->busy_handler #define sqlite3_busy_timeout sqlite3_api->busy_timeout #define sqlite3_changes sqlite3_api->changes #define sqlite3_close sqlite3_api->close #define sqlite3_collation_needed sqlite3_api->collation_needed #define sqlite3_collation_needed16 sqlite3_api->collation_needed16 #define sqlite3_column_blob sqlite3_api->column_blob #define sqlite3_column_bytes sqlite3_api->column_bytes #define sqlite3_column_bytes16 sqlite3_api->column_bytes16 #define sqlite3_column_count sqlite3_api->column_count #define sqlite3_column_database_name sqlite3_api->column_database_name #define sqlite3_column_database_name16 sqlite3_api->column_database_name16 #define sqlite3_column_decltype sqlite3_api->column_decltype #define sqlite3_column_decltype16 sqlite3_api->column_decltype16 #define sqlite3_column_double sqlite3_api->column_double #define sqlite3_column_int sqlite3_api->column_int #define sqlite3_column_int64 sqlite3_api->column_int64 #define sqlite3_column_name sqlite3_api->column_name #define sqlite3_column_name16 sqlite3_api->column_name16 #define sqlite3_column_origin_name sqlite3_api->column_origin_name #define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 #define sqlite3_column_table_name sqlite3_api->column_table_name #define sqlite3_column_table_name16 sqlite3_api->column_table_name16 #define sqlite3_column_text sqlite3_api->column_text #define sqlite3_column_text16 sqlite3_api->column_text16 #define sqlite3_column_type sqlite3_api->column_type #define sqlite3_column_value sqlite3_api->column_value #define sqlite3_commit_hook sqlite3_api->commit_hook #define sqlite3_complete sqlite3_api->complete #define sqlite3_complete16 sqlite3_api->complete16 #define sqlite3_create_collation sqlite3_api->create_collation #define sqlite3_create_collation16 sqlite3_api->create_collation16 #define sqlite3_create_function sqlite3_api->create_function #define sqlite3_create_function16 sqlite3_api->create_function16 #define sqlite3_create_module sqlite3_api->create_module #define sqlite3_create_module_v2 sqlite3_api->create_module_v2 #define sqlite3_data_count sqlite3_api->data_count #define sqlite3_db_handle sqlite3_api->db_handle #define sqlite3_declare_vtab sqlite3_api->declare_vtab #define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache #define sqlite3_errcode sqlite3_api->errcode #define sqlite3_errmsg sqlite3_api->errmsg #define sqlite3_errmsg16 sqlite3_api->errmsg16 #define sqlite3_exec sqlite3_api->exec #ifndef SQLITE_OMIT_DEPRECATED #define sqlite3_expired sqlite3_api->expired #endif #define sqlite3_finalize sqlite3_api->finalize #define sqlite3_free sqlite3_api->free #define sqlite3_free_table sqlite3_api->free_table #define sqlite3_get_autocommit sqlite3_api->get_autocommit #define sqlite3_get_auxdata sqlite3_api->get_auxdata #define sqlite3_get_table sqlite3_api->get_table #ifndef SQLITE_OMIT_DEPRECATED #define sqlite3_global_recover sqlite3_api->global_recover #endif #define sqlite3_interrupt sqlite3_api->interruptx #define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid #define sqlite3_libversion sqlite3_api->libversion #define sqlite3_libversion_number sqlite3_api->libversion_number #define sqlite3_malloc sqlite3_api->malloc #define sqlite3_mprintf sqlite3_api->mprintf #define sqlite3_open sqlite3_api->open #define sqlite3_open16 sqlite3_api->open16 #define sqlite3_prepare sqlite3_api->prepare #define sqlite3_prepare16 sqlite3_api->prepare16 #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 #define sqlite3_profile sqlite3_api->profile #define sqlite3_progress_handler sqlite3_api->progress_handler #define sqlite3_realloc sqlite3_api->realloc #define sqlite3_reset sqlite3_api->reset #define sqlite3_result_blob sqlite3_api->result_blob #define sqlite3_result_double sqlite3_api->result_double #define sqlite3_result_error sqlite3_api->result_error #define sqlite3_result_error16 sqlite3_api->result_error16 #define sqlite3_result_int sqlite3_api->result_int #define sqlite3_result_int64 sqlite3_api->result_int64 #define sqlite3_result_null sqlite3_api->result_null #define sqlite3_result_text sqlite3_api->result_text #define sqlite3_result_text16 sqlite3_api->result_text16 #define sqlite3_result_text16be sqlite3_api->result_text16be #define sqlite3_result_text16le sqlite3_api->result_text16le #define sqlite3_result_value sqlite3_api->result_value #define sqlite3_rollback_hook sqlite3_api->rollback_hook #define sqlite3_set_authorizer sqlite3_api->set_authorizer #define sqlite3_set_auxdata sqlite3_api->set_auxdata #define sqlite3_snprintf sqlite3_api->snprintf #define sqlite3_step sqlite3_api->step #define sqlite3_table_column_metadata sqlite3_api->table_column_metadata #define sqlite3_thread_cleanup sqlite3_api->thread_cleanup #define sqlite3_total_changes sqlite3_api->total_changes #define sqlite3_trace sqlite3_api->trace #ifndef SQLITE_OMIT_DEPRECATED #define sqlite3_transfer_bindings sqlite3_api->transfer_bindings #endif #define sqlite3_update_hook sqlite3_api->update_hook #define sqlite3_user_data sqlite3_api->user_data #define sqlite3_value_blob sqlite3_api->value_blob #define sqlite3_value_bytes sqlite3_api->value_bytes #define sqlite3_value_bytes16 sqlite3_api->value_bytes16 #define sqlite3_value_double sqlite3_api->value_double #define sqlite3_value_int sqlite3_api->value_int #define sqlite3_value_int64 sqlite3_api->value_int64 #define sqlite3_value_numeric_type sqlite3_api->value_numeric_type #define sqlite3_value_text sqlite3_api->value_text #define sqlite3_value_text16 sqlite3_api->value_text16 #define sqlite3_value_text16be sqlite3_api->value_text16be #define sqlite3_value_text16le sqlite3_api->value_text16le #define sqlite3_value_type sqlite3_api->value_type #define sqlite3_vmprintf sqlite3_api->vmprintf #define sqlite3_vsnprintf sqlite3_api->vsnprintf #define sqlite3_overload_function sqlite3_api->overload_function #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 #define sqlite3_clear_bindings sqlite3_api->clear_bindings #define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob #define sqlite3_blob_bytes sqlite3_api->blob_bytes #define sqlite3_blob_close sqlite3_api->blob_close #define sqlite3_blob_open sqlite3_api->blob_open #define sqlite3_blob_read sqlite3_api->blob_read #define sqlite3_blob_write sqlite3_api->blob_write #define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 #define sqlite3_file_control sqlite3_api->file_control #define sqlite3_memory_highwater sqlite3_api->memory_highwater #define sqlite3_memory_used sqlite3_api->memory_used #define sqlite3_mutex_alloc sqlite3_api->mutex_alloc #define sqlite3_mutex_enter sqlite3_api->mutex_enter #define sqlite3_mutex_free sqlite3_api->mutex_free #define sqlite3_mutex_leave sqlite3_api->mutex_leave #define sqlite3_mutex_try sqlite3_api->mutex_try #define sqlite3_open_v2 sqlite3_api->open_v2 #define sqlite3_release_memory sqlite3_api->release_memory #define sqlite3_result_error_nomem sqlite3_api->result_error_nomem #define sqlite3_result_error_toobig sqlite3_api->result_error_toobig #define sqlite3_sleep sqlite3_api->sleep #define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit #define sqlite3_vfs_find sqlite3_api->vfs_find #define sqlite3_vfs_register sqlite3_api->vfs_register #define sqlite3_vfs_unregister sqlite3_api->vfs_unregister #define sqlite3_threadsafe sqlite3_api->xthreadsafe #define sqlite3_result_zeroblob sqlite3_api->result_zeroblob #define sqlite3_result_error_code sqlite3_api->result_error_code #define sqlite3_test_control sqlite3_api->test_control #define sqlite3_randomness sqlite3_api->randomness #define sqlite3_context_db_handle sqlite3_api->context_db_handle #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes #define sqlite3_limit sqlite3_api->limit #define sqlite3_next_stmt sqlite3_api->next_stmt #define sqlite3_sql sqlite3_api->sql #define sqlite3_status sqlite3_api->status #define sqlite3_backup_finish sqlite3_api->backup_finish #define sqlite3_backup_init sqlite3_api->backup_init #define sqlite3_backup_pagecount sqlite3_api->backup_pagecount #define sqlite3_backup_remaining sqlite3_api->backup_remaining #define sqlite3_backup_step sqlite3_api->backup_step #define sqlite3_compileoption_get sqlite3_api->compileoption_get #define sqlite3_compileoption_used sqlite3_api->compileoption_used #define sqlite3_create_function_v2 sqlite3_api->create_function_v2 #define sqlite3_db_config sqlite3_api->db_config #define sqlite3_db_mutex sqlite3_api->db_mutex #define sqlite3_db_status sqlite3_api->db_status #define sqlite3_extended_errcode sqlite3_api->extended_errcode #define sqlite3_log sqlite3_api->log #define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 #define sqlite3_sourceid sqlite3_api->sourceid #define sqlite3_stmt_status sqlite3_api->stmt_status #define sqlite3_strnicmp sqlite3_api->strnicmp #define sqlite3_unlock_notify sqlite3_api->unlock_notify #define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint #define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint #define sqlite3_wal_hook sqlite3_api->wal_hook #define sqlite3_blob_reopen sqlite3_api->blob_reopen #define sqlite3_vtab_config sqlite3_api->vtab_config #define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict /* Version 3.7.16 and later */ #define sqlite3_close_v2 sqlite3_api->close_v2 #define sqlite3_db_filename sqlite3_api->db_filename #define sqlite3_db_readonly sqlite3_api->db_readonly #define sqlite3_db_release_memory sqlite3_api->db_release_memory #define sqlite3_errstr sqlite3_api->errstr #define sqlite3_stmt_busy sqlite3_api->stmt_busy #define sqlite3_stmt_readonly sqlite3_api->stmt_readonly #define sqlite3_stricmp sqlite3_api->stricmp #define sqlite3_uri_boolean sqlite3_api->uri_boolean #define sqlite3_uri_int64 sqlite3_api->uri_int64 #define sqlite3_uri_parameter sqlite3_api->uri_parameter #define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf #define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 /* Version 3.8.7 and later */ #define sqlite3_auto_extension sqlite3_api->auto_extension #define sqlite3_bind_blob64 sqlite3_api->bind_blob64 #define sqlite3_bind_text64 sqlite3_api->bind_text64 #define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension #define sqlite3_load_extension sqlite3_api->load_extension #define sqlite3_malloc64 sqlite3_api->malloc64 #define sqlite3_msize sqlite3_api->msize #define sqlite3_realloc64 sqlite3_api->realloc64 #define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension #define sqlite3_result_blob64 sqlite3_api->result_blob64 #define sqlite3_result_text64 sqlite3_api->result_text64 #define sqlite3_strglob sqlite3_api->strglob /* Version 3.8.11 and later */ #define sqlite3_value_dup sqlite3_api->value_dup #define sqlite3_value_free sqlite3_api->value_free #define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64 #define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64 /* Version 3.9.0 and later */ #define sqlite3_value_subtype sqlite3_api->value_subtype #define sqlite3_result_subtype sqlite3_api->result_subtype /* Version 3.10.0 and later */ #define sqlite3_status64 sqlite3_api->status64 #define sqlite3_strlike sqlite3_api->strlike #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush /* Version 3.12.0 and later */ #define sqlite3_system_errno sqlite3_api->system_errno /* Version 3.14.0 and later */ #define sqlite3_trace_v2 sqlite3_api->trace_v2 #define sqlite3_expanded_sql sqlite3_api->expanded_sql #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) /* This case when the file really is being compiled as a loadable ** extension */ # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; # define SQLITE_EXTENSION_INIT3 \ extern const sqlite3_api_routines *sqlite3_api; #else /* This case when the file is being statically linked into the ** application */ # define SQLITE_EXTENSION_INIT1 /*no-op*/ # define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ # define SQLITE_EXTENSION_INIT3 /*no-op*/ #endif #endif /* SQLITE3EXT_H */ #else // USE_LIBSQLITE3 // If users really want to link against the system sqlite3 we // need to make this file a noop. #endifgo-sqlite3-1.4.0/tool/000077500000000000000000000000001320252770200144605ustar00rootroot00000000000000go-sqlite3-1.4.0/tool/upgrade.go000066400000000000000000000035651320252770200164470ustar00rootroot00000000000000// +build ignore package main import ( "archive/zip" "bytes" "fmt" "io" "io/ioutil" "log" "net/http" "os" "path" "path/filepath" "strings" "github.com/PuerkitoBio/goquery" ) func main() { site := "https://www.sqlite.org/download.html" fmt.Printf("scraping %v\n", site) doc, err := goquery.NewDocument(site) if err != nil { log.Fatal(err) } var url string doc.Find("a").Each(func(_ int, s *goquery.Selection) { if url == "" && strings.HasPrefix(s.Text(), "sqlite-amalgamation-") { url = "https://www.sqlite.org/2017/" + s.Text() } }) if url == "" { return } fmt.Printf("downloading %v\n", url) resp, err := http.Get(url) if err != nil { log.Fatal(err) } b, err := ioutil.ReadAll(resp.Body) if err != nil { resp.Body.Close() log.Fatal(err) } fmt.Printf("extracting %v\n", path.Base(url)) r, err := zip.NewReader(bytes.NewReader(b), resp.ContentLength) if err != nil { resp.Body.Close() log.Fatal(err) } resp.Body.Close() for _, zf := range r.File { var f *os.File switch path.Base(zf.Name) { case "sqlite3.c": f, err = os.Create("sqlite3-binding.c") case "sqlite3.h": f, err = os.Create("sqlite3-binding.h") case "sqlite3ext.h": f, err = os.Create("sqlite3ext.h") default: continue } if err != nil { log.Fatal(err) } zr, err := zf.Open() if err != nil { log.Fatal(err) } _, err = io.WriteString(f, "#ifndef USE_LIBSQLITE3\n") if err != nil { zr.Close() f.Close() log.Fatal(err) } _, err = io.Copy(f, zr) if err != nil { zr.Close() f.Close() log.Fatal(err) } _, err = io.WriteString(f, "#else // USE_LIBSQLITE3\n // If users really want to link against the system sqlite3 we\n// need to make this file a noop.\n #endif") if err != nil { zr.Close() f.Close() log.Fatal(err) } zr.Close() f.Close() fmt.Printf("extracted %v\n", filepath.Base(f.Name())) } }