Skip to content

Prepared statement are broken after sqlite error #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
threez opened this issue Sep 27, 2014 · 10 comments
Closed

Prepared statement are broken after sqlite error #150

threez opened this issue Sep 27, 2014 · 10 comments

Comments

@threez
Copy link

threez commented Sep 27, 2014

I created a prepared statement for an INSERT. If the insert failed, e.g. because a UNIQUE constraint was violated, the prepared statement is broken and needs to be recreated in order to work again.

I have an example project, here where you can find the code to recreate the statement. If i remove it, my tests fail. I created a simple version of the problem here:

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/mattn/go-sqlite3"
)

func main() {
    var db *sql.DB
    db, _ = sql.Open("sqlite3", ":memory:")
    db.Exec(`CREATE TABLE IF NOT EXISTS Users (
        uid INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        CONSTRAINT SingleKeys UNIQUE (name) ON CONFLICT ROLLBACK
    );`)
    stmt, _ := db.Prepare(`INSERT INTO Users (name) VALUES (?);`)
    _, e1 := stmt.Exec("test")
    fmt.Printf("Should NOT return error: %s\n", e1)
    _, e2 := stmt.Exec("test")
    fmt.Printf("Should return error: %s\n", e2)
    _, e3 := stmt.Exec("test1")
    fmt.Printf("Should NOT return error: %s\n", e3)
    stmt1, _ := db.Prepare(`INSERT INTO Users (name) VALUES (?);`)
    _, e4 := stmt1.Exec("test1")
    fmt.Printf("Should NOT return error: %s\n", e4)
}
@mattn
Copy link
Owner

mattn commented Sep 29, 2014

Once you get error for the statement, you need to make it again.
It can't reset the statement by calling sqlite3_reset.

@threez
Copy link
Author

threez commented Sep 29, 2014

but you can make this transparently right? Otherwise i have to do it all over the code. This would not be DRY.

@mattn
Copy link
Owner

mattn commented Sep 29, 2014

but you can make this transparently right?

Hmm, this driver doesn't know whether the code need to make same statement again.

@threez
Copy link
Author

threez commented Sep 29, 2014

I implemented a mechanism in my pull request. Please review.

@mattn
Copy link
Owner

mattn commented Nov 14, 2014

_, e1 := stmt.Exec("test")
if e1 != nil {
  return e1
}

We may not want to repair statement. Sorry this is a spec of my driver.

@mattn mattn closed this as completed Nov 14, 2014
@threez
Copy link
Author

threez commented Nov 15, 2014

MySQL and ProstgreSQL don't behave like this. I think it breaks the go database/sql interface.

@mattn
Copy link
Owner

mattn commented Nov 15, 2014

BTW, why e4 should be error? When e3 is nil, test1 is inserted. So e4 should not be nil, I think.

@mattn
Copy link
Owner

mattn commented Nov 15, 2014

If that e4 is not nil is right behavior, this issue will be fixed by below's patch.

diff --git a/sqlite3.go b/sqlite3.go
index abfc2fe..c5c25ed 100644
--- a/sqlite3.go
+++ b/sqlite3.go
@@ -425,10 +425,12 @@ func (r *SQLiteResult) RowsAffected() (int64, error) {
 // Execute the statement with arguments. Return result object.
 func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
    if err := s.bind(args); err != nil {
+       C.sqlite3_reset(s.s)
        return nil, err
    }
    rv := C.sqlite3_step(s.s)
    if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
+       C.sqlite3_reset(s.s)
        return nil, s.c.lastError()
    }

@threez
Copy link
Author

threez commented Nov 15, 2014

Correct, if e2 doesn't break the stmt, e3 will not be set and e4 will then be an error. If the diff you proposes fixes the problem I would appreciate the change. I could tell you then if it works like MySQL and PostgreSQL.

mattn added a commit that referenced this issue Nov 16, 2014
@antoine-lizee
Copy link
Contributor

This got me stuck for a while. I updated because of this thread, and now it works fine. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants