When a PL/pgSQL function declares a RECORD variable and accesses its fields, the compiler constructs a structured internal representation to manage dynamic field referneces at runtime. Consider this function:
CREATE OR REPLACE FUNCTION demo_fors()
RETURNS VOID AS
$$
DECLARE
a RECORD;
BEGIN
FOR a IN SELECT * FROM some_table LOOP
RAISE NOTICE 'id: %, name: %', a.id, a.name;
END LOOP;
END;
$$
LANGUAGE plpgsql;
The declaration a RECORD; triggers the creation of a PLpgSQL_rec structure during parsing. The parser invokes plpgsql_build_variable, which detects the RECORD type and delegates to plpgsql_build_record. This function allocates memory for the record variable, assigns its name (a), line number, and associated type OID, then registers it into the global plpgsql_Datums array via plpgsql_adddatum. The variable’s index (dno) is assinged sequentially, starting from 0.
Simultaneously, the variable is registered in the namespace chain via plpgsql_ns_additem, linking the identifier a to its datum index. This enables later lookups during expression parsing.
When encountering field access like a.id, the parser invokes plpgsql_parse_dblword, which splits the double-word token into word1 = "a" and word2 = "id". It then searches the namespace for a using plpgsql_ns_lookup. Upon finding the matching PLpgSQL_rec entry, it constructs a PLpgSQL_recfield structure to represent the field reference.
The plpgsql_build_recfield function creates a new datum of type PLPGSQL_DTYPE_RECFIELD, storing the field name (id) and a reference to its parent record via recparentno (the dno of a). This new field datum is added to plpgsql_Datums, and its index is linked into the parent record’s firstfield chain, forming a singly-linked list of all referenced fields for that record.
Each field access—whether a.id or a.name—results in a separate PLpgSQL_recfield node. These nodes are not resolved to actual column types during compilation; instead, they are deferred until execution time, when the record’s runtime tuple descriptor becomes available. The parser wraps each field reference in a PLpgSQL_expr structure, storing the raw text (a.id) and the current namespace context for later evaluation.
Throughout this process, all datums—variables, records, and record fields—are stored in the global plpgsql_Datums array, indexed by their dno. This array is only valid during the compilation phase and is discarded after do_compile completes. The namespace chain (ns_top) provides symbol resolution during parsing, ensuring identifiers like a correctly map to their corresponding record structures.