Add declaration-level assertions for compile-time checks
authorMichael Paquier
Mon, 3 Feb 2020 05:48:42 +0000 (14:48 +0900)
committerMichael Paquier
Mon, 3 Feb 2020 05:48:42 +0000 (14:48 +0900)
Those new assertions can be used at file scope, outside of any function
for compilation checks.  This commit provides implementations for C and
C++, and fallback implementations.

Author: Peter Smith
Reviewed-by: Andres Freund, Kyotaro Horiguchi, Dagfinn Ilmari Mannsåker,
Michael Paquier
Discussion: https://postgr.es/m/201DD0641B056142AC8C6645EC1B5F62014B8E8030@SYD1217

src/backend/storage/page/bufpage.c
src/backend/utils/adt/lockfuncs.c
src/backend/utils/misc/guc.c
src/bin/pg_dump/pg_dump_sort.c
src/common/relpath.c
src/include/c.h
src/include/storage/bufpage.h

index f47176753d910c402002545d1f5ce66fb4bfb929..4ea6ea7a3d91fbb738661e3a793d52d41e885da2 100644 (file)
@@ -119,14 +119,7 @@ PageIsVerified(Page page, BlockNumber blkno)
            return true;
    }
 
-   /*
-    * Check all-zeroes case. Luckily BLCKSZ is guaranteed to always be a
-    * multiple of size_t - and it's much faster to compare memory using the
-    * native word size.
-    */
-   StaticAssertStmt(BLCKSZ == (BLCKSZ / sizeof(size_t)) * sizeof(size_t),
-                    "BLCKSZ has to be a multiple of sizeof(size_t)");
-
+   /* Check all-zeroes case */
    all_zeroes = true;
    pagebytes = (size_t *) page;
    for (i = 0; i < (BLCKSZ / sizeof(size_t)); i++)
index 9a7da12a5b79a7d049b141655d0225b5c0e77d90..7e47ebeb6f6725935a7587baaa75c81aa77f5b37 100644 (file)
@@ -36,6 +36,9 @@ const char *const LockTagTypeNames[] = {
    "advisory"
 };
 
