├── .gitignore ├── Detailed.md ├── License.md ├── Readme.md ├── generate.py ├── requirements.txt └── templates ├── Detailed.md ├── DetailedHeader.md ├── Header.md ├── Hook.md ├── Readme.md └── hooks ├── ClientAuthentication_hook.md ├── ExecutorCheckPerms_hook.md ├── ExecutorEnd_hook.md ├── ExecutorFinish_hook.md ├── ExecutorRun_hook.md ├── ExecutorStart_hook.md ├── ExplainOneQuery_hook.md ├── ProcessUtility_hook.md ├── check_password_hook.md ├── create_upper_paths_hook.md ├── emit_log_hook.md ├── explain_get_index_name_hook.md ├── fmgr_hook.md ├── func_beg.md ├── func_end.md ├── func_setup.md ├── get_attavgwidth_hook.md ├── get_index_stats_hook.md ├── get_relation_info_hook.md ├── get_relation_stats_hook.md ├── join_search_hook.md ├── needs_fmgr_hook.md ├── object_access_hook.md ├── planner_hook.md ├── post_parse_analyze_hook.md ├── row_security_policy_hook_permissive.md ├── row_security_policy_hook_restrictive.md ├── set_join_pathlist_hook.md ├── set_rel_pathlist_hook.md ├── shmem_startup_hook.md ├── stmt_beg.md └── stmt_end.md /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | bin 3 | include 4 | lib 5 | .Python 6 | pip-selfcheck.json 7 | .idea -------------------------------------------------------------------------------- /Detailed.md: -------------------------------------------------------------------------------- 1 | # Postgresql hooks documentation 2 | 3 | PostgreSQL hooks are a simple way to extend functionality of the database. 4 | They allow extensions to introspect database state, react to events and 5 | interfere with database operations. 6 | 7 | Every hook is a pointer to a function, initially set to `NULL`. 8 | 9 | When postgres wants to call a hook, it checks whether the pointer for that 10 | hook is not null and if that's the case, calls the registered function. 11 | 12 | Extensions can update these pointers during the init procedure 13 | in order to register a new handler for a hook. 14 | 15 | That is, when extension is loaded, postgres calls its `_PG_init` function. 16 | Once called, it can alter hook variables which are a part of the public binary 17 | interface. 18 | 19 | A usual setup would include saving the previous value of the hook variable 20 | and writing pointer to a handler defined by extension. 21 | 22 | Saving the previous value is important because another extension could've 23 | registered its own hook handler. If that's the case, we'd like to call it in 24 | our hook so that this extension can operate without errors. Any well-designed 25 | plugin will do such hook chaining. 26 | 27 | To pop the state of the hook created by one extension `_PG_fini` function must be implemented, 28 | which is basically recovers hook to it's value before `_PG_init`. 29 | 30 | A standard example on how to use hooks is the `auth_delay` plugin. 31 | This plugin delays error report in case of user authentication failure, 32 | which is useful to block password brute-forcing. 33 | 34 | ```c 35 | // We store previously assigned hook pointer in a global variable. 36 | static ClientAuthentication_hook_type original_client_auth_hook = NULL; 37 | 38 | // Our hook implementation. 39 | static void auth_delay_checks(Port *port, int status) 40 | { 41 | // If any other extension registered its own hook handler, 42 | // call it before performing our own logic. 43 | if (original_client_auth_hook) 44 | original_client_auth_hook(port, status); 45 | 46 | // If authentication failed, we wait for one second before returning 47 | // control to the caller. 48 | if (status != STATUS_OK) 49 | { 50 | pg_usleep(1000000L); 51 | } 52 | } 53 | 54 | // Called upon extension load. 55 | void _PG_init(void) 56 | { 57 | // Save the original hook value. 58 | original_client_auth_hook = ClientAuthentication_hook; 59 | // Register our handler. 60 | ClientAuthentication_hook = auth_delay_checks; 61 | } 62 | 63 | // Called with extension unload. 64 | void _PG_fini(void) 65 | { 66 | // Return back the original hook value. 67 | ClientAuthentication_hook = original_client_auth_hook; 68 | } 69 | 70 | ``` 71 | 72 | ## Table of contents 73 | 74 | * [General Hooks](#general-hooks) 75 | * [Security Hooks](#security-hooks) 76 | * [Function Manager Hooks](#function-manager-hooks) 77 | * [Planner Hooks](#planner-hooks) 78 | * [Executor Hooks](#executor-hooks) 79 | * [PL/pgsql Hooks](#plpgsql-hooks) 80 | 81 | 82 | ## General Hooks 83 | 84 | 85 | 86 | 87 | # void emit_log_hook(edata) [<>](https://github.com/postgres/postgres/blob/master/src/include/utils/elog.h#L415 "Source") 88 | 89 | Hook for intercepting messages before they are sent to the server log. 90 | 91 | This hook is called just before sending an error message to the server log 92 | and to the client. The purpose of this hook is to invoke an additional 93 | logic and possibly prevent this error message from being added to the 94 | server log. 95 | 96 | This hook is useful for implementing custom logging process. 97 | 98 | *Inputs:* 99 | 100 | * ErrorData * edata — a structure which holds a complete info 101 | about the error message. Despite `edata` is a non-const pointer, the only 102 | supported change in the given structure is setting `output_to_server` to 103 | `false`. That is, any other change, including setting `output_to_server` to 104 | `true`, considered not supported. 105 | 106 | Note: despite any other changes to the edata are not officially supported 107 | (as per comment [on line 1455 of the elog.c][emit_log_hook_1])), 108 | postgres actually checks for both `output_to_server` and `output_to_client` 109 | flags. 110 | 111 | [emit_log_hook_1]: https://github.com/postgres/postgres/blob/master/src/backend/utils/error/elog.c#L1456 112 | 113 | 114 | # void shmem_startup_hook() [<>](https://github.com/postgres/postgres/blob/master/src/include/storage/ipc.h#L78 "Source") 115 | 116 | Hook for extensions to initialize their shared memory. 117 | 118 | This hook is called by postmaster or by a standalone backend 119 | right after postgres initializes its shared memory and semaphores 120 | so that extensions have chance to initialize their shared state. 121 | 122 | It also may be called by a backend forked from the postmaster. 123 | In this situation, the shared memory segment already exists, so you only have 124 | to initialize the local memory state (check `!IsUnderPostmaster` 125 | to determine if that's the case). 126 | 127 | Note that you can bind a callback for shared state teardown 128 | via `on_shmem_exit`. 129 | 130 | Check out the `pg_stat_statements` code to get the idea on how to implement 131 | this hook correctly. 132 | 133 | 134 | ## Security Hooks 135 | 136 | 137 | 138 | 139 | # void check_password_hook(username, shadow_pass, password_type, validuntil_time, validuntil_null) [<>](https://github.com/postgres/postgres/blob/master/src/include/commands/user.h#L25 "Source") 140 | 141 | Hook for enforcing password constraints and performing action on password change. 142 | 143 | This hook is called whenever a new role is created via the `CREATE ROLE` 144 | statement or a password for an existing role is changed via the `ALTER ROLE` 145 | statement. Given a shadow password and some additional info, this hook can 146 | raise an error using the standard `ereport` mechanism if the password 147 | isn't strong enough. 148 | 149 | *Inputs:* 150 | 151 | * const char * username — name of the created/altered role. 152 | * const char * shadow_pass — a shadow pass, i.e. a plain password 153 | or a password hash. 154 | * PasswordType password_type — type of the password. 155 | `PASSWORD_TYPE_MD5` for an md5-encrypted password, 156 | `PASSWORD_TYPE_SCRAM_SHA_256` for a sha-256-encrypted password, 157 | `PASSWORD_TYPE_PLAINTEXT` for a plaintext password. 158 | * Datum validuntil_time — date upon which this password expires. 159 | * bool validuntil_null — a flag that is true if and only if 160 | the password have no expiration date (i.e. a null date is passed). 161 | 162 | 163 | # void ClientAuthentication_hook(port, status) [<>](https://github.com/postgres/postgres/blob/master/src/include/libpq/auth.h#L29 "Source") 164 | 165 | Hook for controlling the authentication process. 166 | 167 | Called after finishing user authentication (regardless of whether authentication 168 | succeed or not). 169 | 170 | This hook will be called for every connection that passed authentication. 171 | However, it is not guaranteed to be called if there are issues with the 172 | connection itself. For example, SSL verification failure or pg_hba.conf 173 | check failure will close the connection without calling this hook. 174 | 175 | *Inputs:* 176 | 177 | * Port * port — full info about the connection and 178 | the connected user. 179 | * int status — a standard status code. `STATUS_OK` (`0`) 180 | if authentication successful. 181 | 182 | 183 | # bool ExecutorCheckPerms_hook(rangeTabls, abort) [<>](https://github.com/postgres/postgres/blob/master/src/include/executor/executor.h#L85 "Source") 184 | 185 | Hook for adding additional security checks on the per-relation level. 186 | 187 | Given a relations list, this hook should return `true` if access is granted. 188 | `false`, if access is not granted and `abort` is `false`. If `abort` is `true` 189 | and access is not granted, it should throw an appropriate error. 190 | 191 | This hook is not called if the standard permission check procedure denies 192 | access to any relation in the list. Therefore, there is no way to actually 193 | raise user privileges. 194 | 195 | Theoretically, only plain-relation RTEs need to be checked in this hook. 196 | Function RTEs are checked during the function preparation procedure. 197 | Join, subquery, and special RTEs need no checks. 198 | 199 | *Inputs:* 200 | 201 | * List * rangeTabls — list of `RangeTblEntry` objects that needs 202 | checking. 203 | * bool abort — if `true`, raise `aclcheck_error` instead of 204 | returning `false` from the hook. 205 | 206 | *Output:* 207 | 208 | `true` if user have privileges to access given relations, `false` or raise an 209 | error otherwise, depending on the `abort` flag. 210 | 211 | 212 | # void object_access_hook(access, classId, objectId, subId, arg) [<>](https://github.com/postgres/postgres/blob/master/src/include/catalog/objectaccess.h#L138 "Source") 213 | 214 | Hook to monitor accesses to objects. 215 | 216 | Object access hooks are called just before or just after performing certain 217 | actions on an SQL object. This is intended as infrastructure for security 218 | or logging extensions. 219 | 220 | There are several types of actions defined in `ObjectAccessType`: 221 | 222 | `OAT_POST_CREATE`: hook is invoked just after the object is created. 223 | Typically, this is done after inserting the primary catalog records and 224 | associated dependencies. 225 | 226 | `OAT_DROP`: hook is invoked just before deletion of objects. 227 | 228 | `OAT_POST_ALTER`: hook is invoked just after the object is altered, 229 | but before the command counter is incremented. An extension using the 230 | hook can use a current MVCC snapshot to get the old version of the tuple, 231 | and can use `SnapshotSelf` to get the new version of the tuple. 232 | 233 | `OAT_NAMESPACE_SEARCH`: hook is invoked prior to object name lookup under 234 | a particular namespace. This event is equivalent to usage permission 235 | on a schema under the default access control mechanism. 236 | 237 | `OAT_FUNCTION_EXECUTE`: hook is invoked prior to function execution. 238 | This event is almost equivalent to execute permission on functions, 239 | except for the case when execute permission is checked during object 240 | creation or altering, because `OAT_POST_CREATE` or `OAT_POST_ALTER` are 241 | sufficient for extensions to track these kind of checks. 242 | 243 | Other types may be added in the future. 244 | 245 | *Inputs:* 246 | 247 | For different access types, inputs of this hook mean different things. 248 | 249 | * ObjectAccessType access — access type. 250 | * Oid classId — id of a relation which contains this object. 251 | You can determine type of an object by this parameter. 252 | * Oid objectId — object that is being accessed. 253 | * int subId — subitem within object (e.g. column), or 0. 254 | * void * arg — access type specific argument. 255 | 256 | For `OAT_POST_CREATE`, `arg` is a pointer to `ObjectAccessPostCreate` 257 | structure, which contain a single field, namely `is_internal`. This field 258 | describes whether the context of this creation is invoked by user's 259 | operations, or not. As for `subId`, I've counted two cases when it's non-zero. 260 | The first is when creating a column, and the second one is when creating 261 | a default expression on a column. In either case, `subId` is 262 | an `AttrNumber` of a column. 263 | 264 | For `OAT_DROP` type, `arg` is a pointer to `ObjectAccessPostCreate` structure. 265 | It contains a single field called `dropflags`. They inform extensions the 266 | context of this deletion. 267 | 268 | For `OAT_POST_ALTER` type, `arg` is a pointer to `ObjectAccessPostAlter` 269 | structure. It contains an `is_internal` flag (see `OAT_POST_CREATE`) and an 270 | `auxiliary_id`. The latter is used when system catalog takes two IDs to 271 | identify a particular tuple of the catalog. It is only used when the caller want 272 | to identify an entry of pg_inherits, pg_db_role_setting or pg_user_mapping. 273 | Elsewhere, InvalidOid is be set. 274 | 275 | For `OAT_NAMESPACE_SEARCH` type, `subId` is unused, `classId` is always 276 | `NamespaceRelationId`, and `arg` is a pointer to `ObjectAccessNamespaceSearch`. 277 | 278 | `ObjectAccessNamespaceSearch` structure contain two fields. The first one, 279 | `ereport_on_violation`, indicates that the hook should raise an error when 280 | permission to search this schema is denied. The second one, `result`, is in fact 281 | an out parameter. Core code should initialize this to true, and any extension 282 | that wants to deny access should reset it to false. But an extension should be 283 | careful never to store a true value here, so that in case there are multiple 284 | extensions access is only allowed if all extensions agree. 285 | 286 | For `OAT_FUNCTION_EXECUTE` type, `subId` and `arg` are unused, and 287 | `classId` is always `ProcedureRelationId`. 288 | 289 | 290 | # List * row_security_policy_hook_permissive(cmdtype, relation) [<>](https://github.com/postgres/postgres/blob/master/src/include/rewrite/rowsecurity.h#L40 "Source") 291 | 292 | Hook to add policies which are combined with the other permissive policies. 293 | 294 | This hook, along with the `row_security_policy_hook_restrictive`, allows adding 295 | custom security policies. It is called to build a list of policies for the given 296 | command applied to the given relation. 297 | 298 | Access is granted to an object if and only if no restrictive policies deny 299 | access and any permissive policy grant access. 300 | 301 | *Inputs:* 302 | 303 | * CmdType cmdtype — command type. 304 | * Relation relation — relation id. 305 | 306 | *Output:* 307 | 308 | List of additional permissive policies that will be added to the list of 309 | default permissive policies. 310 | 311 | 312 | # List * row_security_policy_hook_restrictive(cmdtype, relation) [<>](https://github.com/postgres/postgres/blob/master/src/include/rewrite/rowsecurity.h#L42 "Source") 313 | 314 | Hook to add policies which are enforced, regardless of other policies. 315 | 316 | See `row_security_policy_hook_permissive` for a detailed description. 317 | 318 | Unlike for permissive policies, postgres guarantees that restrictive policies 319 | will be executed in a predefined order. That is, first postgres executes the 320 | default policies sorted by their name, than postgres executes custom policies, 321 | also sorted by their name. 322 | 323 | *Inputs:* 324 | 325 | * CmdType cmdtype — command type. 326 | * Relation relation — relation id. 327 | 328 | 329 | ## Function Manager Hooks 330 | 331 | 332 | 333 | 334 | # bool needs_fmgr_hook(fn_oid) [<>](https://github.com/postgres/postgres/blob/master/src/include/fmgr.h#L775 "Source") 335 | 336 | Auxiliary hook which decides whether `fmgr_hook` should be applied to a function. 337 | 338 | Given a function id, decide whether `fmgr_hook` should be called upon executing 339 | this function. 340 | 341 | The result of this hook should be combined with the result of a previously 342 | registered `needs_fmgr_hook` via the `OR` clause. This is required to ensure 343 | that other extensions can hook function even though this very extension does 344 | not hook them. Such behavior is vital for proper work of the security extensions. 345 | 346 | Note that hooked functions are not inlined. 347 | 348 | *Inputs:* 349 | 350 | * Oid fn_oid — id of a function which needs hooking. 351 | 352 | *Output:* 353 | 354 | Return `true` if you want to hook enter/exit event for this function. 355 | 356 | 357 | # void fmgr_hook(event, flinfo, arg) [<>](https://github.com/postgres/postgres/blob/master/src/include/fmgr.h#L776 "Source") 358 | 359 | Hook for controlling function execution process. 360 | 361 | This hook is intended as support for loadable security policy modules, which may 362 | want to perform additional privilege checks on function entry or exit, 363 | or to do other internal bookkeeping. 364 | 365 | It is invoked whenever postgres executes a function which was explicitly 366 | marked as hookable by `needs_fmgr_hook`. For each execution this hook is fired 367 | exactly twice: first time before invoking the function, second time after 368 | the function returns/throws. 369 | 370 | Note that there is a change that this hook will be called even if a function 371 | is not of interest of your extension (maybe some other extension made it 372 | hookable via its `needs_fmgr_hook`). 373 | 374 | *Inputs:* 375 | 376 | * FmgrHookEventType event — event type, can be one of 377 | `FHET_START`, `FHET_END`, `FHET_ABORT`. 378 | * FmgrInfo * flinfo — function info, including its id and 379 | arguments specification. 380 | * Datum * arg — function arguments. 381 | 382 | 383 | ## Planner Hooks 384 | 385 | 386 | 387 | 388 | # const char * explain_get_index_name_hook(indexId) [<>](https://github.com/postgres/postgres/blob/master/src/include/commands/explain.h#L76 "Source") 389 | 390 | Hook for altering index names in explain statements. 391 | 392 | Extensions may override the default name generation mechanism 393 | so that plans involving hypothetical indexes can be explained. 394 | 395 | *Inputs:* 396 | 397 | * Oid indexId — index id. 398 | 399 | *Output:* 400 | 401 | Name of the index or `NULL`. In the later case, a default name 402 | will be generated. 403 | 404 | 405 | # void ExplainOneQuery_hook(query, cursorOptions, into, es, queryString, params, queryEnv) [<>](https://github.com/postgres/postgres/blob/master/src/include/commands/explain.h#L72 "Source") 406 | 407 | Hook for overriding explain procedure for a single query. 408 | 409 | This hook, if present, should generate explanation for the given query 410 | using other `Explain*` functions and modifying the explain state. 411 | 412 | The default behaviour is to plan query using `pg_plan_query()` and than 413 | delegate printing to the `ExplainOnePlan()` function. 414 | 415 | *Inputs:* 416 | 417 | * Query * query — query that needs explanation. 418 | * int cursorOptions — cursor options in form of a per-bit enum. 419 | See `CURSOR_OPT_*` macros for detailed documentations. 420 | * IntoClause * into — target information for `SELECT INTO`, 421 | `CREATE TABLE AS`, and `CREATE MATERIALIZED VIEW`. `NULL` unless 422 | explaining the contents of a `CreateTableAsStmt`. 423 | * ExplainState * es — current explain state. The hook is free to 424 | modify it in order to produce output. 425 | * const char * queryString — an actual query string. 426 | * ParamListInfo params — plan parameters. 427 | * QueryEnvironment * queryEnv — context-specific values. 428 | 429 | *Output:* 430 | 431 | This hook does not produce any output. 432 | 433 | 434 | 435 | # int32 get_attavgwidth_hook(relid, attnum) [<>](https://github.com/postgres/postgres/blob/master/src/include/utils/lsyscache.h#L66 "Source") 436 | 437 | Hook for controlling an algorithm for predicting the average width of entries in the column. 438 | 439 | This hook, if set, should return the average width of entries in the column. 440 | If returned value is greater than `0`, it is returned to the planner. 441 | Otherwise, the default algorithm is invoked. 442 | 443 | *Inputs:* 444 | 445 | * Oid relid — relation id. 446 | * AttrNumber attnum — column number. 447 | 448 | *Output:* 449 | 450 | Average width of entries in the given column of the given relation or zero 451 | to fall back to the default algorithm. 452 | 453 | 454 | # bool get_index_stats_hook(root, indexOid, indexattnum, vardata) [<>](https://github.com/postgres/postgres/blob/master/src/include/utils/selfuncs.h#L146 "Source") 455 | 456 | Hook for overriding index stats lookup. 457 | 458 | Given the planner state and an index, the hook should decide if it can provide 459 | any useful stats. If yes, it should supply a `statsTuple` and a `freefunc` and 460 | return `true`. If no, it should return `false`. 461 | 462 | Note that `freefunc` must be set if `statsTuple` is set. 463 | 464 | Note also that `vardata` should not be changed if `false` is returned. 465 | Postgres will not check whether `statsTuple` and `freefunc` are set. 466 | It will simply overwrite them. 467 | 468 | *Inputs:* 469 | 470 | * PlannerInfo * root — current planner info. 471 | * Oid indexOid — id of the index that we are looking stats for. 472 | * AttrNumber indexattnum — index column. 473 | * VariableStatData * vardata — container for the return value. 474 | 475 | 476 | # void get_relation_info_hook(root, relationObjectId, inhparent, rel) [<>](https://github.com/postgres/postgres/blob/master/src/include/optimizer/plancat.h#L25 "Source") 477 | 478 | Hook for altering results of the relation info lookup. 479 | 480 | This hook allow plugins to editorialize on the info that was obtained from the 481 | catalogs by the default relation info lookup. Actions might include altering 482 | the assumed relation size, removing an index, or adding a hypothetical 483 | index to the `indexlist`. 484 | 485 | *Inputs:* 486 | 487 | * PlannerInfo * root — current planner info. 488 | * Oid relationObjectId — id of the relation that we are looking 489 | info for. 490 | * bool inhparent — if true, all we need to do is set up the attr 491 | arrays: the `RelOptInfo` actually represents the `appendrel` formed by an 492 | inheritance tree, and so the parent rel's physical size and index information 493 | isn't important for it. 494 | * RelOptInfo * rel — relation info that can be adjusted. 495 | 496 | 497 | # bool get_relation_stats_hook(root, rte, attnum, vardata) [<>](https://github.com/postgres/postgres/blob/master/src/include/utils/selfuncs.h#L141 "Source") 498 | 499 | Hook for overriding relation stats lookup. 500 | 501 | Similar to `get_index_stats_hook`, this hook should either return `false` 502 | or take control over relation stats lookup, write output the the `vardata` 503 | container, and return `true`. 504 | 505 | See `get_index_stats_hook` for more details. 506 | 507 | *Inputs:* 508 | 509 | * PlannerInfo * root — current planner info. 510 | * Oid indexOid — id of the index that we are looking stats for. 511 | * AttrNumber indexattnum — index column. 512 | * VariableStatData * vardata — container for the return value. 513 | 514 | 515 | # PlannedStmt * planner_hook(parse, query_string, cursorOptions, boundParams) [<>](https://github.com/postgres/postgres/blob/master/src/include/optimizer/planner.h#L30 "Source") 516 | 517 | Called in query optimizer entry point. 518 | 519 | If set, replaces standard planner. Consider inclusion of the standard planner to hook 520 | if this hook assuming just pre-process or post-process for builtin planner. 521 | 522 | *Inputs:* 523 | 524 | * Query * parse — parsed query text. 525 | * const char * query_string — original query text. 526 | * int cursorOptions 527 | * ParamListInfo boundParams 528 | 529 | 530 | # RelOptInfo * join_search_hook(root, levels_needed, initial_rels) [<>](https://github.com/postgres/postgres/blob/master/src/include/optimizer/paths.h#L49 "Source") 531 | 532 | Called when optimiser chooses order for join relations. 533 | 534 | When the hook is set, replaces GEQO or standard join search. 535 | 536 | *Inputs:* 537 | 538 | * PlannerInfo * root — query plan root. 539 | * int levels_needed — the number of child joinlist nodes. 540 | * List * initial_rels — list of join relations. 541 | 542 | 543 | # void set_rel_pathlist_hook(root, rel, rti, rte) [<>](https://github.com/postgres/postgres/blob/master/src/include/optimizer/paths.h#L34 "Source") 544 | 545 | Called at the end of building access paths for a base relation. 546 | 547 | The hook can apply changes to set of paths by adding new paths or deleting them. 548 | 549 | *Inputs:* 550 | 551 | * PlannerInfo * root 552 | * RelOptInfo * rel - relation info. 553 | * Index rti - range table index. 554 | * RangeTblEntry * rte range table entry. 555 | 556 | 557 | # void set_join_pathlist_hook(root, joinrel, outerrel, innerrel, jointype, extra) [<>](https://github.com/postgres/postgres/blob/master/src/include/optimizer/paths.h#L43 "Source") 558 | 559 | Called at the end of the process of joinrel modification to contain the best paths. 560 | 561 | The hook can manipulate path list to perform a postprocess for best paths. 562 | 563 | *Inputs:* 564 | 565 | * PlannerInfo * root — query plan root. 566 | * RelOptInfo * joinrel — list of paths. 567 | * RelOptInfo * outerrel - list of outer relation paths. 568 | * RelOptInfo * innerrel - list of inner relation paths. 569 | * JoinType jointype - the type of a join. 570 | * JoinPathExtraData * extra 571 | 572 | 573 | # void create_upper_paths_hook(root, stage, input_rel, output_rel) [<>](https://github.com/postgres/postgres/blob/master/src/include/optimizer/planner.h#L38 "Source") 574 | 575 | Called when postprocess of the path of set operations occurs. 576 | 577 | It's a possibility for extensions to contribute path in relation. 578 | 579 | *Inputs:* 580 | 581 | * PlannerInfo * root — query plan root. 582 | * UpperRelationKind stage 583 | * RelOptInfo * input_rel 584 | * RelOptInfo * output_rel 585 | 586 | 587 | # void post_parse_analyze_hook(pstate, query) [<>](https://github.com/postgres/postgres/blob/master/src/include/parser/analyze.h#L25 "Source") 588 | 589 | Called when parse analyze goes, right after performing transformTopLevelStmt(). 590 | 591 | Used in several internal methods: 592 | [pg_analyze_and_rewrite_params()](https://github.com/postgres/postgres/blob/src/backend/tcop/postgres.c#L686), 593 | [parse_analyze()](https://github.com/postgres/postgres/blob/src/backend/parser/analyze.c#L100). 594 | 595 | *Inputs:* 596 | 597 | * ParseState * pstate — parse state filled by query_string and queryEnv. 598 | * Query * query — output result of the transformTopLevelStmt(). 599 | 600 | 601 | ## Executor Hooks 602 | 603 | 604 | 605 | 606 | # void ExecutorStart_hook(queryDesc, eflags) [<>](https://github.com/postgres/postgres/blob/master/src/include/executor/executor.h#L66 "Source") 607 | 608 | Called at the beginning of any execution of any query plan. 609 | 610 | Note: when it set, replaces the [standard_ExecutorStart()](https://github.com/postgres/postgres/blob/src/backend/executor/execMain.c#L149), 611 | which contains a lot of predefined logic. 612 | Consider inclusion of the standard executor to the hook handler 613 | if you assume adding your logic atop. 614 | 615 | *Inputs:* 616 | 617 | * QueryDesc * queryDesc — created by CreateQueryDesc, 618 | tupDesc field of the QueryDesc is filled in to describe the tuples that will be 619 | returned, and the internal fields (estate and planstate) are set up. 620 | * int eflags — contains flag bits as described in executor.h. 621 | 622 | 623 | # void ExecutorRun_hook(queryDesc, direction, count, execute_once) [<>](https://github.com/postgres/postgres/blob/master/src/include/executor/executor.h#L73 "Source") 624 | 625 | Called at any plan execution, after ExecutorStart. 626 | 627 | Replaces [standard_ExecutorRun()](https://github.com/postgres/postgres/blob/src/backend/executor/execMain.c#L308) 628 | 629 | *Inputs:* 630 | 631 | * QueryDesc * queryDesc — query descriptor from the traffic cop. 632 | * ScanDirection direction - if value is NoMovementScanDirection then nothing is done 633 | except to start up/shut down the destination. 634 | * uint64 count — count = 0 is interpreted as no portal limit, i.e., 635 | run to completion. Also note that the count limit is only applied to 636 | retrieved tuples, not for instance to those inserted/updated/deleted by a ModifyTable plan node. 637 | * bool execute_once — becomes equal to true after first execution. 638 | 639 | *Output:* 640 | 641 | This hook should not provide any output. However output tuples (if any) are sent to 642 | the destination receiver specified in the QueryDesc. 643 | The number of tuples processed at the top level can be found in estate->es_processed. 644 | 645 | 646 | # void ExecutorFinish_hook(queryDesc) [<>](https://github.com/postgres/postgres/blob/master/src/include/executor/executor.h#L77 "Source") 647 | 648 | Called after the last ExecutorRun call 649 | 650 | Replaces [standard_ExecutorFinish()](https://github.com/postgres/postgres/blob/src/backend/executor/execMain.c#L408) 651 | 652 | *Inputs:* 653 | 654 | * QueryDesc * queryDesc — query descriptor from the traffic cop. 655 | 656 | 657 | # void ExecutorEnd_hook(queryDesc) [<>](https://github.com/postgres/postgres/blob/master/src/include/executor/executor.h#L81 "Source") 658 | 659 | Called at the end of execution of any query plan. 660 | 661 | * QueryDesc * queryDesc — query descriptor from the traffic cop. 662 | 663 | 664 | # void ProcessUtility_hook(pstmt, queryString, context, params, queryEnv, dest, completionTag) [<>](https://github.com/postgres/postgres/blob/master/src/include/tcop/utility.h#L78 "Source") 665 | 666 | Hook for the ProcessUtility. 667 | 668 | Replaces [standard_ProcessUtility()](https://github.com/postgres/postgres/blob/src/backend/tcop/utility.c#L375) 669 | 670 | This hook should not provide any output. 671 | 672 | *Inputs:* 673 | 674 | * PlannedStmt * pstmt — PlannedStmt wrapper for the utility statement 675 | * const char * queryString — original source text of command, 676 | may be passed multiple times when processing a query string 677 | containing multiple semicolon-separated statements. pstmt->stmt_location and pstmt->stmt_len 678 | indicates the substring containing the current statement. 679 | * ProcessUtilityContext context — identifies source of statement 680 | (toplevel client command, non-toplevel client command, subcommand of a larger utility command) 681 | * ParamListInfo params — parameters of an execution. 682 | * QueryEnvironment * queryEnv — execution environment, optional, can be NULL. 683 | * DestReceiver * dest — results receiver. 684 | * char * completionTag — points to a buffer of size COMPLETION_TAG_BUFSIZE 685 | in which to store a command completion status string 686 | 687 | 688 | ## PL/pgsql Hooks 689 | 690 | 691 | 692 | 693 | # void func_setup(estate, func) [<>](https://github.com/postgres/postgres/blob/master/src/pl/plpgsql/src/plpgsql.h#L1136 "Source") 694 | 695 | Hook for intercepting PLpgSQL function pre-init phase. 696 | 697 | This hook is called when we start a function before we've initialized 698 | the local variables defined by the function. 699 | Can be useful for time measuring of а function initialization in tandem 700 | with [func_beg()](Detailed.md#func_beg) and for measuring total execution time 701 | with the help of [func_end()](Detailed.md#func_end). 702 | 703 | Before any call to func_setup, PLpgSQL fills in the error_callback 704 | and assign_expr fields with pointers to its own plpgsql_exec_error_callback 705 | and exec_assign_expr functions. 706 | 707 | *Inputs:* 708 | 709 | * PLpgSQL_execstate * estate — runtime execution data. 710 | * PLpgSQL_function * func — PLpgSQL compiled function. 711 | 712 | 713 | # void func_beg(estate, func) [<>](https://github.com/postgres/postgres/blob/master/src/pl/plpgsql/src/plpgsql.h#L1137 "Source") 714 | 715 | Hook for intercepting post-init phase. 716 | 717 | This hook is called when we start PLpgSQL function, after we've initialized 718 | the local variables. 719 | The hook can be used for pre-validation of a function arguments. 720 | 721 | *Inputs:* 722 | 723 | * PLpgSQL_execstate * estate — runtime execution data. 724 | * PLpgSQL_function * func — PLpgSQL compiled function. 725 | 726 | 727 | # void func_end(estate, func) [<>](https://github.com/postgres/postgres/blob/master/src/pl/plpgsql/src/plpgsql.h#L1138 "Source") 728 | 729 | Hook for intercepting end of a function. 730 | 731 | This hook is called at the end of PLpgSQL function. 732 | Can be used as a function callback. 733 | 734 | *Inputs:* 735 | 736 | * PLpgSQL_execstate * estate — runtime execution data. 737 | * PLpgSQL_function * func — PLpgSQL compiled function. 738 | 739 | 740 | # void stmt_beg(estate, stmt) [<>](https://github.com/postgres/postgres/blob/master/src/pl/plpgsql/src/plpgsql.h#L1139 "Source") 741 | 742 | Called before each statement of a function. 743 | 744 | *Inputs:* 745 | 746 | * PLpgSQL_execstate * estate — runtime execution data. 747 | * PLpgSQL_stmt * stmt — execution node. 748 | 749 | 750 | # void stmt_end(estate, stmt) [<>](https://github.com/postgres/postgres/blob/master/src/pl/plpgsql/src/plpgsql.h#L1140 "Source") 751 | 752 | Called after each statement of a function. 753 | 754 | *Inputs:* 755 | 756 | * PLpgSQL_execstate * estate — runtime execution data. 757 | * PLpgSQL_stmt * stmt — execution node. 758 | 759 | 760 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | Unofficial documentation for PostgreSQL hooks. 2 | 3 | Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group 4 | 5 | Portions Copyright (c) 1994, The Regents of the University of California 6 | 7 | Permission to use, copy, modify, and distribute this software and its 8 | documentation for any purpose, without fee, and without a written agreement 9 | is hereby granted, provided that the above copyright notice and this 10 | paragraph and the following two paragraphs appear in all copies. 11 | 12 | IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR 13 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING 14 | LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 15 | DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE 16 | POSSIBILITY OF SUCH DAMAGE. 17 | 18 | THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 19 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 20 | AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 | ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO 22 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Postgresql hooks documentation 2 | 3 | Unofficial documentation for PostgreSQL hooks. 4 | 5 | > *Denial of responsibility:* 6 | > 7 | > This work is not a part of the official PostgreSQL documentation. 8 | > 9 | > Contents of this repository were compiled by Begishev Nikita and 10 | > Goncharov Vladimir, neither of whom appear to be a developer or a maintainer 11 | > of the PostgreSQL Database Management System. 12 | > 13 | > Use this documentation at your own risk. 14 | > 15 | > 16 | > *Copyright notice:* 17 | > 18 | > This work combines some research made by contributors with 19 | > information acquired from the postgres source code, comments and 20 | > documentation. Some contents of this work were copied from source code 21 | > comments as is, others were written from scratch. 22 | > 23 | > In no way we (Begishev Nikita and Goncharov Vladimir) claim copyright on texts 24 | > that were copied or adapted from the sources described above. 25 | > 26 | > This work is distributed under the terms of the PostgreSQL License, a copy of 27 | > which may be found in the file called 'License.md'. 28 | 29 | PostgreSQL hooks are a simple way to extend functionality of the database. 30 | They allow extensions to introspect database state, react to events and 31 | interfere with database operations. 32 | 33 | In terms of the programming language, each hook is a pointer to a function 34 | of a specific type, initially set to be `NULL`. 35 | 36 | Upon init, database extensions are free to overwrite those function pointers 37 | with their own values. A previous value of the overwritten pointer is usually 38 | stored withing the extension local memory. 39 | 40 | During its work, postgres checks whether certain function pointers are not null 41 | and if that's the case, calls them. 42 | 43 | See the [detailed description](Detailed.md) for an explanation on 44 | how to implement a hook and an example. 45 | 46 | * [General Hooks](#general-hooks) 47 | * [Security Hooks](#security-hooks) 48 | * [Function Manager Hooks](#function-manager-hooks) 49 | * [Planner Hooks](#planner-hooks) 50 | * [Executor Hooks](#executor-hooks) 51 | * [PL/pgsql Hooks](#plpgsql-hooks) 52 | 53 | 54 | ## [General Hooks](Detailed.md#general-hooks) 55 | 56 | 57 | 58 | * [emit_log_hook](Detailed.md#emit_log_hook) — hook for intercepting messages before they are sent to the server log. 59 | * [shmem_startup_hook](Detailed.md#shmem_startup_hook) — hook for extensions to initialize their shared memory. 60 | 61 | ## [Security Hooks](Detailed.md#security-hooks) 62 | 63 | 64 | 65 | * [check_password_hook](Detailed.md#check_password_hook) — hook for enforcing password constraints and performing action on password change. 66 | * [ClientAuthentication_hook](Detailed.md#ClientAuthentication_hook) — hook for controlling the authentication process. 67 | * [ExecutorCheckPerms_hook](Detailed.md#ExecutorCheckPerms_hook) — hook for adding additional security checks on the per-relation level. 68 | * [object_access_hook](Detailed.md#object_access_hook) — hook to monitor accesses to objects. 69 | * [row_security_policy_hook_permissive](Detailed.md#row_security_policy_hook_permissive) — hook to add policies which are combined with the other permissive policies. 70 | * [row_security_policy_hook_restrictive](Detailed.md#row_security_policy_hook_restrictive) — hook to add policies which are enforced, regardless of other policies. 71 | 72 | ## [Function Manager Hooks](Detailed.md#function-manager-hooks) 73 | 74 | 75 | 76 | * [needs_fmgr_hook](Detailed.md#needs_fmgr_hook) — auxiliary hook which decides whether `fmgr_hook` should be applied to a function. 77 | * [fmgr_hook](Detailed.md#fmgr_hook) — hook for controlling function execution process. 78 | 79 | ## [Planner Hooks](Detailed.md#planner-hooks) 80 | 81 | 82 | 83 | * [explain_get_index_name_hook](Detailed.md#explain_get_index_name_hook) — hook for altering index names in explain statements. 84 | * [ExplainOneQuery_hook](Detailed.md#ExplainOneQuery_hook) — hook for overriding explain procedure for a single query. 85 | * [get_attavgwidth_hook](Detailed.md#get_attavgwidth_hook) — hook for controlling an algorithm for predicting the average width of entries in the column. 86 | * [get_index_stats_hook](Detailed.md#get_index_stats_hook) — hook for overriding index stats lookup. 87 | * [get_relation_info_hook](Detailed.md#get_relation_info_hook) — hook for altering results of the relation info lookup. 88 | * [get_relation_stats_hook](Detailed.md#get_relation_stats_hook) — hook for overriding relation stats lookup. 89 | * [planner_hook](Detailed.md#planner_hook) — called in query optimizer entry point. 90 | * [join_search_hook](Detailed.md#join_search_hook) — called when optimiser chooses order for join relations. 91 | * [set_rel_pathlist_hook](Detailed.md#set_rel_pathlist_hook) — called at the end of building access paths for a base relation. 92 | * [set_join_pathlist_hook](Detailed.md#set_join_pathlist_hook) — called at the end of the process of joinrel modification to contain the best paths. 93 | * [create_upper_paths_hook](Detailed.md#create_upper_paths_hook) — called when postprocess of the path of set operations occurs. 94 | * [post_parse_analyze_hook](Detailed.md#post_parse_analyze_hook) — called when parse analyze goes, right after performing transformTopLevelStmt(). 95 | 96 | ## [Executor Hooks](Detailed.md#executor-hooks) 97 | 98 | 99 | 100 | * [ExecutorStart_hook](Detailed.md#ExecutorStart_hook) — called at the beginning of any execution of any query plan. 101 | * [ExecutorRun_hook](Detailed.md#ExecutorRun_hook) — called at any plan execution, after ExecutorStart. 102 | * [ExecutorFinish_hook](Detailed.md#ExecutorFinish_hook) — called after the last ExecutorRun call 103 | * [ExecutorEnd_hook](Detailed.md#ExecutorEnd_hook) — called at the end of execution of any query plan. 104 | * [ProcessUtility_hook](Detailed.md#ProcessUtility_hook) — hook for the ProcessUtility. 105 | 106 | ## [PL/pgsql Hooks](Detailed.md#plpgsql-hooks) 107 | 108 | 109 | 110 | * [func_setup](Detailed.md#func_setup) — hook for intercepting PLpgSQL function pre-init phase. 111 | * [func_beg](Detailed.md#func_beg) — hook for intercepting post-init phase. 112 | * [func_end](Detailed.md#func_end) — hook for intercepting end of a function. 113 | * [stmt_beg](Detailed.md#stmt_beg) — called before each statement of a function. 114 | * [stmt_end](Detailed.md#stmt_end) — called after each statement of a function. 115 | 116 | -------------------------------------------------------------------------------- /generate.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | import os 4 | 5 | import jinja2 6 | 7 | HookType = namedtuple('HookType', 'name output inputs') 8 | HookInput = namedtuple('HookInput', 'type name') 9 | Hook = namedtuple('Hook', 'type name source_link') 10 | HookSection = namedtuple('HookSection', 'name slug short_desc long_desc hooks') 11 | 12 | 13 | def link(path): 14 | return 'https://github.com/postgres/postgres/blob/master/' + path 15 | 16 | 17 | set_rel_pathlist_hook_type = HookType( 18 | name='set_rel_pathlist_hook_type', 19 | output='void', 20 | inputs=[ 21 | HookInput('PlannerInfo *', 'root'), 22 | HookInput('RelOptInfo *', 'rel'), 23 | HookInput('Index', 'rti'), 24 | HookInput('RangeTblEntry *', 'rte'), 25 | ] 26 | ) 27 | set_join_pathlist_hook_type = HookType( 28 | name='set_join_pathlist_hook_type', 29 | output='void', 30 | inputs=[ 31 | HookInput('PlannerInfo *', 'root'), 32 | HookInput('RelOptInfo *', 'joinrel'), 33 | HookInput('RelOptInfo *', 'outerrel'), 34 | HookInput('RelOptInfo *', 'innerrel'), 35 | HookInput('JoinType', 'jointype'), 36 | HookInput('JoinPathExtraData *', 'extra'), 37 | ] 38 | ) 39 | needs_fmgr_hook_type = HookType( 40 | name='needs_fmgr_hook_type', 41 | output='bool', 42 | inputs=[ 43 | HookInput('Oid', 'fn_oid'), 44 | ] 45 | ) 46 | fmgr_hook_type = HookType( 47 | name='fmgr_hook_type', 48 | output='void', 49 | inputs=[ 50 | HookInput('FmgrHookEventType', 'event'), 51 | HookInput('FmgrInfo *', 'flinfo'), 52 | HookInput('Datum *', 'arg'), 53 | ] 54 | ) 55 | object_access_hook_type = HookType( 56 | name='object_access_hook_type', 57 | output='void', 58 | inputs=[ 59 | HookInput('ObjectAccessType', 'access'), 60 | HookInput('Oid', 'classId'), 61 | HookInput('Oid', 'objectId'), 62 | HookInput('int', 'subId'), 63 | HookInput('void *', 'arg'), 64 | ] 65 | ) 66 | ExplainOneQuery_hook_type = HookType( 67 | name='ExplainOneQuery_hook_type', 68 | output='void', 69 | inputs=[ 70 | HookInput('Query *', 'query'), 71 | HookInput('int', 'cursorOptions'), 72 | HookInput('IntoClause *', 'into'), 73 | HookInput('ExplainState *', 'es'), 74 | HookInput('const char *', 'queryString'), 75 | HookInput('ParamListInfo', 'params'), 76 | HookInput('QueryEnvironment *', 'queryEnv'), 77 | ] 78 | ) 79 | explain_get_index_name_hook_type = HookType( 80 | name='explain_get_index_name_hook_type', 81 | output='const char *', 82 | inputs=[ 83 | HookInput('Oid', 'indexId'), 84 | ] 85 | ) 86 | check_password_hook_type = HookType( 87 | name='check_password_hook_type', 88 | output='void', 89 | inputs=[ 90 | HookInput('const char *', 'username'), 91 | HookInput('const char *', 'shadow_pass'), 92 | HookInput('PasswordType', 'password_type'), 93 | HookInput('Datum', 'validuntil_time'), 94 | HookInput('bool', 'validuntil_null'), 95 | ] 96 | ) 97 | ExecutorStart_hook_type = HookType( 98 | name='ExecutorStart_hook_type', 99 | output='void', 100 | inputs=[ 101 | HookInput('QueryDesc *', 'queryDesc'), 102 | HookInput('int', 'eflags'), 103 | ] 104 | ) 105 | ExecutorRun_hook_type = HookType( 106 | name='ExecutorRun_hook_type', 107 | output='void', 108 | inputs=[ 109 | HookInput('QueryDesc *', 'queryDesc'), 110 | HookInput('ScanDirection', 'direction'), 111 | HookInput('uint64', 'count'), 112 | HookInput('bool', 'execute_once'), 113 | ] 114 | ) 115 | ExecutorFinish_hook_type = HookType( 116 | name='ExecutorFinish_hook_type', 117 | output='void', 118 | inputs=[ 119 | HookInput('QueryDesc *', 'queryDesc'), 120 | ] 121 | ) 122 | ExecutorEnd_hook_type = HookType( 123 | name='ExecutorEnd_hook_type', 124 | output='void', 125 | inputs=[ 126 | HookInput('QueryDesc *', 'queryDesc'), 127 | ] 128 | ) 129 | ExecutorCheckPerms_hook_type = HookType( 130 | name='ExecutorCheckPerms_hook_type', 131 | output='bool', 132 | inputs=[ 133 | HookInput('List *', 'rangeTabls'), 134 | HookInput('bool', 'abort'), 135 | ] 136 | ) 137 | ClientAuthentication_hook_type = HookType( 138 | name='ClientAuthentication_hook_type', 139 | output='void', 140 | inputs=[ 141 | HookInput('Port *', 'port'), 142 | HookInput('int', 'status'), 143 | ] 144 | ) 145 | join_search_hook_type = HookType( 146 | name='join_search_hook_type', 147 | output='RelOptInfo *', 148 | inputs=[ 149 | HookInput('PlannerInfo *', 'root'), 150 | HookInput('int', 'levels_needed'), 151 | HookInput('List *', 'initial_rels'), 152 | ] 153 | ) 154 | get_relation_info_hook_type = HookType( 155 | name='get_relation_info_hook_type', 156 | output='void', 157 | inputs=[ 158 | HookInput('PlannerInfo *', 'root'), 159 | HookInput('Oid', 'relationObjectId'), 160 | HookInput('bool', 'inhparent'), 161 | HookInput('RelOptInfo *', 'rel'), 162 | ] 163 | ) 164 | planner_hook_type = HookType( 165 | name='planner_hook_type', 166 | output='PlannedStmt *', 167 | inputs=[ 168 | HookInput('Query *', 'parse'), 169 | HookInput('const char *', 'query_string'), 170 | HookInput('int', 'cursorOptions'), 171 | HookInput('ParamListInfo', 'boundParams'), 172 | ] 173 | ) 174 | create_upper_paths_hook_type = HookType( 175 | name='create_upper_paths_hook_type', 176 | output='void', 177 | inputs=[ 178 | HookInput('PlannerInfo *', 'root'), 179 | HookInput('UpperRelationKind', 'stage'), 180 | HookInput('RelOptInfo *', 'input_rel'), 181 | HookInput('RelOptInfo *', 'output_rel'), 182 | ] 183 | ) 184 | post_parse_analyze_hook_type = HookType( 185 | name='post_parse_analyze_hook_type', 186 | output='void', 187 | inputs=[ 188 | HookInput('ParseState *', 'pstate'), 189 | HookInput('Query *', 'query'), 190 | ] 191 | ) 192 | row_security_policy_hook_type = HookType( 193 | name='row_security_policy_hook_type', 194 | output='List *', 195 | inputs=[ 196 | HookInput('CmdType', 'cmdtype'), 197 | HookInput('Relation', 'relation'), 198 | ] 199 | ) 200 | shmem_startup_hook_type = HookType( 201 | name='shmem_startup_hook_type', 202 | output='void', 203 | inputs=[ 204 | ] 205 | ) 206 | ProcessUtility_hook_type = HookType( 207 | name='ProcessUtility_hook_type', 208 | output='void', 209 | inputs=[ 210 | HookInput('PlannedStmt *', 'pstmt'), 211 | HookInput('const char *', 'queryString'), 212 | HookInput('ProcessUtilityContext', 'context'), 213 | HookInput('ParamListInfo', 'params'), 214 | HookInput('QueryEnvironment *', 'queryEnv'), 215 | HookInput('DestReceiver *', 'dest'), 216 | HookInput('char *', 'completionTag'), 217 | ] 218 | ) 219 | emit_log_hook_type = HookType( 220 | name='emit_log_hook_type', 221 | output='void', 222 | inputs=[ 223 | HookInput('ErrorData *', 'edata'), 224 | ] 225 | ) 226 | get_attavgwidth_hook_type = HookType( 227 | name='get_attavgwidth_hook_type', 228 | output='int32', 229 | inputs=[ 230 | HookInput('Oid', 'relid'), 231 | HookInput('AttrNumber', 'attnum'), 232 | ] 233 | ) 234 | get_relation_stats_hook_type = HookType( 235 | name='get_relation_stats_hook_type', 236 | output='bool', 237 | inputs=[ 238 | HookInput('PlannerInfo *', 'root'), 239 | HookInput('RangeTblEntry *', 'rte'), 240 | HookInput('AttrNumber', 'attnum'), 241 | HookInput('VariableStatData *', 'vardata'), 242 | ] 243 | ) 244 | get_index_stats_hook_type = HookType( 245 | name='get_index_stats_hook_type', 246 | output='bool', 247 | inputs=[ 248 | HookInput('PlannerInfo *', 'root'), 249 | HookInput('Oid', 'indexOid'), 250 | HookInput('AttrNumber', 'indexattnum'), 251 | HookInput('VariableStatData *', 'vardata'), 252 | ] 253 | ) 254 | func_hook_type = HookType( 255 | name='func_hook_type', 256 | output='void', 257 | inputs=[ 258 | HookInput('PLpgSQL_execstate *', 'estate'), 259 | HookInput('PLpgSQL_function *', 'func'), 260 | ] 261 | ) 262 | stmt_hook_type = HookType( 263 | name='stmt_hook_type', 264 | output='void', 265 | inputs=[ 266 | HookInput('PLpgSQL_execstate *', 'estate'), 267 | HookInput('PLpgSQL_stmt *', 'stmt'), 268 | ] 269 | ) 270 | 271 | sections = [ 272 | HookSection( 273 | 'General Hooks', 274 | 'general-hooks', 275 | '', 276 | '', 277 | [ 278 | Hook( 279 | emit_log_hook_type, 280 | 'emit_log_hook', 281 | link('src/include/utils/elog.h#L415') 282 | ), 283 | Hook( 284 | shmem_startup_hook_type, 285 | 'shmem_startup_hook', 286 | link('src/include/storage/ipc.h#L78') 287 | ), 288 | ] 289 | ), 290 | HookSection( 291 | 'Security Hooks', 292 | 'security-hooks', 293 | '', 294 | '', 295 | [ 296 | Hook( 297 | check_password_hook_type, 298 | 'check_password_hook', 299 | link('src/include/commands/user.h#L25') 300 | ), 301 | Hook( 302 | ClientAuthentication_hook_type, 303 | 'ClientAuthentication_hook', 304 | link('src/include/libpq/auth.h#L29') 305 | ), 306 | Hook( 307 | ExecutorCheckPerms_hook_type, 308 | 'ExecutorCheckPerms_hook', 309 | link('src/include/executor/executor.h#L85') 310 | ), 311 | Hook( 312 | object_access_hook_type, 313 | 'object_access_hook', 314 | link('src/include/catalog/objectaccess.h#L138') 315 | ), 316 | Hook( 317 | row_security_policy_hook_type, 318 | 'row_security_policy_hook_permissive', 319 | link('src/include/rewrite/rowsecurity.h#L40') 320 | ), 321 | Hook( 322 | row_security_policy_hook_type, 323 | 'row_security_policy_hook_restrictive', 324 | link('src/include/rewrite/rowsecurity.h#L42') 325 | ), 326 | ] 327 | ), 328 | HookSection( 329 | 'Function Manager Hooks', 330 | 'function-manager-hooks', 331 | '', 332 | '', 333 | [ 334 | Hook( 335 | needs_fmgr_hook_type, 336 | 'needs_fmgr_hook', 337 | link('src/include/fmgr.h#L775') 338 | ), 339 | Hook( 340 | fmgr_hook_type, 341 | 'fmgr_hook', 342 | link('src/include/fmgr.h#L776') 343 | ), 344 | ] 345 | ), 346 | HookSection( 347 | 'Planner Hooks', 348 | 'planner-hooks', 349 | '', 350 | '', 351 | [ 352 | Hook( 353 | explain_get_index_name_hook_type, 354 | 'explain_get_index_name_hook', 355 | link('src/include/commands/explain.h#L76') 356 | ), 357 | Hook( 358 | ExplainOneQuery_hook_type, 359 | 'ExplainOneQuery_hook', 360 | link('src/include/commands/explain.h#L72') 361 | ), 362 | Hook( 363 | get_attavgwidth_hook_type, 364 | 'get_attavgwidth_hook', 365 | link('src/include/utils/lsyscache.h#L66') 366 | ), 367 | Hook( 368 | get_index_stats_hook_type, 369 | 'get_index_stats_hook', 370 | link('src/include/utils/selfuncs.h#L146') 371 | ), 372 | Hook( 373 | get_relation_info_hook_type, 374 | 'get_relation_info_hook', 375 | link('src/include/optimizer/plancat.h#L25') 376 | ), 377 | Hook( 378 | get_relation_stats_hook_type, 379 | 'get_relation_stats_hook', 380 | link('src/include/utils/selfuncs.h#L141') 381 | ), 382 | Hook( 383 | planner_hook_type, 384 | 'planner_hook', 385 | link('src/include/optimizer/planner.h#L30') 386 | ), 387 | Hook( 388 | join_search_hook_type, 389 | 'join_search_hook', 390 | link('src/include/optimizer/paths.h#L49') 391 | ), 392 | Hook( # TODO: does it really belong to this section? 393 | set_rel_pathlist_hook_type, 394 | 'set_rel_pathlist_hook', 395 | link('src/include/optimizer/paths.h#L34') 396 | ), 397 | Hook( # TODO: does it really belong to this section? 398 | set_join_pathlist_hook_type, 399 | 'set_join_pathlist_hook', 400 | link('src/include/optimizer/paths.h#L43') 401 | ), 402 | Hook( # TODO: does it really belong to this section? 403 | create_upper_paths_hook_type, 404 | 'create_upper_paths_hook', 405 | link('src/include/optimizer/planner.h#L38') 406 | ), 407 | Hook( # TODO: does it really belongs to this section 408 | post_parse_analyze_hook_type, 409 | 'post_parse_analyze_hook', 410 | link('src/include/parser/analyze.h#L25') 411 | ), 412 | ] 413 | ), 414 | HookSection( 415 | 'Executor Hooks', 416 | 'executor-hooks', 417 | '', 418 | '', 419 | [ 420 | Hook( 421 | ExecutorStart_hook_type, 422 | 'ExecutorStart_hook', 423 | link('src/include/executor/executor.h#L66') 424 | ), 425 | Hook( 426 | ExecutorRun_hook_type, 427 | 'ExecutorRun_hook', 428 | link('src/include/executor/executor.h#L73') 429 | ), 430 | Hook( 431 | ExecutorFinish_hook_type, 432 | 'ExecutorFinish_hook', 433 | link('src/include/executor/executor.h#L77') 434 | ), 435 | Hook( 436 | ExecutorEnd_hook_type, 437 | 'ExecutorEnd_hook', 438 | link('src/include/executor/executor.h#L81') 439 | ), 440 | Hook( 441 | ProcessUtility_hook_type, 442 | 'ProcessUtility_hook', 443 | link('src/include/tcop/utility.h#L78') 444 | ), 445 | ] 446 | ), 447 | HookSection( 448 | 'PL/pgsql Hooks', 449 | 'plpgsql-hooks', 450 | '', 451 | '', 452 | [ 453 | Hook( 454 | func_hook_type, 455 | 'func_setup', 456 | link('src/pl/plpgsql/src/plpgsql.h#L1136') 457 | ), 458 | Hook( 459 | func_hook_type, 460 | 'func_beg', 461 | link('src/pl/plpgsql/src/plpgsql.h#L1137') 462 | ), 463 | Hook( 464 | func_hook_type, 465 | 'func_end', 466 | link('src/pl/plpgsql/src/plpgsql.h#L1138') 467 | ), 468 | Hook( 469 | stmt_hook_type, 470 | 'stmt_beg', 471 | link('src/pl/plpgsql/src/plpgsql.h#L1139') 472 | ), 473 | Hook( 474 | stmt_hook_type, 475 | 'stmt_end', 476 | link('src/pl/plpgsql/src/plpgsql.h#L1140') 477 | ), 478 | ] 479 | ), 480 | ] 481 | 482 | 483 | def make_short_description(hook): 484 | path = 'templates/hooks/' + hook.name + '.md' 485 | 486 | if not os.path.exists(path): 487 | return '' 488 | 489 | with open(path, encoding='utf-8') as hook_text: 490 | for line in hook_text: 491 | line = line.strip() 492 | if line: 493 | return line[0].lower() + line[1:] 494 | 495 | 496 | def move_before_generation(output_path): 497 | if not os.path.exists(output_path): 498 | return 499 | 500 | path, ext = os.path.splitext(output_path) 501 | 502 | i = 1 503 | while os.path.exists(path + '.old.' + str(i) + ext): 504 | i += 1 505 | 506 | os.rename(output_path, path + '.old.' + str(i) + ext) 507 | 508 | 509 | def write_template(template_path, output_path, **context): 510 | with open(template_path, encoding='utf-8') as template_text: 511 | environment = jinja2.Environment(loader=jinja2.FileSystemLoader('.')) 512 | environment.globals['make_short_description'] = make_short_description 513 | template = environment.from_string(template_text.read()) 514 | with open(output_path, 'w', encoding='utf-8') as f: 515 | f.write(template.render(**context)) 516 | 517 | 518 | def main(): 519 | for section in sections: 520 | for hook in section.hooks: 521 | path = 'templates/hooks/' + hook.name + '.md' 522 | if os.path.exists(path): 523 | continue 524 | write_template('templates/Hook.md', path, hook=hook) 525 | 526 | move_before_generation('Readme.md') 527 | write_template('templates/Readme.md', 'Readme.md', sections=sections) 528 | 529 | move_before_generation('Detailed.md') 530 | write_template('templates/Detailed.md', 'Detailed.md', sections=sections) 531 | 532 | 533 | if __name__ == '__main__': 534 | main() 535 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | jinja2 2 | -------------------------------------------------------------------------------- /templates/Detailed.md: -------------------------------------------------------------------------------- 1 | # Postgresql hooks documentation 2 | 3 | {% include "templates/DetailedHeader.md" %} 4 | 5 | ## Table of contents 6 | 7 | {% for section in sections -%} 8 | * [{{ section.name }}](#{{ section.slug }}) 9 | {% endfor %} 10 | 11 | {% for section in sections -%} 12 | ## {{ section.name }} 13 | 14 | {{ section.long_desc }} 15 | 16 | {% for hook in section.hooks %} 17 | # {{ hook.type.output }} {{ hook.name }}({% for input in hook.type.inputs %}{{ input.name }}{% if loop.nextitem is defined %}, {% endif %}{% endfor %}) [<>]({{ hook.source_link }} "Source") 18 | 19 | {% include "templates/hooks/" + hook.name + ".md" %} 20 | 21 | {% endfor %} 22 | {% endfor %} 23 | -------------------------------------------------------------------------------- /templates/DetailedHeader.md: -------------------------------------------------------------------------------- 1 | PostgreSQL hooks are a simple way to extend functionality of the database. 2 | They allow extensions to introspect database state, react to events and 3 | interfere with database operations. 4 | 5 | Every hook is a pointer to a function, initially set to `NULL`. 6 | 7 | When postgres wants to call a hook, it checks whether the pointer for that 8 | hook is not null and if that's the case, calls the registered function. 9 | 10 | Extensions can update these pointers during the init procedure 11 | in order to register a new handler for a hook. 12 | 13 | That is, when extension is loaded, postgres calls its `_PG_init` function. 14 | Once called, it can alter hook variables which are a part of the public binary 15 | interface. 16 | 17 | A usual setup would include saving the previous value of the hook variable 18 | and writing pointer to a handler defined by extension. 19 | 20 | Saving the previous value is important because another extension could've 21 | registered its own hook handler. If that's the case, we'd like to call it in 22 | our hook so that this extension can operate without errors. Any well-designed 23 | plugin will do such hook chaining. 24 | 25 | To pop the state of the hook created by one extension `_PG_fini` function must be implemented, 26 | which is basically recovers hook to it's value before `_PG_init`. 27 | 28 | A standard example on how to use hooks is the `auth_delay` plugin. 29 | This plugin delays error report in case of user authentication failure, 30 | which is useful to block password brute-forcing. 31 | 32 | ```c 33 | // We store previously assigned hook pointer in a global variable. 34 | static ClientAuthentication_hook_type original_client_auth_hook = NULL; 35 | 36 | // Our hook implementation. 37 | static void auth_delay_checks(Port *port, int status) 38 | { 39 | // If any other extension registered its own hook handler, 40 | // call it before performing our own logic. 41 | if (original_client_auth_hook) 42 | original_client_auth_hook(port, status); 43 | 44 | // If authentication failed, we wait for one second before returning 45 | // control to the caller. 46 | if (status != STATUS_OK) 47 | { 48 | pg_usleep(1000000L); 49 | } 50 | } 51 | 52 | // Called upon extension load. 53 | void _PG_init(void) 54 | { 55 | // Save the original hook value. 56 | original_client_auth_hook = ClientAuthentication_hook; 57 | // Register our handler. 58 | ClientAuthentication_hook = auth_delay_checks; 59 | } 60 | 61 | // Called with extension unload. 62 | void _PG_fini(void) 63 | { 64 | // Return back the original hook value. 65 | ClientAuthentication_hook = original_client_auth_hook; 66 | } 67 | 68 | ``` 69 | -------------------------------------------------------------------------------- /templates/Header.md: -------------------------------------------------------------------------------- 1 | Unofficial documentation for PostgreSQL hooks. 2 | 3 | > *Denial of responsibility:* 4 | > 5 | > This work is not a part of the official PostgreSQL documentation. 6 | > 7 | > Contents of this repository were compiled by Begishev Nikita and 8 | > Goncharov Vladimir, neither of whom appear to be a developer or a maintainer 9 | > of the PostgreSQL Database Management System. 10 | > 11 | > Use this documentation at your own risk. 12 | > 13 | > 14 | > *Copyright notice:* 15 | > 16 | > This work combines some research made by contributors with 17 | > information acquired from the postgres source code, comments and 18 | > documentation. Some contents of this work were copied from source code 19 | > comments as is, others were written from scratch. 20 | > 21 | > In no way we (Begishev Nikita and Goncharov Vladimir) claim copyright on texts 22 | > that were copied or adapted from the sources described above. 23 | > 24 | > This work is distributed under the terms of the PostgreSQL License, a copy of 25 | > which may be found in the file called 'License.md'. 26 | 27 | PostgreSQL hooks are a simple way to extend functionality of the database. 28 | They allow extensions to introspect database state, react to events and 29 | interfere with database operations. 30 | 31 | In terms of the programming language, each hook is a pointer to a function 32 | of a specific type, initially set to be `NULL`. 33 | 34 | Upon init, database extensions are free to overwrite those function pointers 35 | with their own values. A previous value of the overwritten pointer is usually 36 | stored withing the extension local memory. 37 | 38 | During its work, postgres checks whether certain function pointers are not null 39 | and if that's the case, calls them. 40 | 41 | See the [detailed description](Detailed.md) for an explanation on 42 | how to implement a hook and an example. 43 | -------------------------------------------------------------------------------- /templates/Hook.md: -------------------------------------------------------------------------------- 1 | Short description of this hook. 2 | 3 | Remember to mention when it's called, what should it do, what inputs supplied to this hook, 4 | what output is expected and (shortly) how postgres changes its behavior based on received output. 5 | It may be helpful to mention any common use-cases for this hook or some 6 | extensions that are using this hook. 7 | 8 | *Inputs:* 9 | 10 | {% if hook.type.inputs -%} 11 | Briefly describe hook inputs. Are inputs preprocessed somehow before calling the hook? 12 | Are there any special input states? Can they be null (e.g. `nullptr`)? 13 | 14 | {% for input in hook.type.inputs -%} 15 | * {{ input.type }} {{ input.name }} — ... 16 | {% endfor %} 17 | {%- else -%} 18 | There are no inputs for this hook. Is there a global state this hook should introspect? 19 | {%- endif %} 20 | *Output:* 21 | 22 | {% if hook.type.output != 'void' -%} 23 | Describe hook output. Are there any constraints for the output value? 24 | How postgres changes its behavior based on received output? 25 | Are there any special cases for output, e.g. returning `-1` or `nullptr`? 26 | Are there any mutable inputs this hook should change? 27 | {%- endif %} 28 | -------------------------------------------------------------------------------- /templates/Readme.md: -------------------------------------------------------------------------------- 1 | # Postgresql hooks documentation 2 | 3 | {% include "templates/Header.md" %} 4 | 5 | {% for section in sections -%} 6 | * [{{ section.name }}](#{{ section.slug }}) 7 | {% endfor %} 8 | 9 | {% for section in sections -%} 10 | ## [{{ section.name }}](Detailed.md#{{ section.slug }}) 11 | 12 | {{ section.short_desc }} 13 | 14 | {% for hook in section.hooks -%} 15 | * [{{ hook.name }}](Detailed.md#{{ hook.name }}) — {{ make_short_description(hook) }} 16 | {% endfor %} 17 | {% endfor %} 18 | -------------------------------------------------------------------------------- /templates/hooks/ClientAuthentication_hook.md: -------------------------------------------------------------------------------- 1 | Hook for controlling the authentication process. 2 | 3 | Called after finishing user authentication (regardless of whether authentication 4 | succeed or not). 5 | 6 | This hook will be called for every connection that passed authentication. 7 | However, it is not guaranteed to be called if there are issues with the 8 | connection itself. For example, SSL verification failure or pg_hba.conf 9 | check failure will close the connection without calling this hook. 10 | 11 | *Inputs:* 12 | 13 | * Port * port — full info about the connection and 14 | the connected user. 15 | * int status — a standard status code. `STATUS_OK` (`0`) 16 | if authentication successful. 17 | -------------------------------------------------------------------------------- /templates/hooks/ExecutorCheckPerms_hook.md: -------------------------------------------------------------------------------- 1 | Hook for adding additional security checks on the per-relation level. 2 | 3 | Given a relations list, this hook should return `true` if access is granted. 4 | `false`, if access is not granted and `abort` is `false`. If `abort` is `true` 5 | and access is not granted, it should throw an appropriate error. 6 | 7 | This hook is not called if the standard permission check procedure denies 8 | access to any relation in the list. Therefore, there is no way to actually 9 | raise user privileges. 10 | 11 | Theoretically, only plain-relation RTEs need to be checked in this hook. 12 | Function RTEs are checked during the function preparation procedure. 13 | Join, subquery, and special RTEs need no checks. 14 | 15 | *Inputs:* 16 | 17 | * List * rangeTabls — list of `RangeTblEntry` objects that needs 18 | checking. 19 | * bool abort — if `true`, raise `aclcheck_error` instead of 20 | returning `false` from the hook. 21 | 22 | *Output:* 23 | 24 | `true` if user have privileges to access given relations, `false` or raise an 25 | error otherwise, depending on the `abort` flag. 26 | -------------------------------------------------------------------------------- /templates/hooks/ExecutorEnd_hook.md: -------------------------------------------------------------------------------- 1 | Called at the end of execution of any query plan. 2 | 3 | * QueryDesc * queryDesc — query descriptor from the traffic cop. 4 | -------------------------------------------------------------------------------- /templates/hooks/ExecutorFinish_hook.md: -------------------------------------------------------------------------------- 1 | Called after the last ExecutorRun call 2 | 3 | Replaces [standard_ExecutorFinish()](https://github.com/postgres/postgres/blob/src/backend/executor/execMain.c#L408) 4 | 5 | *Inputs:* 6 | 7 | * QueryDesc * queryDesc — query descriptor from the traffic cop. -------------------------------------------------------------------------------- /templates/hooks/ExecutorRun_hook.md: -------------------------------------------------------------------------------- 1 | Called at any plan execution, after ExecutorStart. 2 | 3 | Replaces [standard_ExecutorRun()](https://github.com/postgres/postgres/blob/src/backend/executor/execMain.c#L308) 4 | 5 | *Inputs:* 6 | 7 | * QueryDesc * queryDesc — query descriptor from the traffic cop. 8 | * ScanDirection direction - if value is NoMovementScanDirection then nothing is done 9 | except to start up/shut down the destination. 10 | * uint64 count — count = 0 is interpreted as no portal limit, i.e., 11 | run to completion. Also note that the count limit is only applied to 12 | retrieved tuples, not for instance to those inserted/updated/deleted by a ModifyTable plan node. 13 | * bool execute_once — becomes equal to true after first execution. 14 | 15 | *Output:* 16 | 17 | This hook should not provide any output. However output tuples (if any) are sent to 18 | the destination receiver specified in the QueryDesc. 19 | The number of tuples processed at the top level can be found in estate->es_processed. -------------------------------------------------------------------------------- /templates/hooks/ExecutorStart_hook.md: -------------------------------------------------------------------------------- 1 | Called at the beginning of any execution of any query plan. 2 | 3 | Note: when it set, replaces the [standard_ExecutorStart()](https://github.com/postgres/postgres/blob/src/backend/executor/execMain.c#L149), 4 | which contains a lot of predefined logic. 5 | Consider inclusion of the standard executor to the hook handler 6 | if you assume adding your logic atop. 7 | 8 | *Inputs:* 9 | 10 | * QueryDesc * queryDesc — created by CreateQueryDesc, 11 | tupDesc field of the QueryDesc is filled in to describe the tuples that will be 12 | returned, and the internal fields (estate and planstate) are set up. 13 | * int eflags — contains flag bits as described in executor.h. 14 | -------------------------------------------------------------------------------- /templates/hooks/ExplainOneQuery_hook.md: -------------------------------------------------------------------------------- 1 | Hook for overriding explain procedure for a single query. 2 | 3 | This hook, if present, should generate explanation for the given query 4 | using other `Explain*` functions and modifying the explain state. 5 | 6 | The default behaviour is to plan query using `pg_plan_query()` and than 7 | delegate printing to the `ExplainOnePlan()` function. 8 | 9 | *Inputs:* 10 | 11 | * Query * query — query that needs explanation. 12 | * int cursorOptions — cursor options in form of a per-bit enum. 13 | See `CURSOR_OPT_*` macros for detailed documentations. 14 | * IntoClause * into — target information for `SELECT INTO`, 15 | `CREATE TABLE AS`, and `CREATE MATERIALIZED VIEW`. `NULL` unless 16 | explaining the contents of a `CreateTableAsStmt`. 17 | * ExplainState * es — current explain state. The hook is free to 18 | modify it in order to produce output. 19 | * const char * queryString — an actual query string. 20 | * ParamListInfo params — plan parameters. 21 | * QueryEnvironment * queryEnv — context-specific values. 22 | 23 | *Output:* 24 | 25 | This hook does not produce any output. 26 | 27 | -------------------------------------------------------------------------------- /templates/hooks/ProcessUtility_hook.md: -------------------------------------------------------------------------------- 1 | Hook for the ProcessUtility. 2 | 3 | Replaces [standard_ProcessUtility()](https://github.com/postgres/postgres/blob/src/backend/tcop/utility.c#L375) 4 | 5 | This hook should not provide any output. 6 | 7 | *Inputs:* 8 | 9 | * PlannedStmt * pstmt — PlannedStmt wrapper for the utility statement 10 | * const char * queryString — original source text of command, 11 | may be passed multiple times when processing a query string 12 | containing multiple semicolon-separated statements. pstmt->stmt_location and pstmt->stmt_len 13 | indicates the substring containing the current statement. 14 | * ProcessUtilityContext context — identifies source of statement 15 | (toplevel client command, non-toplevel client command, subcommand of a larger utility command) 16 | * ParamListInfo params — parameters of an execution. 17 | * QueryEnvironment * queryEnv — execution environment, optional, can be NULL. 18 | * DestReceiver * dest — results receiver. 19 | * char * completionTag — points to a buffer of size COMPLETION_TAG_BUFSIZE 20 | in which to store a command completion status string -------------------------------------------------------------------------------- /templates/hooks/check_password_hook.md: -------------------------------------------------------------------------------- 1 | Hook for enforcing password constraints and performing action on password change. 2 | 3 | This hook is called whenever a new role is created via the `CREATE ROLE` 4 | statement or a password for an existing role is changed via the `ALTER ROLE` 5 | statement. Given a shadow password and some additional info, this hook can 6 | raise an error using the standard `ereport` mechanism if the password 7 | isn't strong enough. 8 | 9 | *Inputs:* 10 | 11 | * const char * username — name of the created/altered role. 12 | * const char * shadow_pass — a shadow pass, i.e. a plain password 13 | or a password hash. 14 | * PasswordType password_type — type of the password. 15 | `PASSWORD_TYPE_MD5` for an md5-encrypted password, 16 | `PASSWORD_TYPE_SCRAM_SHA_256` for a sha-256-encrypted password, 17 | `PASSWORD_TYPE_PLAINTEXT` for a plaintext password. 18 | * Datum validuntil_time — date upon which this password expires. 19 | * bool validuntil_null — a flag that is true if and only if 20 | the password have no expiration date (i.e. a null date is passed). 21 | -------------------------------------------------------------------------------- /templates/hooks/create_upper_paths_hook.md: -------------------------------------------------------------------------------- 1 | Called when postprocess of the path of set operations occurs. 2 | 3 | It's a possibility for extensions to contribute path in relation. 4 | 5 | *Inputs:* 6 | 7 | * PlannerInfo * root — query plan root. 8 | * UpperRelationKind stage 9 | * RelOptInfo * input_rel 10 | * RelOptInfo * output_rel -------------------------------------------------------------------------------- /templates/hooks/emit_log_hook.md: -------------------------------------------------------------------------------- 1 | Hook for intercepting messages before they are sent to the server log. 2 | 3 | This hook is called just before sending an error message to the server log 4 | and to the client. The purpose of this hook is to invoke an additional 5 | logic and possibly prevent this error message from being added to the 6 | server log. 7 | 8 | This hook is useful for implementing custom logging process. 9 | 10 | *Inputs:* 11 | 12 | * ErrorData * edata — a structure which holds a complete info 13 | about the error message. Despite `edata` is a non-const pointer, the only 14 | supported change in the given structure is setting `output_to_server` to 15 | `false`. That is, any other change, including setting `output_to_server` to 16 | `true`, considered not supported. 17 | 18 | Note: despite any other changes to the edata are not officially supported 19 | (as per comment [on line 1455 of the elog.c][emit_log_hook_1])), 20 | postgres actually checks for both `output_to_server` and `output_to_client` 21 | flags. 22 | 23 | [emit_log_hook_1]: https://github.com/postgres/postgres/blob/master/src/backend/utils/error/elog.c#L1456 24 | -------------------------------------------------------------------------------- /templates/hooks/explain_get_index_name_hook.md: -------------------------------------------------------------------------------- 1 | Hook for altering index names in explain statements. 2 | 3 | Extensions may override the default name generation mechanism 4 | so that plans involving hypothetical indexes can be explained. 5 | 6 | *Inputs:* 7 | 8 | * Oid indexId — index id. 9 | 10 | *Output:* 11 | 12 | Name of the index or `NULL`. In the later case, a default name 13 | will be generated. 14 | -------------------------------------------------------------------------------- /templates/hooks/fmgr_hook.md: -------------------------------------------------------------------------------- 1 | Hook for controlling function execution process. 2 | 3 | This hook is intended as support for loadable security policy modules, which may 4 | want to perform additional privilege checks on function entry or exit, 5 | or to do other internal bookkeeping. 6 | 7 | It is invoked whenever postgres executes a function which was explicitly 8 | marked as hookable by `needs_fmgr_hook`. For each execution this hook is fired 9 | exactly twice: first time before invoking the function, second time after 10 | the function returns/throws. 11 | 12 | Note that there is a change that this hook will be called even if a function 13 | is not of interest of your extension (maybe some other extension made it 14 | hookable via its `needs_fmgr_hook`). 15 | 16 | *Inputs:* 17 | 18 | * FmgrHookEventType event — event type, can be one of 19 | `FHET_START`, `FHET_END`, `FHET_ABORT`. 20 | * FmgrInfo * flinfo — function info, including its id and 21 | arguments specification. 22 | * Datum * arg — function arguments. 23 | -------------------------------------------------------------------------------- /templates/hooks/func_beg.md: -------------------------------------------------------------------------------- 1 | Hook for intercepting post-init phase. 2 | 3 | This hook is called when we start PLpgSQL function, after we've initialized 4 | the local variables. 5 | The hook can be used for pre-validation of a function arguments. 6 | 7 | *Inputs:* 8 | 9 | * PLpgSQL_execstate * estate — runtime execution data. 10 | * PLpgSQL_function * func — PLpgSQL compiled function. -------------------------------------------------------------------------------- /templates/hooks/func_end.md: -------------------------------------------------------------------------------- 1 | Hook for intercepting end of a function. 2 | 3 | This hook is called at the end of PLpgSQL function. 4 | Can be used as a function callback. 5 | 6 | *Inputs:* 7 | 8 | * PLpgSQL_execstate * estate — runtime execution data. 9 | * PLpgSQL_function * func — PLpgSQL compiled function. -------------------------------------------------------------------------------- /templates/hooks/func_setup.md: -------------------------------------------------------------------------------- 1 | Hook for intercepting PLpgSQL function pre-init phase. 2 | 3 | This hook is called when we start a function before we've initialized 4 | the local variables defined by the function. 5 | Can be useful for time measuring of а function initialization in tandem 6 | with [func_beg()](Detailed.md#func_beg) and for measuring total execution time 7 | with the help of [func_end()](Detailed.md#func_end). 8 | 9 | Before any call to func_setup, PLpgSQL fills in the error_callback 10 | and assign_expr fields with pointers to its own plpgsql_exec_error_callback 11 | and exec_assign_expr functions. 12 | 13 | *Inputs:* 14 | 15 | * PLpgSQL_execstate * estate — runtime execution data. 16 | * PLpgSQL_function * func — PLpgSQL compiled function. -------------------------------------------------------------------------------- /templates/hooks/get_attavgwidth_hook.md: -------------------------------------------------------------------------------- 1 | Hook for controlling an algorithm for predicting the average width of entries in the column. 2 | 3 | This hook, if set, should return the average width of entries in the column. 4 | If returned value is greater than `0`, it is returned to the planner. 5 | Otherwise, the default algorithm is invoked. 6 | 7 | *Inputs:* 8 | 9 | * Oid relid — relation id. 10 | * AttrNumber attnum — column number. 11 | 12 | *Output:* 13 | 14 | Average width of entries in the given column of the given relation or zero 15 | to fall back to the default algorithm. 16 | -------------------------------------------------------------------------------- /templates/hooks/get_index_stats_hook.md: -------------------------------------------------------------------------------- 1 | Hook for overriding index stats lookup. 2 | 3 | Given the planner state and an index, the hook should decide if it can provide 4 | any useful stats. If yes, it should supply a `statsTuple` and a `freefunc` and 5 | return `true`. If no, it should return `false`. 6 | 7 | Note that `freefunc` must be set if `statsTuple` is set. 8 | 9 | Note also that `vardata` should not be changed if `false` is returned. 10 | Postgres will not check whether `statsTuple` and `freefunc` are set. 11 | It will simply overwrite them. 12 | 13 | *Inputs:* 14 | 15 | * PlannerInfo * root — current planner info. 16 | * Oid indexOid — id of the index that we are looking stats for. 17 | * AttrNumber indexattnum — index column. 18 | * VariableStatData * vardata — container for the return value. 19 | -------------------------------------------------------------------------------- /templates/hooks/get_relation_info_hook.md: -------------------------------------------------------------------------------- 1 | Hook for altering results of the relation info lookup. 2 | 3 | This hook allow plugins to editorialize on the info that was obtained from the 4 | catalogs by the default relation info lookup. Actions might include altering 5 | the assumed relation size, removing an index, or adding a hypothetical 6 | index to the `indexlist`. 7 | 8 | *Inputs:* 9 | 10 | * PlannerInfo * root — current planner info. 11 | * Oid relationObjectId — id of the relation that we are looking 12 | info for. 13 | * bool inhparent — if true, all we need to do is set up the attr 14 | arrays: the `RelOptInfo` actually represents the `appendrel` formed by an 15 | inheritance tree, and so the parent rel's physical size and index information 16 | isn't important for it. 17 | * RelOptInfo * rel — relation info that can be adjusted. 18 | -------------------------------------------------------------------------------- /templates/hooks/get_relation_stats_hook.md: -------------------------------------------------------------------------------- 1 | Hook for overriding relation stats lookup. 2 | 3 | Similar to `get_index_stats_hook`, this hook should either return `false` 4 | or take control over relation stats lookup, write output the the `vardata` 5 | container, and return `true`. 6 | 7 | See `get_index_stats_hook` for more details. 8 | 9 | *Inputs:* 10 | 11 | * PlannerInfo * root — current planner info. 12 | * Oid indexOid — id of the index that we are looking stats for. 13 | * AttrNumber indexattnum — index column. 14 | * VariableStatData * vardata — container for the return value. 15 | -------------------------------------------------------------------------------- /templates/hooks/join_search_hook.md: -------------------------------------------------------------------------------- 1 | Called when optimiser chooses order for join relations. 2 | 3 | When the hook is set, replaces GEQO or standard join search. 4 | 5 | *Inputs:* 6 | 7 | * PlannerInfo * root — query plan root. 8 | * int levels_needed — the number of child joinlist nodes. 9 | * List * initial_rels — list of join relations. -------------------------------------------------------------------------------- /templates/hooks/needs_fmgr_hook.md: -------------------------------------------------------------------------------- 1 | Auxiliary hook which decides whether `fmgr_hook` should be applied to a function. 2 | 3 | Given a function id, decide whether `fmgr_hook` should be called upon executing 4 | this function. 5 | 6 | The result of this hook should be combined with the result of a previously 7 | registered `needs_fmgr_hook` via the `OR` clause. This is required to ensure 8 | that other extensions can hook function even though this very extension does 9 | not hook them. Such behavior is vital for proper work of the security extensions. 10 | 11 | Note that hooked functions are not inlined. 12 | 13 | *Inputs:* 14 | 15 | * Oid fn_oid — id of a function which needs hooking. 16 | 17 | *Output:* 18 | 19 | Return `true` if you want to hook enter/exit event for this function. 20 | -------------------------------------------------------------------------------- /templates/hooks/object_access_hook.md: -------------------------------------------------------------------------------- 1 | Hook to monitor accesses to objects. 2 | 3 | Object access hooks are called just before or just after performing certain 4 | actions on an SQL object. This is intended as infrastructure for security 5 | or logging extensions. 6 | 7 | There are several types of actions defined in `ObjectAccessType`: 8 | 9 | `OAT_POST_CREATE`: hook is invoked just after the object is created. 10 | Typically, this is done after inserting the primary catalog records and 11 | associated dependencies. 12 | 13 | `OAT_DROP`: hook is invoked just before deletion of objects. 14 | 15 | `OAT_POST_ALTER`: hook is invoked just after the object is altered, 16 | but before the command counter is incremented. An extension using the 17 | hook can use a current MVCC snapshot to get the old version of the tuple, 18 | and can use `SnapshotSelf` to get the new version of the tuple. 19 | 20 | `OAT_NAMESPACE_SEARCH`: hook is invoked prior to object name lookup under 21 | a particular namespace. This event is equivalent to usage permission 22 | on a schema under the default access control mechanism. 23 | 24 | `OAT_FUNCTION_EXECUTE`: hook is invoked prior to function execution. 25 | This event is almost equivalent to execute permission on functions, 26 | except for the case when execute permission is checked during object 27 | creation or altering, because `OAT_POST_CREATE` or `OAT_POST_ALTER` are 28 | sufficient for extensions to track these kind of checks. 29 | 30 | Other types may be added in the future. 31 | 32 | *Inputs:* 33 | 34 | For different access types, inputs of this hook mean different things. 35 | 36 | * ObjectAccessType access — access type. 37 | * Oid classId — id of a relation which contains this object. 38 | You can determine type of an object by this parameter. 39 | * Oid objectId — object that is being accessed. 40 | * int subId — subitem within object (e.g. column), or 0. 41 | * void * arg — access type specific argument. 42 | 43 | For `OAT_POST_CREATE`, `arg` is a pointer to `ObjectAccessPostCreate` 44 | structure, which contain a single field, namely `is_internal`. This field 45 | describes whether the context of this creation is invoked by user's 46 | operations, or not. As for `subId`, I've counted two cases when it's non-zero. 47 | The first is when creating a column, and the second one is when creating 48 | a default expression on a column. In either case, `subId` is 49 | an `AttrNumber` of a column. 50 | 51 | For `OAT_DROP` type, `arg` is a pointer to `ObjectAccessPostCreate` structure. 52 | It contains a single field called `dropflags`. They inform extensions the 53 | context of this deletion. 54 | 55 | For `OAT_POST_ALTER` type, `arg` is a pointer to `ObjectAccessPostAlter` 56 | structure. It contains an `is_internal` flag (see `OAT_POST_CREATE`) and an 57 | `auxiliary_id`. The latter is used when system catalog takes two IDs to 58 | identify a particular tuple of the catalog. It is only used when the caller want 59 | to identify an entry of pg_inherits, pg_db_role_setting or pg_user_mapping. 60 | Elsewhere, InvalidOid is be set. 61 | 62 | For `OAT_NAMESPACE_SEARCH` type, `subId` is unused, `classId` is always 63 | `NamespaceRelationId`, and `arg` is a pointer to `ObjectAccessNamespaceSearch`. 64 | 65 | `ObjectAccessNamespaceSearch` structure contain two fields. The first one, 66 | `ereport_on_violation`, indicates that the hook should raise an error when 67 | permission to search this schema is denied. The second one, `result`, is in fact 68 | an out parameter. Core code should initialize this to true, and any extension 69 | that wants to deny access should reset it to false. But an extension should be 70 | careful never to store a true value here, so that in case there are multiple 71 | extensions access is only allowed if all extensions agree. 72 | 73 | For `OAT_FUNCTION_EXECUTE` type, `subId` and `arg` are unused, and 74 | `classId` is always `ProcedureRelationId`. 75 | -------------------------------------------------------------------------------- /templates/hooks/planner_hook.md: -------------------------------------------------------------------------------- 1 | Called in query optimizer entry point. 2 | 3 | If set, replaces standard planner. Consider inclusion of the standard planner to hook 4 | if this hook assuming just pre-process or post-process for builtin planner. 5 | 6 | *Inputs:* 7 | 8 | * Query * parse — parsed query text. 9 | * const char * query_string — original query text. 10 | * int cursorOptions 11 | * ParamListInfo boundParams 12 | -------------------------------------------------------------------------------- /templates/hooks/post_parse_analyze_hook.md: -------------------------------------------------------------------------------- 1 | Called when parse analyze goes, right after performing transformTopLevelStmt(). 2 | 3 | Used in several internal methods: 4 | [pg_analyze_and_rewrite_params()](https://github.com/postgres/postgres/blob/src/backend/tcop/postgres.c#L686), 5 | [parse_analyze()](https://github.com/postgres/postgres/blob/src/backend/parser/analyze.c#L100). 6 | 7 | *Inputs:* 8 | 9 | * ParseState * pstate — parse state filled by query_string and queryEnv. 10 | * Query * query — output result of the transformTopLevelStmt(). -------------------------------------------------------------------------------- /templates/hooks/row_security_policy_hook_permissive.md: -------------------------------------------------------------------------------- 1 | Hook to add policies which are combined with the other permissive policies. 2 | 3 | This hook, along with the `row_security_policy_hook_restrictive`, allows adding 4 | custom security policies. It is called to build a list of policies for the given 5 | command applied to the given relation. 6 | 7 | Access is granted to an object if and only if no restrictive policies deny 8 | access and any permissive policy grant access. 9 | 10 | *Inputs:* 11 | 12 | * CmdType cmdtype — command type. 13 | * Relation relation — relation id. 14 | 15 | *Output:* 16 | 17 | List of additional permissive policies that will be added to the list of 18 | default permissive policies. 19 | -------------------------------------------------------------------------------- /templates/hooks/row_security_policy_hook_restrictive.md: -------------------------------------------------------------------------------- 1 | Hook to add policies which are enforced, regardless of other policies. 2 | 3 | See `row_security_policy_hook_permissive` for a detailed description. 4 | 5 | Unlike for permissive policies, postgres guarantees that restrictive policies 6 | will be executed in a predefined order. That is, first postgres executes the 7 | default policies sorted by their name, than postgres executes custom policies, 8 | also sorted by their name. 9 | 10 | *Inputs:* 11 | 12 | * CmdType cmdtype — command type. 13 | * Relation relation — relation id. 14 | -------------------------------------------------------------------------------- /templates/hooks/set_join_pathlist_hook.md: -------------------------------------------------------------------------------- 1 | Called at the end of the process of joinrel modification to contain the best paths. 2 | 3 | The hook can manipulate path list to perform a postprocess for best paths. 4 | 5 | *Inputs:* 6 | 7 | * PlannerInfo * root — query plan root. 8 | * RelOptInfo * joinrel — list of paths. 9 | * RelOptInfo * outerrel - list of outer relation paths. 10 | * RelOptInfo * innerrel - list of inner relation paths. 11 | * JoinType jointype - the type of a join. 12 | * JoinPathExtraData * extra 13 | -------------------------------------------------------------------------------- /templates/hooks/set_rel_pathlist_hook.md: -------------------------------------------------------------------------------- 1 | Called at the end of building access paths for a base relation. 2 | 3 | The hook can apply changes to set of paths by adding new paths or deleting them. 4 | 5 | *Inputs:* 6 | 7 | * PlannerInfo * root 8 | * RelOptInfo * rel - relation info. 9 | * Index rti - range table index. 10 | * RangeTblEntry * rte range table entry. -------------------------------------------------------------------------------- /templates/hooks/shmem_startup_hook.md: -------------------------------------------------------------------------------- 1 | Hook for extensions to initialize their shared memory. 2 | 3 | This hook is called by postmaster or by a standalone backend 4 | right after postgres initializes its shared memory and semaphores 5 | so that extensions have chance to initialize their shared state. 6 | 7 | It also may be called by a backend forked from the postmaster. 8 | In this situation, the shared memory segment already exists, so you only have 9 | to initialize the local memory state (check `!IsUnderPostmaster` 10 | to determine if that's the case). 11 | 12 | Note that you can bind a callback for shared state teardown 13 | via `on_shmem_exit`. 14 | 15 | Check out the `pg_stat_statements` code to get the idea on how to implement 16 | this hook correctly. 17 | -------------------------------------------------------------------------------- /templates/hooks/stmt_beg.md: -------------------------------------------------------------------------------- 1 | Called before each statement of a function. 2 | 3 | *Inputs:* 4 | 5 | * PLpgSQL_execstate * estate — runtime execution data. 6 | * PLpgSQL_stmt * stmt — execution node. -------------------------------------------------------------------------------- /templates/hooks/stmt_end.md: -------------------------------------------------------------------------------- 1 | Called after each statement of a function. 2 | 3 | *Inputs:* 4 | 5 | * PLpgSQL_execstate * estate — runtime execution data. 6 | * PLpgSQL_stmt * stmt — execution node. --------------------------------------------------------------------------------