aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Mercl <0xjnml@gmail.com>2019-12-28 11:10:32 +0100
committerJan Mercl <0xjnml@gmail.com>2019-12-28 11:10:32 +0100
commit45936a375aff7a72b87df54bb40896ff2ac25615 (patch)
treeef0be323789cdc18a9a78e8fc049f4893772ea9a
parentcab95cbf041c29cf5c041cbbc597baffaaf2c6a4 (diff)
fix (*stmt).Close logic, closes #19
-rw-r--r--all_test.go111
-rw-r--r--sqlite.go14
2 files changed, 124 insertions, 1 deletions
diff --git a/all_test.go b/all_test.go
index e2b416f..1c830c8 100644
--- a/all_test.go
+++ b/all_test.go
@@ -540,3 +540,114 @@ outer:
t.Fatalf("%s\nerror: summary line not found", out)
}
}
+
+// https://gitlab.com/cznic/sqlite/issues/19
+func TestIssue19(t *testing.T) {
+ const (
+ drop = `
+drop table if exists products;
+`
+
+ up = `
+CREATE TABLE IF NOT EXISTS "products" (
+ "id" VARCHAR(255),
+ "user_id" VARCHAR(255),
+ "name" VARCHAR(255),
+ "description" VARCHAR(255),
+ "created_at" BIGINT,
+ "credits_price" BIGINT,
+ "enabled" BOOLEAN,
+ PRIMARY KEY("id")
+);
+`
+
+ productInsert = `
+INSERT INTO "products" ("id", "user_id", "name", "description", "created_at", "credits_price", "enabled") VALUES ('9be4398c-d527-4efb-93a4-fc532cbaf804', '16935690-348b-41a6-bb20-f8bb16011015', 'dqdwqdwqdwqwqdwqd', 'qwdwqwqdwqdwqdwqd', '1577448686', '1', '0');
+INSERT INTO "products" ("id", "user_id", "name", "description", "created_at", "credits_price", "enabled") VALUES ('759f10bd-9e1d-4ec7-b764-0868758d7b85', '16935690-348b-41a6-bb20-f8bb16011015', 'qdqwqwdwqdwqdwqwqd', 'wqdwqdwqdwqdwqdwq', '1577448692', '1', '1');
+INSERT INTO "products" ("id", "user_id", "name", "description", "created_at", "credits_price", "enabled") VALUES ('512956e7-224d-4b2a-9153-b83a52c4aa38', '16935690-348b-41a6-bb20-f8bb16011015', 'qwdwqwdqwdqdwqwqd', 'wqdwdqwqdwqdwqdwqdwqdqw', '1577448699', '2', '1');
+INSERT INTO "products" ("id", "user_id", "name", "description", "created_at", "credits_price", "enabled") VALUES ('02cd138f-6fa6-4909-9db7-a9d0eca4a7b7', '16935690-348b-41a6-bb20-f8bb16011015', 'qdwqdwqdwqwqdwdq', 'wqddwqwqdwqdwdqwdqwq', '1577448706', '3', '1');
+`
+ )
+
+ dir, err := ioutil.TempDir("", "sqlite-test-")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ defer os.RemoveAll(dir)
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ defer os.Chdir(wd)
+
+ if err := os.Chdir(dir); err != nil {
+ t.Fatal(err)
+ }
+
+ db, err := sql.Open("sqlite", "test.db")
+ if err != nil {
+ t.Fatal("failed to connect database")
+ }
+
+ defer db.Close()
+
+ db.SetMaxOpenConns(1)
+
+ if _, err = db.Exec(drop); err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err = db.Exec(up); err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err = db.Exec(productInsert); err != nil {
+ t.Fatal(err)
+ }
+
+ var count int64
+ if err = db.QueryRow("select count(*) from products where user_id = ?", "16935690-348b-41a6-bb20-f8bb16011015").Scan(&count); err != nil {
+ t.Fatal(err)
+ }
+
+ if count != 4 {
+ t.Fatalf("expected result for the count query %d, we received %d\n", 4, count)
+ }
+
+ rows, err := db.Query("select * from products where user_id = ?", "16935690-348b-41a6-bb20-f8bb16011015")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count = 0
+ for rows.Next() {
+ count++
+ }
+ if err := rows.Err(); err != nil {
+ t.Fatal(err)
+ }
+
+ if count != 4 {
+ t.Fatalf("expected result for the select query %d, we received %d\n", 4, count)
+ }
+
+ rows, err = db.Query("select * from products where enabled = ?", true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ count = 0
+ for rows.Next() {
+ count++
+ }
+ if err := rows.Err(); err != nil {
+ t.Fatal(err)
+ }
+
+ if count != 3 {
+ t.Fatalf("expected result for the enabled select query %d, we received %d\n", 3, count)
+ }
+}
diff --git a/sqlite.go b/sqlite.go
index 8a4d366..6617e28 100644
--- a/sqlite.go
+++ b/sqlite.go
@@ -119,6 +119,7 @@ type rows struct {
}
func newRows(s *stmt, pstmt crt.Intptr, rc0 int) (*rows, error) {
+ s.owned = true
r := &rows{
stmt: s,
pstmt: pstmt,
@@ -149,7 +150,12 @@ func (r *rows) Columns() (c []string) {
// Close closes the rows iterator.
func (r *rows) Close() (err error) {
- return r.finalize(r.pstmt)
+ err = r.finalize(r.pstmt)
+ r.stmt.owned = false
+ if err2 := r.stmt.Close(); err2 != nil && err == nil {
+ err = err2
+ }
+ return err
}
// Next is called to populate the next row of data into the provided slice. The
@@ -297,6 +303,8 @@ type stmt struct {
psql crt.Intptr // *int8
ppstmt crt.Intptr // **sqlite3_stmt
pzTail crt.Intptr // **int8
+
+ owned bool
}
func newStmt(c *conn, sql string) (*stmt, error) {
@@ -329,6 +337,10 @@ func newStmt(c *conn, sql string) (*stmt, error) {
//
// As of Go 1.1, a Stmt will not be closed if it's in use by any queries.
func (s *stmt) Close() (err error) {
+ if s.owned {
+ return
+ }
+
if s.psql != 0 {
err = s.free(s.psql)
s.psql = 0