Hi, I have noticed a slight mismatch between typedefs in docs and header files. On current master branch: - CustomScanState is missing custom_ps, pscan_len and slotOps fields in docs. - `methods` field of CustomPath, CustomScan and CustomScanState is missing `struct` in type. - BrinOpcInfo.oi_regular_nulls is missing. - pgNotify.next is missing. But the comment above it says apps should not use it, so I guess it can be left as is.
Attached diff file shows other mismatches I could find. There are somecomments that could be updated. Other differences are caused by indentation variations and false positives. The script I used for typedef extraction is also attached.
Regards, Artem Fadeev. https://postgrespro.com
diff --git a/tmp/typedefs_output.code.txt b/tmp/typedefs_output.docs.txt index 25bee9e7846..e4e6c89fc90 100644 --- a/tmp/typedefs_output.code.txt +++ b/tmp/typedefs_output.docs.txt @@ -8,15 +8,11 @@ typedef struct OutputPluginCallbacks LogicalDecodeMessageCB message_cb; LogicalDecodeFilterByOriginCB filter_by_origin_cb; LogicalDecodeShutdownCB shutdown_cb; - - /* streaming of changes at prepare time */ LogicalDecodeFilterPrepareCB filter_prepare_cb; LogicalDecodeBeginPrepareCB begin_prepare_cb; LogicalDecodePrepareCB prepare_cb; LogicalDecodeCommitPreparedCB commit_prepared_cb; LogicalDecodeRollbackPreparedCB rollback_prepared_cb; - - /* streaming of changes */ LogicalDecodeStreamStartCB stream_start_cb; LogicalDecodeStreamStopCB stream_stop_cb; LogicalDecodeStreamAbortCB stream_abort_cb; @@ -43,11 +39,13 @@ typedef struct ArchiveModuleCallbacks === typedef struct { - float8 x, - y; + double x, y; } Point; === -typedef struct varlena text; +typedef struct { + int32 length; + char data[FLEXIBLE_ARRAY_MEMBER]; +} text; === typedef struct FuncCallContext { @@ -62,16 +60,16 @@ typedef struct FuncCallContext /* * OPTIONAL maximum number of calls * - * max_calls is here for convenience only and setting it is optional. If - * not set, you must provide alternative means to know when the function - * is done. + * max_calls is here for convenience only and setting it is optional. + * If not set, you must provide alternative means to know when the + * function is done. */ uint64 max_calls; /* * OPTIONAL pointer to miscellaneous user-provided context information * - * user_fctx is for use as a pointer to your own struct to retain + * user_fctx is for use as a pointer to your own data to retain * arbitrary context information between calls of your function. */ void *user_fctx; @@ -79,9 +77,10 @@ typedef struct FuncCallContext /* * OPTIONAL pointer to struct containing attribute type input metadata * - * attinmeta is for use when returning tuples (i.e. composite data types) - * and is not used when returning base data types. It is only needed if - * you intend to use BuildTupleFromCStrings() to create the return tuple. + * attinmeta is for use when returning tuples (i.e., composite data types) + * and is not used when returning base data types. It is only needed + * if you intend to use BuildTupleFromCStrings() to create the return + * tuple. */ AttInMetadata *attinmeta; @@ -90,15 +89,15 @@ typedef struct FuncCallContext * * multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used * by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory - * context for any memory that is to be reused across multiple calls of - * the SRF. + * context for any memory that is to be reused across multiple calls + * of the SRF. */ MemoryContext multi_call_memory_ctx; /* * OPTIONAL pointer to struct containing tuple description * - * tuple_desc is for use when returning tuples (i.e. composite data types) + * tuple_desc is for use when returning tuples (i.e., composite data types) * and is only needed if you are going to build the tuples with * heap_form_tuple() rather than with BuildTupleFromCStrings(). Note that * the TupleDesc pointer stored here should usually have been run through @@ -122,34 +121,33 @@ typedef struct SPITupleTable SubTransactionId subid; /* subxact in which tuptable was created */ } SPITupleTable; === -typedef struct _PQconninfoOption +typedef struct { char *keyword; /* The keyword of the option */ char *envvar; /* Fallback environment variable name */ char *compiled; /* Fallback compiled in default value */ char *val; /* Option's current value, or NULL */ char *label; /* Label for field in connect dialog */ - char *dispchar; /* Indicates how to display this field in a - * connect dialog. Values are: "" Display - * entered value as is "*" Password field - - * hide value "D" Debug option - don't show - * by default */ + char *dispchar; /* Indicates how to display this field + in a connect dialog. Values are: + "" Display entered value as is + "*" Password field - hide value + "D" Debug option - don't show by default */ int dispsize; /* Field size in characters for dialog */ } PQconninfoOption; === -typedef struct _PQprintOpt +typedef struct { pqbool header; /* print output field headings and row count */ pqbool align; /* fill align the fields */ pqbool standard; /* old brain dead format */ - pqbool html3; /* output html tables */ + pqbool html3; /* output HTML tables */ pqbool expanded; /* expand tables */ pqbool pager; /* use pager for output if needed */ char *fieldSep; /* field separator */ - char *tableOpt; /* insert to HTML <table ...> */ - char *caption; /* HTML <caption> */ - char **fieldName; /* null terminated array of replacement field - * names */ + char *tableOpt; /* attributes for HTML table element */ + char *caption; /* HTML table caption */ + char **fieldName; /* null-terminated array of replacement field names */ } PQprintOpt; === typedef struct @@ -158,33 +156,31 @@ typedef struct int isint; union { - int *ptr; /* can't use void (dec compiler barfs) */ + int *ptr; int integer; } u; } PQArgBlock; === typedef struct pgNotify { - char *relname; /* notification condition name */ + char *relname; /* notification channel name */ int be_pid; /* process ID of notifying server process */ - char *extra; /* notification parameter */ - /* Fields below here are private to libpq; apps should not use 'em */ - struct pgNotify *next; /* list link */ + char *extra; /* notification payload string */ } PGnotify; === typedef enum { - PQERRORS_TERSE, /* single-line error messages */ - PQERRORS_DEFAULT, /* recommended style */ - PQERRORS_VERBOSE, /* all the facts, ma'am */ - PQERRORS_SQLSTATE /* only error severity and SQLSTATE code */ + PQERRORS_TERSE, + PQERRORS_DEFAULT, + PQERRORS_VERBOSE, + PQERRORS_SQLSTATE } PGVerbosity; === typedef enum { - PQSHOW_CONTEXT_NEVER, /* never show CONTEXT field */ - PQSHOW_CONTEXT_ERRORS, /* show CONTEXT for errors only (default) */ - PQSHOW_CONTEXT_ALWAYS /* always show CONTEXT field */ + PQSHOW_CONTEXT_NEVER, + PQSHOW_CONTEXT_ERRORS, + PQSHOW_CONTEXT_ALWAYS } PGContextVisibility; === typedef struct @@ -219,7 +215,11 @@ typedef struct PGresult *result; } PGEventResultDestroy; === -'mydata;' is not present in code +typedef struct +{ + int n; + char *str; +} mydata; === typedef struct _PGpromptOAuthDevice { @@ -233,68 +233,39 @@ typedef struct _PGpromptOAuthDevice typedef struct PGoauthBearerRequest { /* Hook inputs (constant across all calls) */ - const char *openid_configuration; /* OIDC discovery URI */ + const char *openid_configuration; /* OIDC discovery URL */ const char *scope; /* required scope(s), or NULL */ /* Hook outputs */ - /*--------- - * Callback implementing a custom asynchronous OAuth flow. - * - * The callback may return - * - PGRES_POLLING_READING/WRITING, to indicate that a socket descriptor - * has been stored in *altsock and libpq should wait until it is - * readable or writable before calling back; - * - PGRES_POLLING_OK, to indicate that the flow is complete and - * request->token has been set; or - * - PGRES_POLLING_FAILED, to indicate that token retrieval has failed. - * - * This callback is optional. If the token can be obtained without - * blocking during the original call to the PQAUTHDATA_OAUTH_BEARER_TOKEN - * hook, it may be returned directly, but one of request->async or - * request->token must be set by the hook. - */ + /* Callback implementing a custom asynchronous OAuth flow. */ PostgresPollingStatusType (*async) (PGconn *conn, struct PGoauthBearerRequest *request, - SOCKTYPE * altsock); + SOCKTYPE *altsock); - /* - * Callback to clean up custom allocations. A hook implementation may use - * this to free request->token and any resources in request->user. - * - * This is technically optional, but highly recommended, because there is - * no other indication as to when it is safe to free the token. - */ + /* Callback to clean up custom allocations. */ void (*cleanup) (PGconn *conn, struct PGoauthBearerRequest *request); - /* - * The hook should set this to the Bearer token contents for the - * connection, once the flow is completed. The token contents must remain - * available to libpq until the hook's cleanup callback is called. - */ - char *token; - - /* - * Hook-defined data. libpq will not modify this pointer across calls to - * the async callback, so it can be used to keep track of - * application-specific state. Resources allocated here should be freed by - * the cleanup callback. - */ - void *user; + char *token; /* acquired Bearer token */ + void *user; /* hook-defined allocated data */ } PGoauthBearerRequest; === -'comp_t;' is not present in code +typedef struct +{ + int intval; + varchar textval[33]; +} comp_t; === -typedef struct sqlda_struct sqlda_t; +typedef struct sqlda_compat sqlda_t; === -typedef struct sqlvar_struct sqlvar_t; +typedef struct sqlvar_compat sqlvar_t; === typedef struct EventTriggerData { NodeTag type; const char *event; /* event name */ Node *parsetree; /* parse tree */ - CommandTag tag; + CommandTag tag; /* command tag */ } EventTriggerData; === typedef struct RmgrData @@ -310,52 +281,38 @@ typedef struct RmgrData struct XLogRecordBuffer *buf); } RmgrData; === -'Complex;' is not present in code +typedef struct Complex { + double x; + double y; +} Complex; === typedef struct CustomPath { Path path; - uint32 flags; /* mask of CUSTOMPATH_* flags, see - * nodes/extensible.h */ - List *custom_paths; /* list of child Path nodes, if any */ + uint32 flags; + List *custom_paths; List *custom_restrictinfo; List *custom_private; - const struct CustomPathMethods *methods; + const CustomPathMethods *methods; } CustomPath; === typedef struct CustomScan { Scan scan; - /* mask of CUSTOMPATH_* flags, see nodes/extensible.h */ uint32 flags; - /* list of Plan nodes, if any */ List *custom_plans; - /* expressions that custom code may evaluate */ List *custom_exprs; - /* private data for custom code */ List *custom_private; - /* optional tlist describing scan tuple */ List *custom_scan_tlist; - /* RTIs generated by this scan */ Bitmapset *custom_relids; - - /* - * NOTE: The method field of CustomScan is required to be a pointer to a - * static table of callback functions. So we don't copy the table itself, - * just reference the original one. - */ - const struct CustomScanMethods *methods; + const CustomScanMethods *methods; } CustomScan; === typedef struct CustomScanState { ScanState ss; - uint32 flags; /* mask of CUSTOMPATH_* flags, see - * nodes/extensible.h */ - List *custom_ps; /* list of child PlanState nodes, if any */ - Size pscan_len; /* size of parallel coordination information */ - const struct CustomExecMethods *methods; - const struct TupleTableSlotOps *slotOps; + uint32 flags; + const CustomExecMethods *methods; } CustomScanState; === typedef struct TriggerData @@ -375,8 +332,7 @@ typedef struct TriggerData === typedef struct Trigger { - Oid tgoid; /* OID of trigger (pg_trigger row) */ - /* Remaining fields are copied from pg_trigger, see pg_trigger.h */ + Oid tgoid; char *tgname; Oid tgfoid; int16 tgtype; @@ -408,21 +364,30 @@ typedef struct BackgroundWorker char bgw_function_name[BGW_MAXLEN]; Datum bgw_main_arg; char bgw_extra[BGW_EXTRALEN]; - pid_t bgw_notify_pid; /* SIGUSR1 this backend on start/stop */ + pid_t bgw_notify_pid; } BackgroundWorker; === -'MyEnumType;' is not present in code +typedef enum MyEnumType +{ + MY_ENUM_ON, + MY_ENUM_OFF, + MY_ENUM_AUTO +} MyEnumType; === -'MyOptionsStruct;' is not present in code +typedef struct +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int int_param; /* integer parameter */ + double real_param; /* real parameter */ + MyEnumType enum_param; /* enum parameter */ + int str_param; /* string parameter */ +} MyOptionsStruct; === typedef struct BrinOpcInfo { /* Number of columns stored in an index column of this opclass */ uint16 oi_nstored; - /* Regular processing of NULLs in BrinValues? */ - bool oi_regular_nulls; - /* Opaque pointer for the opclass' private use */ void *oi_opaque; @@ -441,21 +406,7 @@ typedef struct OAuthValidatorCallbacks === typedef struct ValidatorModuleResult { - /* - * Should be set to true if the token carries sufficient permissions for - * the bearer to connect. - */ bool authorized; - - /* - * If the token authenticates the user, this should be set to a palloc'd - * string containing the SYSTEM_USER to use for HBA mapping. Consider - * setting this even if result->authorized is false so that DBAs may use - * the logs to match end users to token failures. - * - * This is required if the module is not configured for ident mapping - * delegation. See the validator module documentation for details. - */ char *authn_id; } ValidatorModuleResult; === @@ -508,19 +459,14 @@ typedef struct IndexAmRoutine bool amcaninclude; /* does AM use maintenance_work_mem? */ bool amusemaintenanceworkmem; - /* does AM store tuple information only at block granularity? */ + /* does AM summarize tuples, with at least all tuples in the block + * summarized in one summary */ bool amsummarizing; - /* OR of parallel vacuum flags. See vacuum.h for flags. */ + /* OR of parallel vacuum flags */ uint8 amparallelvacuumoptions; /* type of data stored in index, or InvalidOid if variable */ Oid amkeytype; - /* - * If you add new properties to either the above or the below lists, then - * they should also (usually) be exposed via the property API (see - * IndexAMProperty at the top of the file, and utils/adt/amutils.c). - */ - /* interface functions */ ambuild_function ambuild; ambuildempty_function ambuildempty; @@ -565,7 +511,7 @@ typedef struct spgConfigOut Oid labelType; /* Data type of inner-tuple node labels */ Oid leafType; /* Data type of leaf-tuple values */ bool canReturnData; /* Opclass can reconstruct original data */ - bool longValuesOK; /* Opclass can cope with values > 1 page */ + bool longValuesOK; /* Opclass can cope with values > 1 page */ } spgConfigOut; === typedef struct spgChooseIn @@ -586,7 +532,7 @@ typedef enum spgChooseResultType { spgMatchNode = 1, /* descend into existing node */ spgAddNode, /* add a node to the inner tuple */ - spgSplitTuple, /* split inner tuple (change its prefix) */ + spgSplitTuple /* split inner tuple (change its prefix) */ } spgChooseResultType; === typedef struct spgChooseOut @@ -611,8 +557,8 @@ typedef struct spgChooseOut bool prefixHasPrefix; /* tuple should have a prefix? */ Datum prefixPrefixDatum; /* if so, its value */ int prefixNNodes; /* number of nodes */ - Datum *prefixNodeLabels; /* their labels (or NULL for no - * labels) */ + Datum *prefixNodeLabels; /* their labels (or NULL for + * no labels) */ int childNodeN; /* which node gets child tuple */ /* Info to form new lower-level inner tuple with all old nodes */
#!/usr/bin/env python3 import pathlib import re import sys OUTPUT_PREFIX = "./typedefs_output" SEPARATOR = "===" # separator line used in output files def read_statement(text, start): """Extract text of a C statement starting at given index""" curr = start depth = 0 # depth of {} nesting while not (depth == 0 and text[curr] == ";"): if text[curr] == "{": depth += 1 elif text[curr] == "}": depth -= 1 curr += 1 curr += 1 # trailing ";" return text[start:curr] def standardize(text): text = text.replace("\t", " ") # no tabs text = re.sub("[ ]+", " ", text) # squash spaces return text def process_files(path_list, statement_regexp, name_regexp): """Parse path_list, return {what_is_declared: declaration, ...} dict""" result = {} for path in path_list: text = path.read_text() for match in statement_regexp.finditer(text): try: decl = read_statement(text, match.start(0)) except IndexError: # Code with unmatched '{' in comments may break this little parser. # Report such cases and go on. print( f"Failed parsing {path} at index {match.start(0)}.", file=sys.stderr ) print("First 100 symbols:", file=sys.stderr) print(text[match.start(0) : match.start(0) + 100], file=sys.stderr) print(file=sys.stderr) continue decl = standardize(decl) try: name = re.search(name_regexp, decl)[0] except Exception as e: print(decl) print( f"Regexp {name_regexp!r} could not find declaration name in {decl!r}", file=sys.stderr, ) raise e result[name] = decl return result def main(): if len(sys.argv) not in (3, 4): print( r""" Usage: ./typedefs.py <stmt_regexp> <name_regexp> [<output_prefix>] # For finding function type typedefs ./typedefs.py '^typedef[^\{;]*\(' '\([^\)]*\)' # For finding struct/enum typedefs ./typedefs.py '^typedef\s+(struct|enum)' '[^\s]*;$' # Then compare diff ./typedefs_output* Description: Scans working directory for code declarations and code declaration excerpts in docs matching "stmt_regexp" regular expression. For each declared name (). Scans working directory for statements matching <stmt_regexp> in code and code excerpts in docs. <name_regexp> is used to extract name from declaration. Writes all found statements from docs into <output_prefix>.docs.txt and corresponding declarations from code into <output_prefix>.code.txt (<output_prefix> is "./typedefs_output" by default) """.strip() ) exit(1) statement_regexp = re.compile(sys.argv[1], re.MULTILINE) name_regexp = sys.argv[2] output_prefix = sys.argv[3] if len(sys.argv) == 4 else OUTPUT_PREFIX docs_typedefs = process_files( pathlib.Path(".").glob("**/*.sgml"), statement_regexp, name_regexp ) code_typedefs = process_files( pathlib.Path(".").glob("**/*.h"), statement_regexp, name_regexp ) docs_output = [] code_output = [] for name, decl in docs_typedefs.items(): docs_output.append(decl) code_output.append(code_typedefs.get(name, f"{name!r} is not present in code")) pathlib.Path(f"{output_prefix}.docs.txt").write_text( f"\n{SEPARATOR}\n".join(docs_output) ) pathlib.Path(f"{output_prefix}.code.txt").write_text( f"\n{SEPARATOR}\n".join(code_output) ) if __name__ == "__main__": main()