From de619b6b1a87a2d57268155e94f5c91a63ed2566 Mon Sep 17 00:00:00 2001 From: ketmar Date: Mon, 3 Jun 2024 20:59:15 +0000 Subject: [PATCH] sq3: show SQLite error messages on stderr by default FossilOrigin-Name: 1616fe051d7b4ce90b987d55ed4760a3df28fecede140ad5f802e9141884da0c --- sq3.d | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/sq3.d b/sq3.d index 64f60d7..f393060 100644 --- a/sq3.d +++ b/sq3.d @@ -29,6 +29,10 @@ private import std.internal.cstring : tempCString; // ////////////////////////////////////////////////////////////////////////// // +public __gshared logToStderr = true; + + +// ////////////////////////////////////////////////////////////////////////// // // use this in `to` to avoid copying public alias SQ3Blob = const(char)[]; // use this in `to` to avoid copying @@ -128,6 +132,99 @@ public void sq3check() (sqlite3* db, int rc, string file=__FILE__, usize line=__ if (rc != SQLITE_OK) throw new SQLiteErr(db, rc, file, line); } +extern(C) { + static usize isSqlError (const(char)* zMsg) { + usize pos = 0; + while (zMsg[pos] != 0 && zMsg[pos] != '\n') { + if (zMsg[pos + 0] == ' ' && zMsg[pos + 1] == 'i' && + zMsg[pos + 2] == 'n' && zMsg[pos + 3] == ' ' && + zMsg[pos + 4] == '"') + { + return pos + 5; + } + pos += 1; + } + return 0; + } + //SQLITE ERROR LOG (1): no such table: packagesz in "INSERT INTO packages + + static void sq3PrintStderrErr (const(char)* pfx, int iErrCode, const(char)* zMsg) { + import core.stdc.stdio : stderr, fprintf, fflush, fputc; + fflush(null); /* flush all output */ + fprintf(stderr, "\r%s (%d): ", pfx, iErrCode); + bool wasNL = false; + usize expos = isSqlError(zMsg); + if (expos) { + fprintf(stderr, "%.*s\n", cast(uint)expos, zMsg); + zMsg += expos; + wasNL = true; + } + while (zMsg[0] != 0) { + usize end = 0; + while (zMsg[end] != 0 && zMsg[end] != '\n') end += 1; + if (end == 0) { + zMsg += 1; + } else { + usize ee = end; + while (ee != 0 && zMsg[ee - 1] <= 32) ee -= 1; + if (ee != 0) { + bool seenBad = false; + usize pos = 0; + while (pos != ee && !seenBad) { + seenBad = zMsg[pos] == 127 || (zMsg[pos] < 32 && zMsg[pos] != 9); + pos += 1; + } + seenBad = true; + if (wasNL) fprintf(stderr, "... "); + if (seenBad) { + pos = 0; + while (pos != ee) { + char ch = zMsg[pos]; + if (ch == 127 || (ch < 32 && ch != 9)) { + fprintf(stderr, "\\x%02x", cast(uint)ch); + } else { + fputc(ch, stderr); + } + pos += 1; + } + fputc('\n', stderr); + } else { + fprintf(stderr, "%.*s\n", cast(uint)ee, zMsg); + } + } + zMsg += end; + if (zMsg[0] == '\n') zMsg += 1; + wasNL = true; + } + } + if (!wasNL) fprintf(stderr, "wtf?!\n"); + } + + static void sq3ErrorLogCallback (void* pArg, int iErrCode, const(char)* zMsg) { + if (!logToStderr) return; + switch (iErrCode) { + case SQLITE_NOTICE: + sq3PrintStderrErr("SQLITE NOTICE", iErrCode, zMsg); + break; + case SQLITE_WARNING: + sq3PrintStderrErr("SQLITE WARNING", iErrCode, zMsg); + break; + case SQLITE_NOTICE_RECOVER_WAL: + sq3PrintStderrErr("SQLITE WAL RECOVER", iErrCode, zMsg); + break; + case SQLITE_NOTICE_RECOVER_ROLLBACK: + sq3PrintStderrErr("SQLITE ROLLBACK RECOVER", iErrCode, zMsg); + break; + case SQLITE_WARNING_AUTOINDEX: + sq3PrintStderrErr("SQLITE AUTOINDEX WARNING", iErrCode, zMsg); + break; + default: + sq3PrintStderrErr("SQLITE ERROR LOG", iErrCode, zMsg); + break; + } + } +} + //////////////////////////////////////////////////////////////////////////////// // WARNING! don't forget to finalize ALL prepared statements! @@ -144,6 +241,8 @@ private: usize udbi = 0; + static __gshared errCBInited = false; + private: @property inout(DBInfo)* dbi () inout pure nothrow @trusted @nogc { pragma(inline, true); return cast(inout(DBInfo)*)udbi; } @property void dbi (DBInfo *adbi) nothrow @trusted @nogc { pragma(inline, true); udbi = cast(usize)adbi; } @@ -259,6 +358,14 @@ public: import std.internal.cstring; immutable bool allowWrite = (mode == Mode.ReadWrite || mode == Mode.ReadWriteCreate); bool allowCreate = (mode == Mode.ReadWriteCreate); + if (!errCBInited) { + // we are interested in all errors + sqlite3_config(SQLITE_CONFIG_LOG, &sq3ErrorLogCallback, null); + errCBInited = true; + if (sqlite3_initialize() != SQLITE_OK) { + throw new Error("cannot initialise SQLite"); + } + } if (allowCreate) { while (schema.length && schema[$-1] <= ' ') schema = schema[0..$-1]; if (schema.length == 0) allowCreate = false; @@ -274,6 +381,7 @@ public: sq3check(null, rc); } scope(failure) { close(); } + if (dbi) sqlite3_extended_result_codes(dbi.db, 1); if (pragmas.length) { execute(pragmas); execute(`PRAGMA trusted_schema = ON;`); -- 2.11.4.GIT