forked from mirror/go-sqlite3
Enable extended error codes.
This commit is contained in:
parent
58c62dc30c
commit
f395aa170e
129
error.go
129
error.go
|
@ -4,51 +4,120 @@ import "C"
|
|||
|
||||
type ErrNo int
|
||||
|
||||
const ErrNoMask C.int = 0xff
|
||||
|
||||
type ErrNoExtended int
|
||||
|
||||
type Error struct {
|
||||
Code ErrNo /* The error code returned by SQLite */
|
||||
err string /* The error string returned by sqlite3_errmsg(),
|
||||
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 error = ErrNo(1) /* SQL error or missing database */
|
||||
ErrInternal error = ErrNo(2) /* Internal logic error in SQLite */
|
||||
ErrPerm error = ErrNo(3) /* Access permission denied */
|
||||
ErrAbort error = ErrNo(4) /* Callback routine requested an abort */
|
||||
ErrBusy error = ErrNo(5) /* The database file is locked */
|
||||
ErrLocked error = ErrNo(6) /* A table in the database is locked */
|
||||
ErrNomem error = ErrNo(7) /* A malloc() failed */
|
||||
ErrReadonly error = ErrNo(8) /* Attempt to write a readonly database */
|
||||
ErrInterrupt error = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */
|
||||
ErrIoErr error = ErrNo(10) /* Some kind of disk I/O error occurred */
|
||||
ErrCorrupt error = ErrNo(11) /* The database disk image is malformed */
|
||||
ErrNotFound error = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */
|
||||
ErrFull error = ErrNo(13) /* Insertion failed because database is full */
|
||||
ErrCantOpen error = ErrNo(14) /* Unable to open the database file */
|
||||
ErrProtocol error = ErrNo(15) /* Database lock protocol error */
|
||||
ErrEmpty error = ErrNo(16) /* Database is empty */
|
||||
ErrSchema error = ErrNo(17) /* The database schema changed */
|
||||
ErrTooBig error = ErrNo(18) /* String or BLOB exceeds size limit */
|
||||
ErrConstraint error = ErrNo(19) /* Abort due to constraint violation */
|
||||
ErrMismatch error = ErrNo(20) /* Data type mismatch */
|
||||
ErrMisuse error = ErrNo(21) /* Library used incorrectly */
|
||||
ErrNoLFS error = ErrNo(22) /* Uses OS features not supported on host */
|
||||
ErrAuth error = ErrNo(23) /* Authorization denied */
|
||||
ErrFormat error = ErrNo(24) /* Auxiliary database format error */
|
||||
ErrRange error = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */
|
||||
ErrNotADB error = ErrNo(26) /* File opened that is not a database file */
|
||||
ErrNotice error = ErrNo(27) /* Notifications from sqlite3_log() */
|
||||
ErrWarning error = ErrNo(28) /* Warnings from sqlite3_log() */
|
||||
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() */
|
||||
)
|
||||
|
||||
func (err ErrNo) Error() string {
|
||||
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 {
|
||||
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)
|
||||
)
|
||||
|
|
164
error_test.go
164
error_test.go
|
@ -57,15 +57,175 @@ func TestSqlLogicErrors(t *testing.T) {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -139,8 +139,10 @@ func (c *SQLiteConn) AutoCommit() bool {
|
|||
}
|
||||
|
||||
func (c *SQLiteConn) lastError() Error {
|
||||
return Error{Code: ErrNo(C.sqlite3_errcode(c.db)),
|
||||
err: C.GoString(C.sqlite3_errmsg(c.db)),
|
||||
return Error{
|
||||
Code: ErrNo(C.sqlite3_errcode(c.db)),
|
||||
ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(c.db)),
|
||||
err: C.GoString(C.sqlite3_errmsg(c.db)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue