blob: 21f7be03076b4dc53f12342f8105f05a4b66a71d [file] [log] [blame]
Avi Drissman69b874f2022-09-15 19:11:141// Copyright 2022 The Chromium Authors
Victor Costanab7a2452022-03-21 23:06:082// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "sql/sqlite_result_code.h"
6
7#include <ostream> // Needed to compile NOTREACHED() with operator <<.
8#include <set>
Will Harrisb60653cc2023-06-13 22:32:239#include <string>
Md Hasibul Hasan52ffeca62024-03-26 18:23:1810#include <string_view>
Victor Costanab7a2452022-03-21 23:06:0811#include <utility>
12
13#include "base/check_op.h"
14#include "base/metrics/histogram_functions.h"
15#include "base/notreached.h"
16#include "base/ranges/algorithm.h"
Victor Costanab7a2452022-03-21 23:06:0817#include "sql/sqlite_result_code_values.h"
18#include "third_party/sqlite/sqlite3.h"
19
20namespace sql {
21
22namespace {
23
24// The highly packed representation minimizes binary size and memory usage.
25struct SqliteResultCodeMappingEntry {
26 unsigned result_code : 16;
27 unsigned logged_code : 8;
28
29 // The remaining bits will be used to encode the result values of helpers that
30 // indicate corruption handling.
31};
32
33constexpr SqliteResultCodeMappingEntry kResultCodeMapping[] = {
34 // Entries are ordered by SQLite result code value. This should match the
35 // ordering in https://www.sqlite.org/rescode.html.
36
37 {SQLITE_OK, static_cast<int>(SqliteLoggedResultCode::kNoError)},
38 {SQLITE_ERROR, static_cast<int>(SqliteLoggedResultCode::kGeneric)},
39 {SQLITE_INTERNAL, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
40 {SQLITE_PERM, static_cast<int>(SqliteLoggedResultCode::kPermission)},
41 {SQLITE_ABORT, static_cast<int>(SqliteLoggedResultCode::kAbort)},
42 {SQLITE_BUSY, static_cast<int>(SqliteLoggedResultCode::kBusy)},
43
44 // Chrome features shouldn't execute conflicting statements concurrently.
45 {SQLITE_LOCKED, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
46
47 // Chrome should crash on OOM.
48 {SQLITE_NOMEM, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
49
50 {SQLITE_READONLY, static_cast<int>(SqliteLoggedResultCode::kReadOnly)},
51
52 // Chrome doesn't use sqlite3_interrupt().
53 {SQLITE_INTERRUPT, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
54
55 {SQLITE_IOERR, static_cast<int>(SqliteLoggedResultCode::kIo)},
56 {SQLITE_CORRUPT, static_cast<int>(SqliteLoggedResultCode::kCorrupt)},
57
58 // Chrome only passes a few known-good opcodes to sqlite3_file_control().
59 {SQLITE_NOTFOUND, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
60
61 {SQLITE_FULL, static_cast<int>(SqliteLoggedResultCode::kFullDisk)},
62 {SQLITE_CANTOPEN, static_cast<int>(SqliteLoggedResultCode::kCantOpen)},
63 {SQLITE_PROTOCOL,
64 static_cast<int>(SqliteLoggedResultCode::kLockingProtocol)},
65 {SQLITE_EMPTY, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
66 {SQLITE_SCHEMA, static_cast<int>(SqliteLoggedResultCode::kSchemaChanged)},
67 {SQLITE_TOOBIG, static_cast<int>(SqliteLoggedResultCode::kTooBig)},
68 {SQLITE_CONSTRAINT, static_cast<int>(SqliteLoggedResultCode::kConstraint)},
69 {SQLITE_MISMATCH, static_cast<int>(SqliteLoggedResultCode::kTypeMismatch)},
70
71 // Chrome should not misuse SQLite's API.
72 {SQLITE_MISUSE, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
73
74 {SQLITE_NOLFS,
75 static_cast<int>(SqliteLoggedResultCode::kNoLargeFileSupport)},
76
77 // Chrome does not set an authorizer callback.
78 {SQLITE_AUTH, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
79
80 {SQLITE_FORMAT, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
81
82 // Chrome should not use invalid column indexes in sqlite3_{bind,column}*().
83 {SQLITE_RANGE, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
84
85 {SQLITE_NOTADB, static_cast<int>(SqliteLoggedResultCode::kNotADatabase)},
86 {SQLITE_NOTICE, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
87 {SQLITE_WARNING, static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
88 {SQLITE_ROW, static_cast<int>(SqliteLoggedResultCode::kNoError)},
89 {SQLITE_DONE, static_cast<int>(SqliteLoggedResultCode::kNoError)},
90 {SQLITE_OK_LOAD_PERMANENTLY,
91 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
92
93 // Chrome should not use collating sequence names in SQL statements.
94 {SQLITE_ERROR_MISSING_COLLSEQ,
95 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
96
97 {SQLITE_BUSY_RECOVERY,
98 static_cast<int>(SqliteLoggedResultCode::kBusyRecovery)},
99
100 // Chrome does not use a shared page cache.
101 {SQLITE_LOCKED_SHAREDCACHE,
102 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
103
104 {SQLITE_READONLY_RECOVERY,
105 static_cast<int>(SqliteLoggedResultCode::kReadOnlyRecovery)},
106 {SQLITE_IOERR_READ, static_cast<int>(SqliteLoggedResultCode::kIoRead)},
107
108 // Chrome does not use a virtual table that signals corruption. We only use
109 // a
110 // virtual table code for recovery. That code does not use this error.
111 {SQLITE_CORRUPT_VTAB,
112 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
113
114 {SQLITE_CANTOPEN_NOTEMPDIR,
115 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
116 {SQLITE_CONSTRAINT_CHECK,
117 static_cast<int>(SqliteLoggedResultCode::kConstraintCheck)},
118
119 // Chrome does not set an authorizer callback.
120 {SQLITE_AUTH_USER, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
121
122 {SQLITE_NOTICE_RECOVER_WAL,
123 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
124 {SQLITE_WARNING_AUTOINDEX,
125 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
126 {SQLITE_ERROR_RETRY,
127 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
128 {SQLITE_ABORT_ROLLBACK,
129 static_cast<int>(SqliteLoggedResultCode::kAbortRollback)},
130 {SQLITE_BUSY_SNAPSHOT,
131 static_cast<int>(SqliteLoggedResultCode::kBusySnapshot)},
132
133 // Chrome does not use a virtual table that signals conflicts. We only use a
134 // virtual table code for recovery. That code does not use this error.
135 {SQLITE_LOCKED_VTAB,
136 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
137
138 {SQLITE_READONLY_CANTLOCK,
139 static_cast<int>(SqliteLoggedResultCode::kReadOnlyCantLock)},
140 {SQLITE_IOERR_SHORT_READ,
141 static_cast<int>(SqliteLoggedResultCode::kIoShortRead)},
142 {SQLITE_CORRUPT_SEQUENCE,
143 static_cast<int>(SqliteLoggedResultCode::kCorruptSequence)},
144 {SQLITE_CANTOPEN_ISDIR,
145 static_cast<int>(SqliteLoggedResultCode::kCantOpenIsDir)},
146
147 // Chrome does not use commit hook callbacks.
148 {SQLITE_CONSTRAINT_COMMITHOOK,
149 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
150
151 {SQLITE_NOTICE_RECOVER_ROLLBACK,
152 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
153
154 // Chrome does not use sqlite3_snapshot_open().
155 {SQLITE_ERROR_SNAPSHOT,
156 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
157#ifdef SQLITE_ENABLE_SNAPSHOT
158#error "This code assumes that Chrome does not use sqlite3_snapshot_open()"
159#endif
160
161 // Chrome does not use blocking Posix advisory file lock requests.
162 {SQLITE_BUSY_TIMEOUT,
163 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
164#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
Ivan Murashov3c7a0d52022-10-27 21:21:47165#error "This code assumes that Chrome does not use blocking Posix advisory \
166file lock requests"
Victor Costanab7a2452022-03-21 23:06:08167#endif
168
169 {SQLITE_READONLY_ROLLBACK,
170 static_cast<int>(SqliteLoggedResultCode::kReadOnlyRollback)},
171 {SQLITE_IOERR_WRITE, static_cast<int>(SqliteLoggedResultCode::kIoWrite)},
172 {SQLITE_CORRUPT_INDEX,
173 static_cast<int>(SqliteLoggedResultCode::kCorruptIndex)},
174
175 // Chrome should always pass full paths to SQLite.
176 {SQLITE_CANTOPEN_FULLPATH,
177 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
178
179 {SQLITE_CONSTRAINT_FOREIGNKEY,
180 static_cast<int>(SqliteLoggedResultCode::kConstraintForeignKey)},
181 {SQLITE_READONLY_DBMOVED,
182 static_cast<int>(SqliteLoggedResultCode::kReadOnlyDbMoved)},
183 {SQLITE_IOERR_FSYNC, static_cast<int>(SqliteLoggedResultCode::kIoFsync)},
184
185 // Chrome does not support Cygwin and does not use its VFS.
186 {SQLITE_CANTOPEN_CONVPATH,
187 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
188
189 // Chrome does not use extension functions.
190 {SQLITE_CONSTRAINT_FUNCTION,
191 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
192
193 {SQLITE_READONLY_CANTINIT,
194 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
195 {SQLITE_IOERR_DIR_FSYNC,
196 static_cast<int>(SqliteLoggedResultCode::kIoDirFsync)},
197 {SQLITE_CANTOPEN_DIRTYWAL,
198 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
199 {SQLITE_CONSTRAINT_NOTNULL,
200 static_cast<int>(SqliteLoggedResultCode::kConstraintNotNull)},
201 {SQLITE_READONLY_DIRECTORY,
202 static_cast<int>(SqliteLoggedResultCode::kReadOnlyDirectory)},
203 {SQLITE_IOERR_TRUNCATE,
204 static_cast<int>(SqliteLoggedResultCode::kIoTruncate)},
205
206 // Chrome does not use the SQLITE_OPEN_NOFOLLOW flag.
207 {SQLITE_CANTOPEN_SYMLINK,
208 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
209
210 {SQLITE_CONSTRAINT_PRIMARYKEY,
211 static_cast<int>(SqliteLoggedResultCode::kConstraintPrimaryKey)},
212 {SQLITE_IOERR_FSTAT, static_cast<int>(SqliteLoggedResultCode::kIoFstat)},
213
214 // Chrome unconditionally disables database triggers via
215 // sqlite3_db_config(SQLITE_DBCONFIG_ENABLE_TRIGGER).
216 {SQLITE_CONSTRAINT_TRIGGER,
217 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
218
219 {SQLITE_IOERR_UNLOCK, static_cast<int>(SqliteLoggedResultCode::kIoUnlock)},
220 {SQLITE_CONSTRAINT_UNIQUE,
221 static_cast<int>(SqliteLoggedResultCode::kConstraintUnique)},
222 {SQLITE_IOERR_RDLOCK,
223 static_cast<int>(SqliteLoggedResultCode::kIoReadLock)},
224
225 // Chrome does not use a virtual table that signals constraints. We only use
226 // a virtual table code for recovery. That code does not use this error.
227 {SQLITE_CONSTRAINT_VTAB,
228 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
229
230 {SQLITE_IOERR_DELETE, static_cast<int>(SqliteLoggedResultCode::kIoDelete)},
231 {SQLITE_CONSTRAINT_ROWID,
232 static_cast<int>(SqliteLoggedResultCode::kConstraintRowId)},
233 {SQLITE_IOERR_BLOCKED,
234 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
235
236 // Chrome unconditionally disables database triggers via
237 // sqlite3_db_config(SQLITE_DBCONFIG_ENABLE_TRIGGER).
238 {SQLITE_CONSTRAINT_PINNED,
239 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
240
241 // The SQLite docus claim that this error code is "normally" converted to
242 // SQLITE_NOMEM. This doesn't seem 100% categorical, so we're flagging this
243 // as "unused in Chrome" per the same rationale as SQLITE_NOMEM.
244 {SQLITE_IOERR_NOMEM,
245 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
246
247 {SQLITE_CONSTRAINT_DATATYPE,
248 static_cast<int>(SqliteLoggedResultCode::kConstraintDataType)},
249 {SQLITE_IOERR_ACCESS, static_cast<int>(SqliteLoggedResultCode::kIoAccess)},
250 {SQLITE_IOERR_CHECKRESERVEDLOCK,
251 static_cast<int>(SqliteLoggedResultCode::kIoCheckReservedLock)},
252 {SQLITE_IOERR_LOCK, static_cast<int>(SqliteLoggedResultCode::kIoLock)},
253 {SQLITE_IOERR_CLOSE, static_cast<int>(SqliteLoggedResultCode::kIoClose)},
254 {SQLITE_IOERR_DIR_CLOSE,
255 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
256
257 // Chrome will only allow enabling WAL on databases with exclusive locking.
258 {SQLITE_IOERR_SHMOPEN,
259 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
260
261 // Chrome will only allow enabling WAL on databases with exclusive locking.
262 {SQLITE_IOERR_SHMSIZE,
263 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
264
265 {SQLITE_IOERR_SHMLOCK,
266 static_cast<int>(SqliteLoggedResultCode::kUnusedSqlite)},
267
268 // Chrome will only allow enabling WAL on databases with exclusive locking.
269 {SQLITE_IOERR_SHMMAP,
270 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
271
272 {SQLITE_IOERR_SEEK, static_cast<int>(SqliteLoggedResultCode::kIoSeek)},
273 {SQLITE_IOERR_DELETE_NOENT,
274 static_cast<int>(SqliteLoggedResultCode::kIoDeleteNoEntry)},
275 {SQLITE_IOERR_MMAP,
276 static_cast<int>(SqliteLoggedResultCode::kIoMemoryMapping)},
277 {SQLITE_IOERR_GETTEMPPATH,
278 static_cast<int>(SqliteLoggedResultCode::kIoGetTemporaryPath)},
279
280 // Chrome does not support Cygwin and does not use its VFS.
281 {SQLITE_IOERR_CONVPATH,
282 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
283
284 // Chrome does not use SQLite extensions.
285 {SQLITE_IOERR_VNODE,
286 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
287
288 // Chrome does not use SQLite extensions.
289 {SQLITE_IOERR_AUTH,
290 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
291
292 {SQLITE_IOERR_BEGIN_ATOMIC,
293 static_cast<int>(SqliteLoggedResultCode::kIoBeginAtomic)},
294 {SQLITE_IOERR_COMMIT_ATOMIC,
295 static_cast<int>(SqliteLoggedResultCode::kIoCommitAtomic)},
296 {SQLITE_IOERR_ROLLBACK_ATOMIC,
297 static_cast<int>(SqliteLoggedResultCode::kIoRollbackAtomic)},
298
299 // Chrome does not use the checksum VFS shim.
300 {SQLITE_IOERR_DATA,
301 static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)},
302
303 {SQLITE_IOERR_CORRUPTFS,
304 static_cast<int>(SqliteLoggedResultCode::kIoCorruptFileSystem)},
305};
306
307// Describes the handling of unknown SQLite error codes.
308constexpr SqliteResultCodeMappingEntry kUnknownResultCodeMappingEntry = {
309 0, static_cast<int>(SqliteLoggedResultCode::kUnusedChrome)};
310
311// Looks up a `sqlite_result_code` in the mapping tables.
312//
313// Returns an entry in kResultCodeMapping or kUnknownResultCodeMappingEntry.
314// DCHECKs if the `sqlite_result_code` is not in the mapping table.
315SqliteResultCodeMappingEntry FindResultCode(int sqlite_result_code) {
316 const auto* mapping_it = base::ranges::find_if(
317 kResultCodeMapping,
318 [&sqlite_result_code](SqliteResultCodeMappingEntry rhs) {
319 return sqlite_result_code == rhs.result_code;
320 });
321
322 if (mapping_it == base::ranges::end(kResultCodeMapping)) {
323 NOTREACHED() << "Unsupported SQLite result code: " << sqlite_result_code;
324 return kUnknownResultCodeMappingEntry;
325 }
326 return *mapping_it;
327}
328
329} // namespace
330
331#if DCHECK_IS_ON()
332
333SqliteResultCode ToSqliteResultCode(int sqlite_result_code) {
334 SqliteLoggedResultCode logged_code = static_cast<SqliteLoggedResultCode>(
335 FindResultCode(sqlite_result_code).logged_code);
336
337 DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
338 << "SQLite reported code marked for internal use: " << sqlite_result_code;
339 DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedChrome)
340 << "SQLite reported code that should never show up in Chrome: "
341 << sqlite_result_code;
342
343 return static_cast<SqliteResultCode>(sqlite_result_code);
344}
345
346SqliteErrorCode ToSqliteErrorCode(SqliteResultCode sqlite_error_code) {
347 SqliteLoggedResultCode logged_code = static_cast<SqliteLoggedResultCode>(
348 FindResultCode(static_cast<int>(sqlite_error_code)).logged_code);
349
350 DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
351 << "SQLite reported code marked for internal use: " << sqlite_error_code;
352 DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedChrome)
353 << "SQLite reported code that should never show up in Chrome: "
354 << sqlite_error_code;
355 DCHECK_NE(logged_code, SqliteLoggedResultCode::kNoError)
356 << __func__
357 << " called with non-error result code: " << sqlite_error_code;
358
359 return static_cast<SqliteErrorCode>(sqlite_error_code);
360}
361
362#endif // DCHECK_IS_ON()
363
Victor Costanf176d242022-03-22 05:31:22364bool IsSqliteSuccessCode(SqliteResultCode sqlite_result_code) {
365 // https://www.sqlite.org/rescode.html lists the result codes that are not
366 // errors.
367 bool is_success = (sqlite_result_code == SqliteResultCode::kOk) ||
368 (sqlite_result_code == SqliteResultCode::kRow) ||
369 (sqlite_result_code == SqliteResultCode::kDone);
370
371#if DCHECK_IS_ON()
372 SqliteLoggedResultCode logged_code = static_cast<SqliteLoggedResultCode>(
373 FindResultCode(static_cast<int>(sqlite_result_code)).logged_code);
374
375 DCHECK_EQ(is_success, logged_code == SqliteLoggedResultCode::kNoError)
376 << __func__ << " logic disagrees with the code mapping for "
377 << sqlite_result_code;
378
379 DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
380 << "SQLite reported code marked for internal use: " << sqlite_result_code;
381 DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedChrome)
382 << "SQLite reported code that should never show up in Chrome: "
383 << sqlite_result_code;
384#endif // DCHECK_IS_ON()
385
386 return is_success;
387}
388
Victor Costanab7a2452022-03-21 23:06:08389SqliteLoggedResultCode ToSqliteLoggedResultCode(int sqlite_result_code) {
390 SqliteLoggedResultCode logged_code = static_cast<SqliteLoggedResultCode>(
391 FindResultCode(sqlite_result_code).logged_code);
392
393 DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
394 << "SQLite reported code marked for internal use: " << sqlite_result_code;
395 DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedChrome)
396 << "SQLite reported code that should never show up in Chrome: "
397 << sqlite_result_code;
398 return logged_code;
399}
400
Will Harrisb60653cc2023-06-13 22:32:23401void UmaHistogramSqliteResult(const std::string& histogram_name,
Victor Costanab7a2452022-03-21 23:06:08402 int sqlite_result_code) {
403 auto logged_code = ToSqliteLoggedResultCode(sqlite_result_code);
404 base::UmaHistogramEnumeration(histogram_name, logged_code);
405}
406
407std::ostream& operator<<(std::ostream& os,
408 SqliteResultCode sqlite_result_code) {
409 return os << static_cast<int>(sqlite_result_code);
410}
411
412std::ostream& operator<<(std::ostream& os, SqliteErrorCode sqlite_error_code) {
413 return os << static_cast<SqliteResultCode>(sqlite_error_code);
414}
415
416void CheckSqliteLoggedResultCodeForTesting() {
417 // Ensure that error codes are alphabetical.
418 const auto* unordered_it = base::ranges::adjacent_find(
419 kResultCodeMapping,
420 [](SqliteResultCodeMappingEntry lhs, SqliteResultCodeMappingEntry rhs) {
421 return lhs.result_code >= rhs.result_code;
422 });
423 DCHECK_EQ(unordered_it, base::ranges::end(kResultCodeMapping))
424 << "Mapping ordering broken at {" << unordered_it->result_code << ", "
425 << static_cast<int>(unordered_it->logged_code) << "}";
426
427 std::set<int> sqlite_result_codes;
428 for (auto& mapping_entry : kResultCodeMapping)
429 sqlite_result_codes.insert(mapping_entry.result_code);
430
431 // SQLite doesn't have special messages for extended errors.
432 // At the time of this writing, sqlite3_errstr() has a string table for
433 // primary result codes, and uses it for extended error codes as well.
434 //
435 // So, we can only use sqlite3_errstr() to check for holes in the primary
436 // message table.
437 for (int result_code = 0; result_code <= 256; ++result_code) {
438 if (sqlite_result_codes.count(result_code) != 0)
439 continue;
440
441 const char* error_message = sqlite3_errstr(result_code);
442
Md Hasibul Hasan52ffeca62024-03-26 18:23:18443 static constexpr std::string_view kUnknownErrorMessage("unknown error");
Victor Costanab7a2452022-03-21 23:06:08444 DCHECK_EQ(kUnknownErrorMessage.compare(error_message), 0)
445 << "Unmapped SQLite result code: " << result_code
446 << " SQLite message: " << error_message;
447 }
448
449 // Number of #defines in https://www.sqlite.org/c3ref/c_abort.html
450 //
451 // This number is also stated at
452 // https://www.sqlite.org/rescode.html#primary_result_code_list
453 static constexpr int kPrimaryResultCodes = 31;
454
455 // Number of #defines in https://www.sqlite.org/c3ref/c_abort_rollback.html
456 //
457 // This number is also stated at
458 // https://www.sqlite.org/rescode.html#extended_result_code_list
459 static constexpr int kExtendedResultCodes = 74;
460
461 DCHECK_EQ(std::size(kResultCodeMapping),
462 size_t{kPrimaryResultCodes + kExtendedResultCodes})
463 << "Mapping table has incorrect number of entries";
464}
465
466} // namespace sql