+StaticAssertDecl(lengthof(LockTagTypeNames) == (LOCKTAG_ADVISORY + 1),
+                "array length mismatch");
+
 /* This must match enum PredicateLockTargetType (predicate_internals.h) */
 static const char *const PredicateLockTagTypeNames[] = {
    "relation",
@@ -43,6 +46,9 @@ static const char *const PredicateLockTagTypeNames[] = {
    "tuple"
 };
 
+StaticAssertDecl(lengthof(PredicateLockTagTypeNames) == (PREDLOCKTAG_TUPLE + 1),
+                "array length mismatch");
+
 /* Working status for pg_lock_status */
 typedef struct
 {
index a16fe8cd5bf1c93098576f86f3ccd0310f059236..9630866a5f90dad9380471ef01576805515841cd 100644 (file)
@@ -241,6 +241,9 @@ static const struct config_enum_entry bytea_output_options[] = {
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(bytea_output_options) == (BYTEA_OUTPUT_HEX + 2),
+                "array length mismatch");
+
 /*
  * We have different sets for client and server message level options because
  * they sort slightly different (see "log" level), and because "fatal"/"panic"
@@ -286,6 +289,9 @@ static const struct config_enum_entry intervalstyle_options[] = {
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(intervalstyle_options) == (INTSTYLE_ISO_8601 + 2),
+                "array length mismatch");
+
 static const struct config_enum_entry log_error_verbosity_options[] = {
    {"terse", PGERROR_TERSE, false},
    {"default", PGERROR_DEFAULT, false},
@@ -293,6 +299,9 @@ static const struct config_enum_entry log_error_verbosity_options[] = {
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(log_error_verbosity_options) == (PGERROR_VERBOSE + 2),
+                "array length mismatch");
+
 static const struct config_enum_entry log_statement_options[] = {
    {"none", LOGSTMT_NONE, false},
    {"ddl", LOGSTMT_DDL, false},
@@ -301,6 +310,9 @@ static const struct config_enum_entry log_statement_options[] = {
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(log_statement_options) == (LOGSTMT_ALL + 2),
+                "array length mismatch");
+
 static const struct config_enum_entry isolation_level_options[] = {
    {"serializable", XACT_SERIALIZABLE, false},
    {"repeatable read", XACT_REPEATABLE_READ, false},
@@ -316,6 +328,9 @@ static const struct config_enum_entry session_replication_role_options[] = {
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(session_replication_role_options) == (SESSION_REPLICATION_ROLE_LOCAL + 2),
+                "array length mismatch");
+
 static const struct config_enum_entry syslog_facility_options[] = {
 #ifdef HAVE_SYSLOG
    {"local0", LOG_LOCAL0, false},
@@ -339,18 +354,27 @@ static const struct config_enum_entry track_function_options[] = {
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(track_function_options) == (TRACK_FUNC_ALL + 2),
+                "array length mismatch");
+
 static const struct config_enum_entry xmlbinary_options[] = {
    {"base64", XMLBINARY_BASE64, false},
    {"hex", XMLBINARY_HEX, false},
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(xmlbinary_options) == (XMLBINARY_HEX + 2),
+                "array length mismatch");
+
 static const struct config_enum_entry xmloption_options[] = {
    {"content", XMLOPTION_CONTENT, false},
    {"document", XMLOPTION_DOCUMENT, false},
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(xmloption_options) == (XMLOPTION_CONTENT + 2),
+                "array length mismatch");
+
 /*
  * Although only "on", "off", and "safe_encoding" are documented, we
  * accept all the likely variants of "on" and "off".
@@ -465,6 +489,9 @@ const struct config_enum_entry ssl_protocol_versions_info[] = {
    {NULL, 0, false}
 };
 
+StaticAssertDecl(lengthof(ssl_protocol_versions_info) == (PG_TLS1_3_VERSION + 2),
+                "array length mismatch");
+
 static struct config_enum_entry shared_memory_options[] = {
 #ifndef WIN32
    {"sysv", SHMEM_TYPE_SYSV, false},
@@ -615,6 +642,9 @@ const char *const GucContext_Names[] =
     /* PGC_USERSET */ "user"
 };
 
+StaticAssertDecl(lengthof(GucContext_Names) == (PGC_USERSET + 1),
+                "array length mismatch");
+
 /*
  * Displayable names for source types (enum GucSource)
  *
@@ -638,6 +668,9 @@ const char *const GucSource_Names[] =
     /* PGC_S_SESSION */ "session"
 };
 
+StaticAssertDecl(lengthof(GucSource_Names) == (PGC_S_SESSION + 1),
+                "array length mismatch");
+
 /*
  * Displayable names for the groupings defined in enum config_group
  */
@@ -749,6 +782,9 @@ const char *const config_group_names[] =
    NULL
 };
 
+StaticAssertDecl(lengthof(config_group_names) == (DEVELOPER_OPTIONS + 2),
+                "array length mismatch");
+
 /*
  * Displayable names for GUC variable types (enum config_type)
  *
@@ -763,6 +799,9 @@ const char *const config_type_names[] =
     /* PGC_ENUM */ "enum"
 };
 
+StaticAssertDecl(lengthof(config_type_names) == (PGC_ENUM + 1),
+                "array length mismatch");
+
 /*
  * Unit conversion tables.
  *
index 464798cf5c758d7c00ec62e22284d099042eaf24..1432dd7e0d67e210847a40eb08a6f2c6822b22ef 100644 (file)
@@ -80,6 +80,9 @@ static const int dbObjectTypePriority[] =
    38                          /* DO_SUBSCRIPTION */
 };
 
+StaticAssertDecl(lengthof(dbObjectTypePriority) == (DO_SUBSCRIPTION + 1),
+                "array length mismatch");
+
 static DumpId preDataBoundId;
 static DumpId postDataBoundId;
 
