bvinc / go-sqlite-lite

SQLite driver for the Go programming language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can I get more specific errors?

slaskis opened this issue · comments

I've been playing around with this brilliant library for a bit and while its really easy to use I've found it hard to troubleshoot because pretty much every error I get is the very generic sqlite3: SQL logic error [1] no matter if its an invalid query or something else.

Is it possible to get more specific errors? It seems to me that sqlite is able to give more helpful error messages (like in this stack overflow question).

Could it be a compile flag missing or something with my go compiler? I tried different DEBUG flags but with no difference. But I'm also pretty new when it comes to sqlite and compiling C code in general so I might just be doing it wrong.

Thanks for making this!

There are some things that I can explore to get better error messages out of SQLite.

Can you give me an example that I can work with where you feel that the error message could really be improved?

Sure!

I've made now this test case with some examples:

package main

import (
	"testing"

	"github.com/bvinc/go-sqlite-lite/sqlite3"
)

func TestErrors(t *testing.T) {
	conn, err := sqlite3.Open(":memory:")
	if err != nil {
		t.Error(err)
	}

	_, err = conn.Prepare("SELECT invalid FROM nowhere")
	if err != nil {
		t.Error(err) // sqlite3: SQL logic error [1]
	}

	_, err = conn.Prepare("SELECT invalid FROX nowhere")
	if err != nil {
		t.Error(err) // sqlite3: SQL logic error [1]
	}

	err = conn.Exec("SELECT invalid FROM nowhere")
	if err != nil {
		t.Error(err) // sqlite3: no such table: nowhere [1]
	}

	err = conn.Exec("SELECT invalid FROX nowhere")
	if err != nil {
		t.Error(err) // sqlite3: near "nowhere": syntax error [1]
	}

	stmt, err := conn.Prepare("CREATE TABLE x (a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);")
	if err != nil {
		t.Error(err) // ok
	}
	err = stmt.Exec()
	if err != nil {
		t.Error(err) // ok
	}
	_, err = conn.Prepare("CREATE TABLE x (a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);")
	if err != nil {
		t.Error(err) // sqlite3: SQL logic error [1]
	}

	err = conn.Exec("CREATE TABLE y (a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);")
	if err != nil {
		t.Error(err) // ok
	}

	err = conn.Exec("CREATE TABLE y (a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);")
	if err != nil {
		t.Error(err) // sqlite3: table y already exists [1]
	}

	_, err = conn.Prepare("SELECT invalid FROM x")
	if err != nil {
		t.Error(err) // sqlite3: SQL logic error [1]
	}

	err = conn.Exec("SELECT invalid FROM x")
	if err != nil {
		t.Error(err) // sqlite3: no such column: invalid [1]
	}

	_, err = conn.Prepare("SELECT a, b FROM x")
	if err != nil {
		t.Error(err) // ok
	}

	err = conn.Exec("SELECT a, b FROM x")
	if err != nil {
		t.Error(err) // ok
	}

}

I noticed that it seems to be the conn.Prepare() function which lacks information. if i run conn.Exec() there are more specific messages. Ideally prepare would give the same messages, in the mean time i guess if i get an error on Prepare i can run the same query with Exec?

Tried to do this:

stmt, err := conn.Prepare(sql)
if err != nil {
  err = conn.Exec(sql)
  return err
}
...

but I still seem to get the sqlite3: SQL logic error [1] so I guess its a state in sqlite or something...

Thanks!

I think this comes from an early decision I made to make Stmt a light wrapper around a sqlite3_stmt * in C. When a function fails, currently, I only call sqlite3_errstr on the return code, which gives a generic error message related to the return code.

I'm going to experiment with calling sqlite3_errmsg on the database connection. I think that it has the ability to give better error messages.

Yeah, it's looking much better:

Before:

--- FAIL: TestErrors (0.00s)
    main_test.go:17: sqlite3: SQL logic error [1]
    main_test.go:22: sqlite3: SQL logic error [1]
    main_test.go:27: sqlite3: no such table: nowhere [1]
    main_test.go:32: sqlite3: near "nowhere": syntax error [1]
    main_test.go:45: sqlite3: SQL logic error [1]
    main_test.go:55: sqlite3: table y already exists [1]
    main_test.go:60: sqlite3: SQL logic error [1]
    main_test.go:65: sqlite3: no such column: invalid [1]

After:

--- FAIL: TestErrors (0.00s)
    main_test.go:17: sqlite3: no such table: nowhere [1]
    main_test.go:22: sqlite3: near "nowhere": syntax error [1]
    main_test.go:27: sqlite3: no such table: nowhere [1]
    main_test.go:32: sqlite3: near "nowhere": syntax error [1]
    main_test.go:45: sqlite3: table x already exists [1]
    main_test.go:55: sqlite3: table y already exists [1]
    main_test.go:60: sqlite3: no such column: invalid [1]
    main_test.go:65: sqlite3: no such column: invalid [1]

Thanks for the bug report. I've committed the change and released a v0.4.2 with the change.

Beautiful. Thank you for the quick fix!