Enable extended error codes.

This commit is contained in:
Christoph Martin 2014-04-01 14:01:19 +02:00
parent 58c62dc30c
commit f395aa170e
3 changed files with 265 additions and 34 deletions

129
error.go
View File

@ -4,51 +4,120 @@ import "C"
type ErrNo int type ErrNo int
const ErrNoMask C.int = 0xff
type ErrNoExtended int
type Error struct { type Error struct {
Code ErrNo /* The error code returned by SQLite */ Code ErrNo /* The error code returned by SQLite */
err string /* The error string returned by sqlite3_errmsg(), ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */
err string /* The error string returned by sqlite3_errmsg(),
this usually contains more specific details. */ this usually contains more specific details. */
} }
// result codes from http://www.sqlite.org/c3ref/c_abort.html // result codes from http://www.sqlite.org/c3ref/c_abort.html
var ( var (
ErrError error = ErrNo(1) /* SQL error or missing database */ ErrError = ErrNo(1) /* SQL error or missing database */
ErrInternal error = ErrNo(2) /* Internal logic error in SQLite */ ErrInternal = ErrNo(2) /* Internal logic error in SQLite */
ErrPerm error = ErrNo(3) /* Access permission denied */ ErrPerm = ErrNo(3) /* Access permission denied */
ErrAbort error = ErrNo(4) /* Callback routine requested an abort */ ErrAbort = ErrNo(4) /* Callback routine requested an abort */
ErrBusy error = ErrNo(5) /* The database file is locked */ ErrBusy = ErrNo(5) /* The database file is locked */
ErrLocked error = ErrNo(6) /* A table in the database is locked */ ErrLocked = ErrNo(6) /* A table in the database is locked */
ErrNomem error = ErrNo(7) /* A malloc() failed */ ErrNomem = ErrNo(7) /* A malloc() failed */
ErrReadonly error = ErrNo(8) /* Attempt to write a readonly database */ ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */
ErrInterrupt error = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */ ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */
ErrIoErr error = ErrNo(10) /* Some kind of disk I/O error occurred */ ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */
ErrCorrupt error = ErrNo(11) /* The database disk image is malformed */ ErrCorrupt = ErrNo(11) /* The database disk image is malformed */
ErrNotFound error = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */ ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */
ErrFull error = ErrNo(13) /* Insertion failed because database is full */ ErrFull = ErrNo(13) /* Insertion failed because database is full */
ErrCantOpen error = ErrNo(14) /* Unable to open the database file */ ErrCantOpen = ErrNo(14) /* Unable to open the database file */
ErrProtocol error = ErrNo(15) /* Database lock protocol error */ ErrProtocol = ErrNo(15) /* Database lock protocol error */
ErrEmpty error = ErrNo(16) /* Database is empty */ ErrEmpty = ErrNo(16) /* Database is empty */
ErrSchema error = ErrNo(17) /* The database schema changed */ ErrSchema = ErrNo(17) /* The database schema changed */
ErrTooBig error = ErrNo(18) /* String or BLOB exceeds size limit */ ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */
ErrConstraint error = ErrNo(19) /* Abort due to constraint violation */ ErrConstraint = ErrNo(19) /* Abort due to constraint violation */
ErrMismatch error = ErrNo(20) /* Data type mismatch */ ErrMismatch = ErrNo(20) /* Data type mismatch */
ErrMisuse error = ErrNo(21) /* Library used incorrectly */ ErrMisuse = ErrNo(21) /* Library used incorrectly */
ErrNoLFS error = ErrNo(22) /* Uses OS features not supported on host */ ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */
ErrAuth error = ErrNo(23) /* Authorization denied */ ErrAuth = ErrNo(23) /* Authorization denied */
ErrFormat error = ErrNo(24) /* Auxiliary database format error */ ErrFormat = ErrNo(24) /* Auxiliary database format error */
ErrRange error = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */ ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */
ErrNotADB error = ErrNo(26) /* File opened that is not a database file */ ErrNotADB = ErrNo(26) /* File opened that is not a database file */
ErrNotice error = ErrNo(27) /* Notifications from sqlite3_log() */ ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */
ErrWarning error = ErrNo(28) /* Warnings from sqlite3_log() */ ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */
) )
func (err ErrNo) Error() string { func (err ErrNo) Error() string {
return Error{Code: err}.Error() return Error{Code: err}.Error()
} }
func (err ErrNo) Extend(by int) ErrNoExtended {
return ErrNoExtended(int(err) | (by << 8))
}
func (err ErrNoExtended) Error() string {
return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error()
}
func (err Error) Error() string { func (err Error) Error() string {
if err.err != "" { if err.err != "" {
return err.err return err.err
} }
return errorString(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)
)

View File

@ -57,15 +57,175 @@ func TestSqlLogicErrors(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
defer db.Close()
_, err = db.Exec("CREATE TABLE Foo (id INT PRIMARY KEY)") _, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
const expectedErr = "table Foo already exists" const expectedErr = "table Foo already exists"
_, err = db.Exec("CREATE TABLE Foo (id INT PRIMARY KEY)") _, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
if err.Error() != expectedErr { if err.Error() != expectedErr {
t.Errorf("Unexpected error: %s, expected %s", 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)
}
}
} }

View File

@ -139,8 +139,10 @@ func (c *SQLiteConn) AutoCommit() bool {
} }
func (c *SQLiteConn) lastError() Error { func (c *SQLiteConn) lastError() Error {
return Error{Code: ErrNo(C.sqlite3_errcode(c.db)), return Error{
err: C.GoString(C.sqlite3_errmsg(c.db)), Code: ErrNo(C.sqlite3_errcode(c.db)),
ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(c.db)),
err: C.GoString(C.sqlite3_errmsg(c.db)),
} }
} }