The Use Of assert() In SQLite
Small. Fast. Reliable.<br>Choose any three.
Home<br>Menu<br>About<br>Documentation<br>Download<br>License<br>Support<br>Purchase
Search
About<br>Documentation<br>Download<br>Support<br>Purchase
Search Documentation<br>Search Changelog
The Use Of assert() In SQLite
Table Of Contents<br>1. Assert() And Similar Macros In SQLite
1.1. Philosophy of assert()
1.2. Different Behaviors According To Build Type
2. Examples
1. Assert() And Similar Macros In SQLite
The assert(X) macro is<br>part of standard C, in the<br>header file.<br>SQLite adds three other assert()-like macros named NEVER(X), ALWAYS(X),<br>and testcase(X).
assert(X) →<br>The assert(X) statement indicates that the condition X is always true.<br>In other words, X is an invariant. The assert(X) macro works like a<br>procedure in that it has no return value.
ALWAYS(X) →<br>The ALWAYS(X) function indicates that condition X is always true as far<br>as the developers know, but there is no proof the X is true, or the<br>proof is complex and error-prone, or the proof depends on implementation<br>details that are likely to change in the future. ALWAYS(X) behaves like<br>a function that returns the boolean value X, and is intended to be used<br>within the conditional of an "if" statement.
NEVER(X) →<br>The NEVER(X) function indicates that condition X is never true. This<br>is the negative analog of the ALWAYS(X) function.
testcase(X) →<br>The testcase(X) statement indicates that X is sometimes true and sometimes<br>false. In other words, testcase(X) indicates that X is definitely not an<br>invariant. Since SQLite uses 100% MC/DC testing, the presence of a<br>testcase(X) macro indicates that not only is it possible for X to be either<br>true or false, but there are test cases to demonstrate this.
SQLite version 3.22.0 (2018-01-22) contains 5290 assert() macros,<br>839 testcase() macros, 88 ALWAYS() macros, and 63 NEVER() macros.
1.1. Philosophy of assert()
In SQLite, the presence of assert(X) means that the developers have<br>a proof that X is always true. Readers can depend upon X being true to<br>help them reason about the code. An assert(X) is a strong statement<br>about the truth of X. There is no doubt.
The ALWAYS(X) and NEVER(X) macros are a weaker statement about the<br>truth of X. The presence of ALWAYS(X) or NEVER(X) means that the developers<br>believe X is always or never true, but there is no proof, or the proof<br>is complex and error-prone, or the proof depends on other aspects<br>of the system that seem likely to change.
Other systems sometimes use assert(X) in a way that is<br>similar to the use of ALWAYS(X) or NEVER(X) in SQLite.<br>Developers will add an assert(X) as a<br>tacit acknowledgement that they<br>do not fully believe that X is always true.<br>We believe that this use of assert(X) is wrong and violates the intent<br>and purpose of having assert(X) available in C in the first place.<br>An assert(X) should not be seen as a safety-net or top-rope used to<br>guard against mistakes. Nor is assert(X) appropriate for defense-in-depth.<br>An ALWAYS(X) or NEVER(X) macro, or something similar, should be used in<br>those cases because ALWAYS(X) or NEVER(X) will be followed by code to<br>actually deal with the problem when the programmers reasoning<br>turns out to be wrong. Since the code that follows ALWAYS(X) or NEVER(X)<br>is untested, it should be something very simple, like a "return" statement,<br>that is easily verified by inspection.
Because assert() can be and is commonly misused, some programming language<br>theorists and designers look upon it with disfavor.<br>For example, the designers of the Go programming language<br>intentionally omit a built-in assert().<br>They feel that the harm caused by misuse of assert()<br>outweighs the benefits of including it as a language built-in.<br>The SQLite developers disagree. In fact, the original purpose of this<br>article is to push back against the common notion that assert() is harmful.<br>In our experience, SQLite would be much more difficult to develop, test,<br>and maintain without assert().
1.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<br>that the test suite provides 100% MC/DC.
The release build is used to validate the generated machine code.
All tests must give the same answer in all three<br>builds. See the "How SQLite Is Tested" document for more detail.
The various assert()-like<br>macros behave differently according to how SQLite is built.
Functionality TestingCoverage TestingRelease<br>assert(X)<br>abort() if X is false<br>no-op<br>no-op
ALWAYS(X)<br>abort() if X is false<br>always true<br>pass through the value X
NEVER(X)<br>abort() if X is true<br>always false<br>pass through the value X
testcase(X)<br>no-op<br>do some harmless work if X is true<br>no-op
The default behavior of assert(X) in standard C is that it is enabled<br>for release builds. This is a reasonable default. However, the<br>SQLite code base has many assert() statements in...