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