blob: 0d9ca88cdc2e64cecdbebbb2ec7121ac8d0985fd [file] [log] [blame]
Victor Costanab7a2452022-03-21 23:06:081// Copyright 2022 The Chromium Authors. All rights reserved.
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
19namespace sql {
20
21namespace {
22
23// The highly packed representation minimizes binary size and memory usage.
24struct 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
32constexpr 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.
306constexpr 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.
313SqliteResultCodeMappingEntry 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
331SqliteResultCode 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
344SqliteErrorCode 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
Victor Costanf176d242022-03-22 05:31:22362bool 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
Victor Costanab7a2452022-03-21 23:06:08387SqliteLoggedResultCode 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
399void 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
405std::ostream& operator<<(std::ostream& os,
406 SqliteResultCode sqlite_result_code) {
407 return os << static_cast<int>(sqlite_result_code);
408}
409
410std::ostream& operator<<(std::ostream& os, SqliteErrorCode sqlite_error_code) {
411 return os << static_cast<SqliteResultCode>(sqlite_error_code);
412}
413
414void 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