Basically as the title says, I'm trying to figure out if the order of elements in the array in pg_prepared_statements.result_types
is guaranteed to match the column order of the results when you do EXECUTE <prepared statement name>;
Yes, it is. Postgres keeps the result structure fixed and constant. There's an adequate comment in src/backend/commands /prepare.c
responsible for this:
/*
* Given a prepared statement, determine the result tupledesc it will
* produce. Returns NULL if the execution will not return tuples.
*
* Note: the result is created or copied into current memory context.
*/
TupleDesc
FetchPreparedStatementResultDesc(PreparedStatement *stmt)
{
/*
* Since we don't allow prepared statements' result tupdescs to change,
* there's no need to worry about revalidating the cached plan here.
*/
/* Shouldn't find a non-fixed-result cached plan */
if (!entry->plansource->fixed_result)
elog(ERROR, "EXECUTE does not support variable-result cached plans");
The pg_prepared_statements
view is based on pg_prepared_statement()
in that file that pulls out the result types directly from the prepared statements.
Unlike routines, prepared statements are not subject to overloading and their names have to be unique within a session:
name
An arbitrary name given to this particular prepared statement. It must be unique within a single session and is subsequently used to execute or deallocate a previously prepared statement.
If you're debugging some strange behaviour that feels like your output structure keeps changing, make sure you're really running the prepared statement you're expecting to run.
One way statements can seemingly share a name is through mixed-case identifiers (they are case-sensitive only when double-quoted):
demo at db<>fiddle
prepare "a_statement"(float) as
select 'I accept a float and return (text,boolean)' as txt
, true as b;
prepare "A_statement"(int) as
select 'I accept an int and return (text,bigint,json)' as txt
, 1e11::bigint
,'{"a":0}'::json;
execute A_STATEMENT(1);
execute a_StaTEmEnT(1);
execute a_statement(1);
txt | b |
---|---|
I accept a float and return (text,boolean) | t |
txt | b |
---|---|
I accept a float and return (text,boolean) | t |
txt | b |
---|---|
I accept a float and return (text,boolean) | t |
execute "A_statement"(1);
txt | int8 | json |
---|---|---|
I accept an integer and return (text,bigint,json) | 100000000000 | {"a":0} |
If you're trying to use the statement in PL/pgSQL, you might want to also make sure you're running it with
execute 'execute a_statement($1,$2)' using p1,p2;
Because in that context execute
is for dynamic statements, and only once you're in it, you can use the plain SQL execute
that's for prepared statements. The doc has a note on that:
Note
The PL/pgSQLEXECUTE
statement is not related to theEXECUTE
SQL statement supported by the PostgreSQL server. The server'sEXECUTE
statement cannot be used directly within PL/pgSQL functions (and is not needed).
It is technically possible to accidentally call an overloaded routine that yields a text
that happens to be a valid SQL statement that PL/pgSQL execute
then executes, yielding something else again. Extremely unlikely and bizarre, but doable:
demo at db<>fiddle
prepare"a_statement"(float)as select 'prep statement(float)->(text,boolean)' txt,true b;
create function a_statement(int)returns text return $$select 1,true,'abc';$$;
do $d$
declare rec record;
begin
execute a_statement(1) into rec;
create table t1 as select rec::text;
execute 'execute a_statement(1)' into rec;
create table t2 as select rec::text;
create table t3 as execute a_statement(1);
end $d$;
table t1;
(1,t,abc) |
table t2;
("prep statement(float)->(text,boolean)",t) |
table t3;
txt | b |
---|---|
prep statement(float)->(text,boolean) | t |