Index: System.Data.SQLite/SQLiteBase.cs
==================================================================
--- System.Data.SQLite/SQLiteBase.cs
+++ System.Data.SQLite/SQLiteBase.cs
@@ -591,14 +591,14 @@
///////////////////////////////////////////////////////////////////////////////////////////////
private static string[] _errorMessages = {
/* SQLITE_OK */ "not an error",
- /* SQLITE_ERROR */ "SQL logic error or missing database",
+ /* SQLITE_ERROR */ "SQL logic error",
/* SQLITE_INTERNAL */ "internal logic error",
/* SQLITE_PERM */ "access permission denied",
- /* SQLITE_ABORT */ "callback requested query abort",
+ /* SQLITE_ABORT */ "query aborted",
/* SQLITE_BUSY */ "database is locked",
/* SQLITE_LOCKED */ "database table is locked",
/* SQLITE_NOMEM */ "out of memory",
/* SQLITE_READONLY */ "attempt to write a readonly database",
/* SQLITE_INTERRUPT */ "interrupted",
@@ -611,16 +611,16 @@
/* SQLITE_EMPTY */ "table contains no data",
/* SQLITE_SCHEMA */ "database schema has changed",
/* SQLITE_TOOBIG */ "string or blob too big",
/* SQLITE_CONSTRAINT */ "constraint failed",
/* SQLITE_MISMATCH */ "datatype mismatch",
- /* SQLITE_MISUSE */ "library routine called out of sequence",
+ /* SQLITE_MISUSE */ "bad parameter or other API misuse",
/* SQLITE_NOLFS */ "large file support is disabled",
/* SQLITE_AUTH */ "authorization denied",
/* SQLITE_FORMAT */ "auxiliary database format error",
- /* SQLITE_RANGE */ "bind or column index out of range",
- /* SQLITE_NOTADB */ "file is encrypted or is not a database",
+ /* SQLITE_RANGE */ "column index out of range",
+ /* SQLITE_NOTADB */ "file is not a database",
/* SQLITE_NOTICE */ "notification message",
/* SQLITE_WARNING */ "warning message"
};
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -631,14 +631,24 @@
///
/// The SQLite return code.
/// The error message or null if it cannot be found.
protected static string FallbackGetErrorString(SQLiteErrorCode rc)
{
+ switch (rc)
+ {
+ case SQLiteErrorCode.Abort_Rollback:
+ return "abort due to ROLLBACK";
+ case SQLiteErrorCode.Row:
+ return "another row available";
+ case SQLiteErrorCode.Done:
+ return "no more rows available";
+ }
+
if (_errorMessages == null)
return null;
- int index = (int)rc;
+ int index = (int)(rc & SQLiteErrorCode.NonExtendedMask);
if ((index < 0) || (index >= _errorMessages.Length))
index = (int)SQLiteErrorCode.Error; /* Make into generic error. */
return _errorMessages[index];
Index: Tests/basic.eagle
==================================================================
--- Tests/basic.eagle
+++ Tests/basic.eagle
@@ -4755,10 +4755,99 @@
} -constraints [fixConstraints {eagle !System.Data.SQLite.Encryption monoBug28\
command.sql compile.DATA SQLite System.Data.SQLite}] -result {1 {SQL logic\
error -- Cannot use "HexPassword" connection string property: library was not\
built with encryption support, please see "https://www.sqlite.org/see" for more\
information}}}
+
+###############################################################################
+
+runTest {test data-1.93 {extended error code messages} -setup {
+ #
+ # HACK: Temporarily disable automatic detection (and use) of the
+ # native sqlite3_errstr() API.
+ #
+ object invoke -flags +NonPublic \
+ System.Data.SQLite.SQLite3 have_errstr false
+
+ setupDb [set fileName data-1.93.db]
+} -body {
+ set errCodes [list]
+
+ lappend errCodes 256; # SQLITE_OK_LOAD_PERMANENTLY
+ lappend errCodes 513; # SQLITE_ERROR_RETRY
+ lappend errCodes 522; # SQLITE_IOERR_SHORT_READ
+ lappend errCodes 270; # SQLITE_CANTOPEN_NOTEMPDIR
+ lappend errCodes 539; # SQLITE_NOTICE_RECOVER_ROLLBACK
+ lappend errCodes 284; # SQLITE_WARNING_AUTOINDEX
+
+ set connection [getDbConnection]
+
+ lappend result [catchAndReturn {$connection EnableExtensions true}]
+
+ lappend result [catchAndReturn {$connection LoadExtension \
+ [getCoreExtensionBinaryFileName null] interop_test_extension_init}]
+
+ lappend result [catchAndReturn {$connection SetExtendedResultCodes false}]
+
+ foreach errCode $errCodes {
+ #
+ # HACK: Without extended error codes, SQLITE_OK_* cannot be handled via
+ # the semantics in the System.Data.SQLite.SQLite3.Prepare method;
+ # it will always assume that more results are available when the
+ # error code is Ok, thereby looping forever.
+ #
+ if {($errCode & 0xFF) == 0} then {continue}
+
+ resetException; catchAndSetException {
+ sql execute -execute scalar $db \
+ "SELECT interopError(?);" [list param1 Int32 $errCode]
+ } exception
+
+ lappend result [$exception ResultCode] \
+ [normalizeExceptionMessage [$exception Message]]
+ }
+
+ lappend result [catchAndReturn {$connection SetExtendedResultCodes true}]
+
+ foreach errCode $errCodes {
+ resetException; catchAndSetException {
+ sql execute -execute scalar $db \
+ "SELECT interopError(?);" [list param1 Int32 $errCode]
+ } exception
+
+ lappend result [$exception ResultCode] \
+ [normalizeExceptionMessage [$exception Message]]
+ }
+
+ set result
+} -cleanup {
+ cleanupDb $fileName
+
+ freeDbConnection
+
+ catch {
+ #
+ # HACK: Restore automatic detection (and use) of the native
+ # sqlite3_errstr() API.
+ #
+ object invoke -flags +NonPublic \
+ System.Data.SQLite.SQLite3 have_errstr null
+ }
+
+ unset -nocomplain result exception errCode errCodes
+ unset -nocomplain connection db fileName
+} -constraints {eagle command.object monoBug28\
+compile.DATA defineConstant.System.Data.SQLite.INTEROP_TEST_EXTENSION\
+command.sql SQLite System.Data.SQLite} -result {{0 {}} {0 {}} {0 {}} Error {SQL\
+logic error ==> SQL logic error} IoErr {disk I/O error ==> disk I/O error}\
+CantOpen {unable to open database file ==> unable to open database file} Notice\
+{notification message ==> unknown error} Warning {warning message ==> unknown\
+error} {0 {}} Ok_Load_Permanently {not an error ==> not an error} Error_Retry\
+{SQL logic error ==> SQL logic error} IoErr_Short_Read {disk I/O error ==> disk\
+I/O error} CantOpen_NoTempDir {unable to open database file ==> unable to open\
+database file} Notice_Recover_Rollback {notification message ==> unknown error}\
+Warning_AutoIndex {warning message ==> unknown error}}}
###############################################################################
reportSQLiteResources $test_channel
Index: lib/System.Data.SQLite/common.eagle
==================================================================
--- lib/System.Data.SQLite/common.eagle
+++ lib/System.Data.SQLite/common.eagle
@@ -2169,10 +2169,40 @@
#
return [list $code $::errorCode \
[extractSystemDataSQLiteExceptionMessage $result]]
}
}
+
+ proc resetException {} {
+ #
+ # NOTE: Reset exception associated with this interpreter (to null).
+ # This (private) property is maintained on a per-thread basis.
+ #
+ object invoke -flags +NonPublic Interpreter.GetActive Exception null
+ return ""
+ }
+
+ proc catchAndSetException { script {varName ""} } {
+ #
+ # NOTE: Evaluate the script provided by our caller in their context.
+ #
+ catch {uplevel 1 $script}
+
+ #
+ # NOTE: Grab the (private) exception property from this interpreter,
+ # for this thread, and add as an opaque object handle in the
+ # context of our caller.
+ #
+ if {[string length $varName] > 0} then {
+ upvar 1 $varName exception
+ }
+
+ set exception [object invoke \
+ -alias -flags +NonPublic Interpreter.GetActive Exception]
+
+ return ""
+ }
proc compileCSharpWith {
text memory symbols strict resultsVarName errorsVarName fileNames
args } {
#
@@ -2286,10 +2316,15 @@
return [list \
[sql execute -execute scalar $db "PRAGMA page_size;"] \
[sql execute -execute scalar $db "PRAGMA cache_size;"]]
}
+
+ proc normalizeExceptionMessage { value } {
+ if {[string length $value] == 0} then {return $value}
+ return [string map [list \r\n " ==> "] $value]
+ }
proc extractSystemDataSQLiteExceptionMessage { value } {
#
# NOTE: If the string conforms to format of the normal exception
# error strings, extract and return only the error message