This document is a work-in-progress and is not yet ready for release. Please do not link to it, yet.
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).
assert(X) → The assert(X) statement indicates that the condition X is always true. In other words, X is an invariant. The assert(X) macro works like a procedure in that it has no return value.
ALWAYS(X) → The ALWAYS(X) function indicates that condition X is always true as far as the developers know, but there is not proof the X is true, or the proof is complex and error-prone, or the proof depends on implementation details that are likely to change in the future. ALWAYS(X) behaves like a function that returns the boolean value X, and is intended to be used within the conditional of an "if" statement.
NEVER(X) → The NEVER(X) function indicates that condition X is never true. This is the negative analog of the ALWAYS(X) function.
testcase(X) → The testcase(X) statement indicates that X is sometimes true and sometimes false. In other words, testcase(X) indicates that X is definitely not an invariant. Since SQLite uses 100% MC/DC testing, the presence of a testcase(X) macro indicates that not only is it possible for X to be either true or false, but there are test cases to demonstrate this.
SQLite version 3.22.0 (2018-01-22) contains 5290 assert() macros, 839 testcase() macros, 88 ALWAYS() macros, and 63 NEVER() macros.
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.
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 Testing||Coverage Testing||Release|
|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.