diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index cfd8b8dc0b..624b5df96f 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ @@ -4372,6 +4372,11 @@ currently held locks + + pg_prepared_statements + current prepared statements + + pg_prepared_xacts currently prepared transactions @@ -4778,6 +4783,101 @@ + + <structname>pg_prepared_statements</structname> + + + pg_prepared_statements + + + + The pg_prepared_statements view displays + all the prepared statements that are available in the current + session. See for more information about prepared + statements. + + + + pg_prepared_statements contains one row + for each prepared statement. Rows are added to the view when a new + prepared statement is created, and removed when a prepared + statement is released (for example, via the + command). + + + + <structname>pg_prepared_statements</> Columns + + + + + Name + Type + References + Description + + + + + name + text + + + The identifier of the prepared statement. + + + + statement + text + + + The query string submitted by the client to create this + prepared statement. For prepared statements created via SQL, + this is the PREPARE statement submitted by + the client. For prepared statements created via the + frontend/backend protocol, this is the text of the prepared + statement itself. + + + + prepare_time + timestamptz + + + The time at which the prepared statement was created. + + + + parameter_types + oid[] + + + The expected parameter types for the prepared statement in the form of + an array of type OIDs. + + + + from_sql + boolean + + + true if the prepared statement was created + via the PREPARE SQL statement; + false if the statement was prepared via the + frontend/backend protocol. + + + + +
+ + + The pg_prepared_statements view is read only. + +
+ <structname>pg_prepared_xacts</structname> diff --git a/doc/src/sgml/ref/prepare.sgml b/doc/src/sgml/ref/prepare.sgml index d0abc49b5c..51c3985117 100644 --- a/doc/src/sgml/ref/prepare.sgml +++ b/doc/src/sgml/ref/prepare.sgml @@ -1,5 +1,5 @@ @@ -145,6 +145,11 @@ PREPARE plan_name [ ( documentation. + + + You can see all available prepared statements of a session by querying the + pg_prepared_statements system view. + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 4ffb8ac2c5..a186c33b44 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -3,7 +3,7 @@ * * Copyright (c) 1996-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.22 2005/10/06 02:29:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.23 2006/01/08 07:00:25 neilc Exp $ */ CREATE VIEW pg_roles AS @@ -156,6 +156,12 @@ CREATE VIEW pg_prepared_xacts AS LEFT JOIN pg_authid U ON P.ownerid = U.oid LEFT JOIN pg_database D ON P.dbid = D.oid; +CREATE VIEW pg_prepared_statements AS + SELECT P.name, P.statement, P.prepare_time, P.parameter_types, P.from_sql + FROM pg_prepared_statement() AS P + (name text, statement text, prepare_time timestamptz, + parameter_types oid[], from_sql boolean); + CREATE VIEW pg_settings AS SELECT * FROM pg_show_all_settings() AS A diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 1ee955ebe1..ccf8c297b2 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -10,21 +10,26 @@ * Copyright (c) 2002-2005, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.44 2005/12/14 17:06:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.45 2006/01/08 07:00:25 neilc Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/heapam.h" +#include "catalog/pg_type.h" #include "commands/explain.h" #include "commands/prepare.h" #include "executor/executor.h" -#include "utils/guc.h" +#include "funcapi.h" +#include "parser/parsetree.h" #include "optimizer/planner.h" #include "rewrite/rewriteHandler.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" +#include "utils/builtins.h" +#include "utils/guc.h" #include "utils/hsearch.h" #include "utils/memutils.h" @@ -40,6 +45,7 @@ static HTAB *prepared_queries = NULL; static void InitQueryHashTable(void); static ParamListInfo EvaluateParams(EState *estate, List *params, List *argtypes); +static Datum build_oid_array(List *oid_list); /* * Implements the 'PREPARE' utility statement. @@ -114,7 +120,8 @@ PrepareQuery(PrepareStmt *stmt) commandTag, query_list, plan_list, - stmt->argtype_oids); + stmt->argtype_oids, + true); } /* @@ -298,7 +305,8 @@ StorePreparedStatement(const char *stmt_name, const char *commandTag, List *query_list, List *plan_list, - List *argtype_list) + List *argtype_list, + bool from_sql) { PreparedStatement *entry; MemoryContext oldcxt, @@ -361,6 +369,8 @@ StorePreparedStatement(const char *stmt_name, entry->plan_list = plan_list; entry->argtype_list = argtype_list; entry->context = entrycxt; + entry->prepare_time = GetCurrentTimestamp(); + entry->from_sql = from_sql; MemoryContextSwitchTo(oldcxt); } @@ -383,7 +393,7 @@ FetchPreparedStatement(const char *stmt_name, bool throwError) { /* * We can't just use the statement name as supplied by the user: the - * hash package is picky enough that it needs to be NULL-padded out to + * hash package is picky enough that it needs to be NUL-padded out to * the appropriate length to work correctly. */ StrNCpy(key, stmt_name, sizeof(key)); @@ -661,3 +671,125 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params, if (estate) FreeExecutorState(estate); } + +/* + * This set returning function reads all the prepared statements and + * returns a set of (name, statement, prepare_time, param_types). + */ +Datum +pg_prepared_statement(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + HASH_SEQ_STATUS *hash_seq; + PreparedStatement *prep_stmt; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + TupleDesc tupdesc; + MemoryContext oldcontext; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * switch to memory context appropriate for multiple function + * calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* allocate memory for user context */ + if (prepared_queries) + { + hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS)); + hash_seq_init(hash_seq, prepared_queries); + funcctx->user_fctx = (void *) hash_seq; + } + else + funcctx->user_fctx = NULL; + + /* + * build tupdesc for result tuples. This must match the + * definition of the pg_prepared_statements view in + * system_views.sql + */ + tupdesc = CreateTemplateTupleDesc(5, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time", + TIMESTAMPTZOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types", + OIDARRAYOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql", + BOOLOID, -1, 0); + + funcctx->tuple_desc = BlessTupleDesc(tupdesc); + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx; + + /* if the hash table is uninitialized, we're done */ + if (hash_seq == NULL) + SRF_RETURN_DONE(funcctx); + + prep_stmt = hash_seq_search(hash_seq); + if (prep_stmt) + { + Datum result; + HeapTuple tuple; + Datum values[5]; + bool nulls[5]; + + MemSet(nulls, 0, sizeof(nulls)); + + values[0] = DirectFunctionCall1(textin, + CStringGetDatum(prep_stmt->stmt_name)); + + if (prep_stmt->query_string == NULL) + nulls[1] = true; + else + values[1] = DirectFunctionCall1(textin, + CStringGetDatum(prep_stmt->query_string)); + + values[2] = TimestampTzGetDatum(prep_stmt->prepare_time); + values[3] = build_oid_array(prep_stmt->argtype_list); + values[4] = BoolGetDatum(prep_stmt->from_sql); + + tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(tuple); + SRF_RETURN_NEXT(funcctx, result); + } + + SRF_RETURN_DONE(funcctx); +} + +/* + * This utility function takes a List of Oids, and returns a Datum + * pointing to a Postgres array containing those OIDs. The empty list + * is returned as a zero-element array, not NULL. + */ +static Datum +build_oid_array(List *oid_list) +{ + ListCell *lc; + int len; + int i; + Datum *tmp_ary; + ArrayType *ary; + + len = list_length(oid_list); + tmp_ary = (Datum *) palloc(len * sizeof(Datum)); + + i = 0; + foreach(lc, oid_list) + tmp_ary[i++] = ObjectIdGetDatum(lfirst_oid(lc)); + + /* XXX: this hardcodes assumptions about the OID type... */ + ary = construct_array(tmp_ary, len, OIDOID, sizeof(Oid), true, 'i'); + return PointerGetDatum(ary); +} diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index e38e66a38c..0fe8ee057d 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.477 2006/01/05 10:07:45 petere Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.478 2006/01/08 07:00:25 neilc Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -55,6 +55,7 @@ #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" +#include "utils/builtins.h" #include "utils/flatfiles.h" #include "utils/guc.h" #include "utils/lsyscache.h" @@ -1308,7 +1309,8 @@ exec_parse_message(const char *query_string, /* string to execute */ commandTag, querytree_list, plantree_list, - param_list); + param_list, + false); } else { @@ -1322,6 +1324,7 @@ exec_parse_message(const char *query_string, /* string to execute */ pstmt->query_list = querytree_list; pstmt->plan_list = plantree_list; pstmt->argtype_list = param_list; + pstmt->from_sql = false; pstmt->context = unnamed_stmt_context; /* Now the unnamed statement is complete and valid */ unnamed_stmt_pstmt = pstmt; diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index a7bad3dfd9..6eb5f72d9f 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.308 2005/12/28 01:30:01 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.309 2006/01/08 07:00:25 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200512271 +#define CATALOG_VERSION_NO 200601081 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 824f7a3fba..7b78e78fcc 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.389 2005/11/17 22:14:54 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.390 2006/01/08 07:00:25 neilc Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -3617,6 +3617,8 @@ DATA(insert OID = 2508 ( pg_get_constraintdef PGNSP PGUID 12 f f t f s 2 25 "26 DESCR("constraint description with pretty-print option"); DATA(insert OID = 2509 ( pg_get_expr PGNSP PGUID 12 f f t f s 3 25 "25 26 16" _null_ _null_ _null_ pg_get_expr_ext - _null_ )); DESCR("deparse an encoded expression with pretty-print option"); +DATA(insert OID = 2510 ( pg_prepared_statement PGNSP PGUID 12 f f t t s 0 2249 "" _null_ _null_ _null_ pg_prepared_statement - _null_ )); +DESCR("get the prepared statements for this session"); /* non-persistent series generator */ DATA(insert OID = 1066 ( generate_series PGNSP PGUID 12 f f t t v 3 23 "23 23 23" _null_ _null_ _null_ generate_series_step_int4 - _null_ )); diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 4b7b7c2652..c6ca59c87e 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.167 2005/11/22 18:17:30 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.168 2006/01/08 07:00:26 neilc Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -406,6 +406,7 @@ DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b t \054 0 23 array_in array_ DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b t \054 0 24 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b t \054 0 25 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b t \054 0 26 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ )); +#define OIDARRAYOID 1028 DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b t \054 0 27 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b t \054 0 28 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b t \054 0 29 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ )); diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h index 50767740cc..503f27e4bc 100644 --- a/src/include/commands/prepare.h +++ b/src/include/commands/prepare.h @@ -6,7 +6,7 @@ * * Copyright (c) 2002-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.16 2005/12/14 17:06:28 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.17 2006/01/08 07:00:26 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -30,13 +30,16 @@ typedef struct { /* dynahash.c requires key to be first field */ - char stmt_name[NAMEDATALEN]; - char *query_string; /* text of query, or NULL */ - const char *commandTag; /* command tag (a constant!), or NULL */ - List *query_list; /* list of queries */ - List *plan_list; /* list of plans */ - List *argtype_list; /* list of parameter type OIDs */ - MemoryContext context; /* context containing this query */ + char stmt_name[NAMEDATALEN]; + char *query_string; /* text of query, or NULL */ + const char *commandTag; /* command tag (a constant!), or NULL */ + List *query_list; /* list of queries, rewritten */ + List *plan_list; /* list of plans */ + List *argtype_list; /* list of parameter type OIDs */ + TimestampTz prepare_time; /* the time when the stmt was prepared */ + bool from_sql; /* stmt prepared via SQL, not + * FE/BE protocol? */ + MemoryContext context; /* context containing this query */ } PreparedStatement; @@ -54,7 +57,8 @@ extern void StorePreparedStatement(const char *stmt_name, const char *commandTag, List *query_list, List *plan_list, - List *argtype_list); + List *argtype_list, + bool from_sql); extern PreparedStatement *FetchPreparedStatement(const char *stmt_name, bool throwError); extern void DropPreparedStatement(const char *stmt_name, bool showError); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 4d1349917c..97fbfdc341 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.268 2005/11/22 18:17:32 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.269 2006/01/08 07:00:26 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -861,4 +861,7 @@ extern Datum pg_prepared_xact(PG_FUNCTION_ARGS); /* catalog/pg_conversion.c */ extern Datum pg_convert_using(PG_FUNCTION_ARGS); +/* commands/prepare.c */ +extern Datum pg_prepared_statement(PG_FUNCTION_ARGS); + #endif /* BUILTINS_H */ diff --git a/src/test/regress/expected/prepare.out b/src/test/regress/expected/prepare.out index 43fd8ecf56..54616199b6 100644 --- a/src/test/regress/expected/prepare.out +++ b/src/test/regress/expected/prepare.out @@ -1,9 +1,22 @@ --- Regression tests for prepareable statements -PREPARE q1 AS SELECT 1; +-- Regression tests for prepareable statements. We query the content +-- of the pg_prepared_statements view as prepared statements are +-- created and removed. +SELECT name, statement, parameter_types FROM pg_prepared_statements; + name | statement | parameter_types +------+-----------+----------------- +(0 rows) + +PREPARE q1 AS SELECT 1 AS a; EXECUTE q1; - ?column? ----------- - 1 + a +--- + 1 +(1 row) + +SELECT name, statement, parameter_types FROM pg_prepared_statements; + name | statement | parameter_types +------+------------------------------+----------------- + q1 | PREPARE q1 AS SELECT 1 AS a; | {} (1 row) -- should fail @@ -18,12 +31,41 @@ EXECUTE q1; 2 (1 row) +PREPARE q2 AS SELECT 2 AS b; +SELECT name, statement, parameter_types FROM pg_prepared_statements; + name | statement | parameter_types +------+------------------------------+----------------- + q1 | PREPARE q1 AS SELECT 2; | {} + q2 | PREPARE q2 AS SELECT 2 AS b; | {} +(2 rows) + -- sql92 syntax DEALLOCATE PREPARE q1; +SELECT name, statement, parameter_types FROM pg_prepared_statements; + name | statement | parameter_types +------+------------------------------+----------------- + q2 | PREPARE q2 AS SELECT 2 AS b; | {} +(1 row) + +DEALLOCATE PREPARE q2; +-- the view should return the empty set again +SELECT name, statement, parameter_types FROM pg_prepared_statements; + name | statement | parameter_types +------+-----------+----------------- +(0 rows) + -- parameterized queries PREPARE q2(text) AS SELECT datname, datistemplate, datallowconn FROM pg_database WHERE datname = $1; +SELECT name, statement, parameter_types FROM pg_prepared_statements; + name | statement | parameter_types +------+--------------------------------------------------------------------------------------------------------+----------------- + q2 | PREPARE q2(text) AS + SELECT datname, datistemplate, datallowconn + FROM pg_database WHERE datname = $1; | {25} +(1 row) + EXECUTE q2('regression'); datname | datistemplate | datallowconn ------------+---------------+-------------- @@ -33,6 +75,17 @@ EXECUTE q2('regression'); PREPARE q3(text, int, float, boolean, oid, smallint) AS SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int); +SELECT name, statement, parameter_types FROM pg_prepared_statements; + name | statement | parameter_types +------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------- + q2 | PREPARE q2(text) AS + SELECT datname, datistemplate, datallowconn + FROM pg_database WHERE datname = $1; | {25} + q3 | PREPARE q3(text, int, float, boolean, oid, smallint) AS + SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR + ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int); | {25,23,701,16,26,21} +(2 rows) + EXECUTE q3('AAAAxx', 5::smallint, 10.5::float, false, 500::oid, 4::bigint); unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 ---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+--------- diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 2c8070d119..67616b7333 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1280,6 +1280,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS "tablespace", pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); pg_locks | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l.transactionid, l.classid, l.objid, l.objsubid, l."transaction", l.pid, l."mode", l."granted" FROM pg_lock_status() l(locktype text, "database" oid, relation oid, page integer, tuple smallint, transactionid xid, classid oid, objid oid, objsubid smallint, "transaction" xid, pid integer, "mode" text, "granted" boolean); + pg_prepared_statements | SELECT p.name, p."statement", p.prepare_time, p.parameter_types, p.from_sql FROM pg_prepared_statement() p(name text, "statement" text, prepare_time timestamp with time zone, parameter_types oid[], from_sql boolean); pg_prepared_xacts | SELECT p."transaction", p.gid, p."prepared", u.rolname AS "owner", d.datname AS "database" FROM ((pg_prepared_xact() p("transaction" xid, gid text, "prepared" timestamp with time zone, ownerid oid, dbid oid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid))); pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, pg_authid.rolconfig, pg_authid.oid FROM pg_authid; pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name); @@ -1320,7 +1321,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); toyemp | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp; -(44 rows) +(45 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; diff --git a/src/test/regress/sql/prepare.sql b/src/test/regress/sql/prepare.sql index fc6924307d..95db2a0910 100644 --- a/src/test/regress/sql/prepare.sql +++ b/src/test/regress/sql/prepare.sql @@ -1,8 +1,14 @@ --- Regression tests for prepareable statements +-- Regression tests for prepareable statements. We query the content +-- of the pg_prepared_statements view as prepared statements are +-- created and removed. -PREPARE q1 AS SELECT 1; +SELECT name, statement, parameter_types FROM pg_prepared_statements; + +PREPARE q1 AS SELECT 1 AS a; EXECUTE q1; +SELECT name, statement, parameter_types FROM pg_prepared_statements; + -- should fail PREPARE q1 AS SELECT 2; @@ -11,19 +17,33 @@ DEALLOCATE q1; PREPARE q1 AS SELECT 2; EXECUTE q1; +PREPARE q2 AS SELECT 2 AS b; +SELECT name, statement, parameter_types FROM pg_prepared_statements; + -- sql92 syntax DEALLOCATE PREPARE q1; +SELECT name, statement, parameter_types FROM pg_prepared_statements; + +DEALLOCATE PREPARE q2; +-- the view should return the empty set again +SELECT name, statement, parameter_types FROM pg_prepared_statements; + -- parameterized queries PREPARE q2(text) AS SELECT datname, datistemplate, datallowconn FROM pg_database WHERE datname = $1; + +SELECT name, statement, parameter_types FROM pg_prepared_statements; + EXECUTE q2('regression'); PREPARE q3(text, int, float, boolean, oid, smallint) AS SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int); +SELECT name, statement, parameter_types FROM pg_prepared_statements; + EXECUTE q3('AAAAxx', 5::smallint, 10.5::float, false, 500::oid, 4::bigint); -- too few params