forked from mirror/go-sqlite3
Enable extended error codes.
This commit is contained in:
parent
58c62dc30c
commit
f395aa170e
125
error.go
125
error.go
|
@ -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 */
|
||||||
|
ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */
|
||||||
err string /* The error string returned by sqlite3_errmsg(),
|
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)
|
||||||
|
)
|
||||||
|
|
164
error_test.go
164
error_test.go
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,9 @@ 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{
|
||||||
|
Code: ErrNo(C.sqlite3_errcode(c.db)),
|
||||||
|
ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(c.db)),
|
||||||
err: C.GoString(C.sqlite3_errmsg(c.db)),
|
err: C.GoString(C.sqlite3_errmsg(c.db)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue