diff --git a/datastore/sqlite.nim b/datastore/sqlite.nim index 52901a6..6054ab9 100644 --- a/datastore/sqlite.nim +++ b/datastore/sqlite.nim @@ -136,9 +136,26 @@ template journalModePragmaStmt*(env: SQLite): RawStmtPtr = s.dispose return failure $sqlite3_errstr(x) - if (let x = $sqlite3_column_text(s, 0).cstring; x != "memory" and x != "wal"): + let + x = sqlite3_column_text(s, 0).cstring + + # detect out-of-memory error + # see the conversion table and final paragraph of: + # https://www.sqlite.org/c3ref/column_blob.html + # see also https://www.sqlite.org/rescode.html + + # in order to detect an out-of-memory error check that the result is a null + # pointer and that the result code is an error code + if x.isNil: + let + code = sqlite3_errcode(sqlite3_db_handle(s)) + + if not (code in [SQLITE_OK, SQLITE_ROW, SQLITE_DONE]): + raise (ref Defect)(msg: $sqlite3_errstr(code)) + + if not ($x in ["memory", "wal"]): s.dispose - return failure "Invalid pragma result: " & $x + return failure "Invalid pragma result: \"" & $x & "\"" s diff --git a/datastore/sqlite_datastore.nim b/datastore/sqlite_datastore.nim index 40d3535..86c1775 100644 --- a/datastore/sqlite_datastore.nim +++ b/datastore/sqlite_datastore.nim @@ -185,7 +185,22 @@ proc idCol*( s: RawStmtPtr, index = 0): string = - $sqlite3_column_text(s, index.cint).cstring + let + text = sqlite3_column_text(s, index.cint).cstring + + # detect out-of-memory error + # see the conversion table and final paragraph of: + # https://www.sqlite.org/c3ref/column_blob.html + + # the "id" column is NOT NULL PRIMARY KEY so an out-of-memory error can be + # inferred from a null pointer result + if text.isNil: + let + code = sqlite3_errcode(sqlite3_db_handle(s)) + + raise (ref Defect)(msg: $sqlite3_errstr(code)) + + $text proc dataCol*( s: RawStmtPtr, @@ -193,9 +208,36 @@ proc dataCol*( let i = index.cint - dataBytes = cast[ptr UncheckedArray[byte]](sqlite3_column_blob(s, i)) + blob = sqlite3_column_blob(s, i) + + # detect out-of-memory error + # see the conversion table and final paragraph of: + # https://www.sqlite.org/c3ref/column_blob.html + # see also https://www.sqlite.org/rescode.html + + # the "data" column can be NULL so in order to detect an out-of-memory error + # it is necessary to check that the result is a null pointer and that the + # result code is an error code + if blob.isNil: + let + code = sqlite3_errcode(sqlite3_db_handle(s)) + + if not (code in [SQLITE_OK, SQLITE_ROW, SQLITE_DONE]): + raise (ref Defect)(msg: $sqlite3_errstr(code)) + + let dataLen = sqlite3_column_bytes(s, i) + # an out-of-memory error can be inferred from a null pointer result + if (unsafeAddr dataLen).isNil: + let + code = sqlite3_errcode(sqlite3_db_handle(s)) + + raise (ref Defect)(msg: $sqlite3_errstr(code)) + + let + dataBytes = cast[ptr UncheckedArray[byte]](blob) + @(toOpenArray(dataBytes, 0, dataLen - 1)) proc timestampCol*(