Small. Fast. Reliable.
Choose any three.
The Use Of assert() In SQLite

1. Work In Progress - Do Not Link!

This document is a work-in-progress and is not yet ready for release. Please do not link to it, yet.

2. Assert() And Other Assert()-like Macros In SQLite

The assert(X) macro is part of standard C, in the the <assert.h> header file. SQLite adds three other assert()-like macros named NEVER(X), ALWAYS(X), and testcase(X).

SQLite version 3.22.0 (2018-01-22) contains 5290 assert() macros, 839 testcase() macros, 88 ALWAYS() macros, and 63 NEVER() macros.

2.1. Philosophy of assert()

In SQLite, the presence of assert(X) means that the developers have a proof that X is always true. Readers can depend upon X being true to help them reason about the code. An assert(X) is a strong statement about the truth of X. There is no doubt.

The ALWAYS(X) and NEVER(X) macros are a weaker statement about the truth of X. The presence of ALWAYS(X) or NEVER(X) means that the developers believe X is always or never true, but there is no proof, or the proof is complex and error-prone, or the proof depends on other aspects of the system that seem likely to change.

In other systems, developers sometimes use assert(X) in a way that is similar to the use of ALWAYS(X) in SQLite. Developers will add an assert(X) as a tacit acknowledgement that they do not fully believe that X is always true. We believe that this use of assert(X) is wrong and violates the intent and purpose of having assert(X) available in C in the first place. An assert(X) should not be seen as a safety-net or top-rope used to guard against mistakes. Nor is assert(X) appropriate for defense-in-depth. An ALWAYS(X) macro, or something similar, should be used in those cases because ALWAYS(X) is followed by code to actually deal with the problem.

The Go programming language omits assert(). The Go developers recognize this is contentious. Disallowing assert() is essentially telling developers that they are not allowed to expression invariants. It is as if the developers of Go do not want coders to prove that the software is correct. The SQLite developers believe that the lack of assert() disqualifies Go as a language for serious development work.

2.2. Different Behaviors According To Build Type

Three separate builds are used to validate the SQLite software. A functionality testing build is used to validate the source code. A coverage testing build is used to validate the test suite, to confirm that the test suite provides 100% MC/DC. The release build is used to validate the machine code. All tests must give the same answer in all three builds. See the "How SQLite Is Tested" document for more detail.

The assert() macros behave differently according to how SQLite is built.

Functionality TestingCoverage TestingRelease
assert(X) abort() if X is false no-op no-op
ALWAYS(X) abort() if X is false always true pass through the value X
NEVER(X) abort() if X is true always false pass through the value X
testcase(X) no-op do some harmless work if X is true no-op

The default behavior of assert(X) in standard C is that it is enabled for release builds. We find this acceptable in general. However, the SQLite code base has many assert() statements in performance-sensitive areas of the code. Leaving assert(X) turned causes SQLite to run about three times slower. Also, SQLite strives to provide 100% MC/DC in an as-delivered configuration, which is obviously impossible if assert(X) statements are enabled. For these reasons, assert(X) is a no-op for release builds in SQLite.