index 91efcedf70dd961313c3b81ffddf7fbc562d1272..ad733d136355978f43c51c705622b10c4c9407be 100644 (file)
@@ -37,6 +37,9 @@ const char *const forkNames[] = {
    "init"                      /* INIT_FORKNUM */
 };
 
+StaticAssertDecl(lengthof(forkNames) == (MAX_FORKNUM + 1),
+                "array length mismatch");
+
 /*
  * forkname_to_number - look up fork number by name
  *
index 6898229b43e81beae2791fb4bcd32dba47bcebf1..d10b9812fbe2e495a131e3ec28c9d3fbc6d2be1d 100644 (file)
@@ -832,8 +832,10 @@ extern void ExceptionalCondition(const char *conditionName,
  * throw a compile error using the "errmessage" (a string literal).
  *
  * gcc 4.6 and up supports _Static_assert(), but there are bizarre syntactic
- * placement restrictions.  These macros make it safe to use as a statement
- * or in an expression, respectively.
+ * placement restrictions.  Macros StaticAssertStmt() and StaticAssertExpr()
+ * make it safe to use as a statement or in an expression, respectively.
+ * The macro StaticAssertDecl() is suitable for use at file scope (outside of
+ * any function).
  *
  * Otherwise we fall back on a kluge that assumes the compiler will complain
  * about a negative width for a struct bit-field.  This will not include a
@@ -845,11 +847,15 @@ extern void ExceptionalCondition(const char *conditionName,
    do { _Static_assert(condition, errmessage); } while(0)
 #define StaticAssertExpr(condition, errmessage) \
    ((void) ({ StaticAssertStmt(condition, errmessage); true; }))
+#define StaticAssertDecl(condition, errmessage) \
+   _Static_assert(condition, errmessage)
 #else                          /* !HAVE__STATIC_ASSERT */
 #define StaticAssertStmt(condition, errmessage) \
    ((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; }))
 #define StaticAssertExpr(condition, errmessage) \
    StaticAssertStmt(condition, errmessage)
+#define StaticAssertDecl(condition, errmessage) \
+   extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1])
 #endif                         /* HAVE__STATIC_ASSERT */
 #else                          /* C++ */
 #if defined(__cpp_static_assert) && __cpp_static_assert >= 200410
@@ -857,12 +863,16 @@ extern void ExceptionalCondition(const char *conditionName,
    static_assert(condition, errmessage)
 #define StaticAssertExpr(condition, errmessage) \
    ({ static_assert(condition, errmessage); })
-#else
+#define StaticAssertDecl(condition, errmessage) \
+   static_assert(condition, errmessage)
+#else                          /* !__cpp_static_assert */
 #define StaticAssertStmt(condition, errmessage) \
    do { struct static_assert_struct { int static_assert_failure : (condition) ? 1 : -1; }; } while(0)
 #define StaticAssertExpr(condition, errmessage) \
    ((void) ({ StaticAssertStmt(condition, errmessage); }))
-#endif
+#define StaticAssertDecl(condition, errmessage) \
+   extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1])
+#endif                         /* __cpp_static_assert */
 #endif                         /* C++ */
 
 
index 870ecb51b792722aa1ccbc1406c2bd83ac5af811..3f88683a059d4c7e7cb579ca04a44995d0bfca71 100644 (file)
@@ -418,6 +418,16 @@ do { \
                        ((overwrite) ? PAI_OVERWRITE : 0) | \
                        ((is_heap) ? PAI_IS_HEAP : 0))
 
+/*
+ * Check that BLCKSZ is a multiple of sizeof(size_t).  In PageIsVerified(),
+ * it is much faster to check if a page is full of zeroes using the native
+ * word size.  Note that this assertion is kept within a header to make
+ * sure that StaticAssertDecl() works across various combinations of
+ * platforms and compilers.
+ */
+StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)),
+                "BLCKSZ has to be a multiple of sizeof(size_t)");
+
 extern void PageInit(Page page, Size pageSize, Size specialSize);
 extern bool PageIsVerified(Page page, BlockNumber blkno);
 extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,