From 44b928e876b06ba6801ec2c60d2cd914a2185c5d Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Sun, 8 Jan 2006 07:00:27 +0000 Subject: [PATCH] Add a new system view, pg_prepared_statements, that can be used to access information about the prepared statements that are available in the current session. Original patch from Joachim Wieland, various improvements by Neil Conway. The "statement" column of the view contains the literal query string sent by the client, without any rewriting or pretty printing. This means that prepared statements created via SQL will be prefixed with "PREPARE ... AS ", whereas those prepared via the FE/BE protocol will not. That is unfortunate, but discussion on -patches did not yield an efficient way to improve this, and there is some merit in returning exactly what the client sent to the backend. Catalog version bumped, regression tests updated. --- doc/src/sgml/catalogs.sgml | 102 +++++++++++++++++- doc/src/sgml/ref/prepare.sgml | 7 +- src/backend/catalog/system_views.sql | 8 +- src/backend/commands/prepare.c | 142 +++++++++++++++++++++++++- src/backend/tcop/postgres.c | 7 +- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_proc.h | 4 +- src/include/catalog/pg_type.h | 3 +- src/include/commands/prepare.h | 22 ++-- src/include/utils/builtins.h | 5 +- src/test/regress/expected/prepare.out | 63 +++++++++++- src/test/regress/expected/rules.out | 3 +- src/test/regress/sql/prepare.sql | 24 ++++- 13 files changed, 362 insertions(+), 32 deletions(-) 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