First phase of plan-invalidation project: create a plan cache management
module and teach PREPARE and protocol-level prepared statements to use it. In service of this, rearrange utility-statement processing so that parse analysis does not assume table schemas can't change before execution for utility statements (necessary because we don't attempt to re-acquire locks for utility statements when reusing a stored plan). This requires some refactoring of the ProcessUtility API, but it ends up cleaner anyway, for instance we can get rid of the QueryContext global. Still to do: fix up SPI and related code to use the plan cache; I'm tempted to try to make SQL functions use it too. Also, there are at least some aspects of system state that we want to ensure remain the same during a replan as in the original processing; search_path certainly ought to behave that way for instance, and perhaps there are others.
This commit is contained in:
parent
f84308f195
commit
b9527e9840
|
@ -10,7 +10,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.235 2007/03/12 22:09:27 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.236 2007/03/13 00:33:38 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -2504,12 +2504,13 @@ AbortCurrentTransaction(void)
|
||||||
* could issue more commands and possibly cause a failure after the statement
|
* could issue more commands and possibly cause a failure after the statement
|
||||||
* completes). Subtransactions are verboten too.
|
* completes). Subtransactions are verboten too.
|
||||||
*
|
*
|
||||||
* stmtNode: pointer to parameter block for statement; this is used in
|
* isTopLevel: passed down from ProcessUtility to determine whether we are
|
||||||
* a very klugy way to determine whether we are inside a function.
|
* inside a function. (We will always fail if this is false, but it's
|
||||||
* stmtType: statement type name for error messages.
|
* convenient to centralize the check here instead of making callers do it.)
|
||||||
|
* stmtType: statement type name, for error messages.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
PreventTransactionChain(void *stmtNode, const char *stmtType)
|
PreventTransactionChain(bool isTopLevel, const char *stmtType)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* xact block already started?
|
* xact block already started?
|
||||||
|
@ -2532,11 +2533,9 @@ PreventTransactionChain(void *stmtNode, const char *stmtType)
|
||||||
stmtType)));
|
stmtType)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Are we inside a function call? If the statement's parameter block was
|
* inside a function call?
|
||||||
* allocated in QueryContext, assume it is an interactive command.
|
|
||||||
* Otherwise assume it is coming from a function.
|
|
||||||
*/
|
*/
|
||||||
if (!MemoryContextContains(QueryContext, stmtNode))
|
if (!isTopLevel)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
|
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
|
||||||
/* translator: %s represents an SQL statement name */
|
/* translator: %s represents an SQL statement name */
|
||||||
|
@ -2562,12 +2561,12 @@ PreventTransactionChain(void *stmtNode, const char *stmtType)
|
||||||
* use of the current statement's results. Likewise subtransactions.
|
* use of the current statement's results. Likewise subtransactions.
|
||||||
* Thus this is an inverse for PreventTransactionChain.
|
* Thus this is an inverse for PreventTransactionChain.
|
||||||
*
|
*
|
||||||
* stmtNode: pointer to parameter block for statement; this is used in
|
* isTopLevel: passed down from ProcessUtility to determine whether we are
|
||||||
* a very klugy way to determine whether we are inside a function.
|
* inside a function.
|
||||||
* stmtType: statement type name for error messages.
|
* stmtType: statement type name, for error messages.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
RequireTransactionChain(void *stmtNode, const char *stmtType)
|
RequireTransactionChain(bool isTopLevel, const char *stmtType)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* xact block already started?
|
* xact block already started?
|
||||||
|
@ -2582,12 +2581,11 @@ RequireTransactionChain(void *stmtNode, const char *stmtType)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Are we inside a function call? If the statement's parameter block was
|
* inside a function call?
|
||||||
* allocated in QueryContext, assume it is an interactive command.
|
|
||||||
* Otherwise assume it is coming from a function.
|
|
||||||
*/
|
*/
|
||||||
if (!MemoryContextContains(QueryContext, stmtNode))
|
if (!isTopLevel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
|
(errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
|
||||||
/* translator: %s represents an SQL statement name */
|
/* translator: %s represents an SQL statement name */
|
||||||
|
@ -2602,11 +2600,11 @@ RequireTransactionChain(void *stmtNode, const char *stmtType)
|
||||||
* a transaction block than when running as single commands. ANALYZE is
|
* a transaction block than when running as single commands. ANALYZE is
|
||||||
* currently the only example.
|
* currently the only example.
|
||||||
*
|
*
|
||||||
* stmtNode: pointer to parameter block for statement; this is used in
|
* isTopLevel: passed down from ProcessUtility to determine whether we are
|
||||||
* a very klugy way to determine whether we are inside a function.
|
* inside a function.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
IsInTransactionChain(void *stmtNode)
|
IsInTransactionChain(bool isTopLevel)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Return true on same conditions that would make PreventTransactionChain
|
* Return true on same conditions that would make PreventTransactionChain
|
||||||
|
@ -2618,7 +2616,7 @@ IsInTransactionChain(void *stmtNode)
|
||||||
if (IsSubTransaction())
|
if (IsSubTransaction())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!MemoryContextContains(QueryContext, stmtNode))
|
if (!isTopLevel)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (CurrentTransactionState->blockState != TBLOCK_DEFAULT &&
|
if (CurrentTransactionState->blockState != TBLOCK_DEFAULT &&
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.87 2007/03/07 13:35:02 alvherre Exp $
|
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.88 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -252,7 +252,7 @@ Boot_DeclareIndexStmt:
|
||||||
LexIDStr($8),
|
LexIDStr($8),
|
||||||
NULL,
|
NULL,
|
||||||
$10,
|
$10,
|
||||||
NULL, NIL, NIL,
|
NULL, NIL,
|
||||||
false, false, false,
|
false, false, false,
|
||||||
false, false, true, false, false);
|
false, false, true, false, false);
|
||||||
do_end();
|
do_end();
|
||||||
|
@ -270,7 +270,7 @@ Boot_DeclareUniqueIndexStmt:
|
||||||
LexIDStr($9),
|
LexIDStr($9),
|
||||||
NULL,
|
NULL,
|
||||||
$11,
|
$11,
|
||||||
NULL, NIL, NIL,
|
NULL, NIL,
|
||||||
true, false, false,
|
true, false, false,
|
||||||
false, false, true, false, false);
|
false, false, true, false, false);
|
||||||
do_end();
|
do_end();
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.156 2007/02/01 19:10:25 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.157 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -82,7 +82,7 @@ static List *get_tables_to_cluster(MemoryContext cluster_context);
|
||||||
*---------------------------------------------------------------------------
|
*---------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
cluster(ClusterStmt *stmt)
|
cluster(ClusterStmt *stmt, bool isTopLevel)
|
||||||
{
|
{
|
||||||
if (stmt->relation != NULL)
|
if (stmt->relation != NULL)
|
||||||
{
|
{
|
||||||
|
@ -173,7 +173,7 @@ cluster(ClusterStmt *stmt)
|
||||||
* We cannot run this form of CLUSTER inside a user transaction block;
|
* We cannot run this form of CLUSTER inside a user transaction block;
|
||||||
* we'd be holding locks way too long.
|
* we'd be holding locks way too long.
|
||||||
*/
|
*/
|
||||||
PreventTransactionChain((void *) stmt, "CLUSTER");
|
PreventTransactionChain(isTopLevel, "CLUSTER");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create special memory context for cross-transaction storage.
|
* Create special memory context for cross-transaction storage.
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.277 2007/03/03 19:32:54 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.278 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -713,7 +713,7 @@ CopyLoadRawBuf(CopyState cstate)
|
||||||
* the table.
|
* the table.
|
||||||
*/
|
*/
|
||||||
uint64
|
uint64
|
||||||
DoCopy(const CopyStmt *stmt)
|
DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||||
{
|
{
|
||||||
CopyState cstate;
|
CopyState cstate;
|
||||||
bool is_from = stmt->is_from;
|
bool is_from = stmt->is_from;
|
||||||
|
@ -982,13 +982,11 @@ DoCopy(const CopyStmt *stmt)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Query *query = stmt->query;
|
|
||||||
List *rewritten;
|
List *rewritten;
|
||||||
|
Query *query;
|
||||||
PlannedStmt *plan;
|
PlannedStmt *plan;
|
||||||
DestReceiver *dest;
|
DestReceiver *dest;
|
||||||
|
|
||||||
Assert(query);
|
|
||||||
Assert(query->commandType == CMD_SELECT);
|
|
||||||
Assert(!is_from);
|
Assert(!is_from);
|
||||||
cstate->rel = NULL;
|
cstate->rel = NULL;
|
||||||
|
|
||||||
|
@ -998,33 +996,18 @@ DoCopy(const CopyStmt *stmt)
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("COPY (SELECT) WITH OIDS is not supported")));
|
errmsg("COPY (SELECT) WITH OIDS is not supported")));
|
||||||
|
|
||||||
/* Query mustn't use INTO, either */
|
|
||||||
if (query->into)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("COPY (SELECT INTO) is not supported")));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The query has already been through parse analysis, but not
|
* Run parse analysis and rewrite. Note this also acquires sufficient
|
||||||
* rewriting or planning. Do that now.
|
* locks on the source table(s).
|
||||||
*
|
*
|
||||||
* Because the planner is not cool about not scribbling on its input,
|
* Because the parser and planner tend to scribble on their input, we
|
||||||
* we make a preliminary copy of the source querytree. This prevents
|
* make a preliminary copy of the source querytree. This prevents
|
||||||
* problems in the case that the COPY is in a portal or plpgsql
|
* problems in the case that the COPY is in a portal or plpgsql
|
||||||
* function and is executed repeatedly. (See also the same hack in
|
* function and is executed repeatedly. (See also the same hack in
|
||||||
* EXPLAIN, DECLARE CURSOR and PREPARE.) XXX the planner really
|
* DECLARE CURSOR and PREPARE.) XXX FIXME someday.
|
||||||
* shouldn't modify its input ... FIXME someday.
|
|
||||||
*/
|
*/
|
||||||
query = copyObject(query);
|
rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
|
||||||
|
queryString, NULL, 0);
|
||||||
/*
|
|
||||||
* Must acquire locks in case we didn't come fresh from the parser.
|
|
||||||
* XXX this also scribbles on query, another reason for copyObject
|
|
||||||
*/
|
|
||||||
AcquireRewriteLocks(query);
|
|
||||||
|
|
||||||
/* Rewrite through rule system */
|
|
||||||
rewritten = QueryRewrite(query);
|
|
||||||
|
|
||||||
/* We don't expect more or less than one result query */
|
/* We don't expect more or less than one result query */
|
||||||
if (list_length(rewritten) != 1)
|
if (list_length(rewritten) != 1)
|
||||||
|
@ -1033,6 +1016,12 @@ DoCopy(const CopyStmt *stmt)
|
||||||
query = (Query *) linitial(rewritten);
|
query = (Query *) linitial(rewritten);
|
||||||
Assert(query->commandType == CMD_SELECT);
|
Assert(query->commandType == CMD_SELECT);
|
||||||
|
|
||||||
|
/* Query mustn't use INTO, either */
|
||||||
|
if (query->into)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("COPY (SELECT INTO) is not supported")));
|
||||||
|
|
||||||
/* plan the query */
|
/* plan the query */
|
||||||
plan = planner(query, false, 0, NULL);
|
plan = planner(query, false, 0, NULL);
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.192 2007/02/09 16:12:18 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.193 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -97,9 +97,6 @@ createdb(const CreatedbStmt *stmt)
|
||||||
int encoding = -1;
|
int encoding = -1;
|
||||||
int dbconnlimit = -1;
|
int dbconnlimit = -1;
|
||||||
|
|
||||||
/* don't call this in a transaction block */
|
|
||||||
PreventTransactionChain((void *) stmt, "CREATE DATABASE");
|
|
||||||
|
|
||||||
/* Extract options from the statement node tree */
|
/* Extract options from the statement node tree */
|
||||||
foreach(option, stmt->options)
|
foreach(option, stmt->options)
|
||||||
{
|
{
|
||||||
|
@ -545,8 +542,6 @@ dropdb(const char *dbname, bool missing_ok)
|
||||||
Relation pgdbrel;
|
Relation pgdbrel;
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
|
||||||
PreventTransactionChain((void *) dbname, "DROP DATABASE");
|
|
||||||
|
|
||||||
AssertArg(dbname);
|
AssertArg(dbname);
|
||||||
|
|
||||||
if (strcmp(dbname, get_database_name(MyDatabaseId)) == 0)
|
if (strcmp(dbname, get_database_name(MyDatabaseId)) == 0)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.159 2007/02/23 21:59:44 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.160 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
#include "optimizer/var.h"
|
#include "optimizer/var.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "rewrite/rewriteHandler.h"
|
#include "rewrite/rewriteHandler.h"
|
||||||
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
@ -41,7 +42,8 @@ typedef struct ExplainState
|
||||||
List *rtable; /* range table */
|
List *rtable; /* range table */
|
||||||
} ExplainState;
|
} ExplainState;
|
||||||
|
|
||||||
static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
|
static void ExplainOneQuery(Query *query, bool isCursor, int cursorOptions,
|
||||||
|
ExplainStmt *stmt, const char *queryString,
|
||||||
ParamListInfo params, TupOutputState *tstate);
|
ParamListInfo params, TupOutputState *tstate);
|
||||||
static double elapsed_time(instr_time *starttime);
|
static double elapsed_time(instr_time *starttime);
|
||||||
static void explain_outNode(StringInfo str,
|
static void explain_outNode(StringInfo str,
|
||||||
|
@ -62,47 +64,34 @@ static void show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
|
||||||
* execute an EXPLAIN command
|
* execute an EXPLAIN command
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExplainQuery(ExplainStmt *stmt, ParamListInfo params, DestReceiver *dest)
|
ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
||||||
|
ParamListInfo params, DestReceiver *dest)
|
||||||
{
|
{
|
||||||
Query *query = stmt->query;
|
Oid *param_types;
|
||||||
|
int num_params;
|
||||||
TupOutputState *tstate;
|
TupOutputState *tstate;
|
||||||
List *rewritten;
|
List *rewritten;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
|
||||||
|
/* Convert parameter type data to the form parser wants */
|
||||||
|
getParamListTypes(params, ¶m_types, &num_params);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Because the planner is not cool about not scribbling on its input, we
|
* Run parse analysis and rewrite. Note this also acquires sufficient
|
||||||
|
* locks on the source table(s).
|
||||||
|
*
|
||||||
|
* Because the parser and planner tend to scribble on their input, we
|
||||||
* make a preliminary copy of the source querytree. This prevents
|
* make a preliminary copy of the source querytree. This prevents
|
||||||
* problems in the case that the EXPLAIN is in a portal or plpgsql
|
* problems in the case that the EXPLAIN is in a portal or plpgsql
|
||||||
* function and is executed repeatedly. (See also the same hack in
|
* function and is executed repeatedly. (See also the same hack in
|
||||||
* DECLARE CURSOR and PREPARE.) XXX the planner really shouldn't modify
|
* DECLARE CURSOR and PREPARE.) XXX FIXME someday.
|
||||||
* its input ... FIXME someday.
|
|
||||||
*/
|
*/
|
||||||
query = copyObject(query);
|
rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
|
||||||
|
queryString, param_types, num_params);
|
||||||
|
|
||||||
/* prepare for projection of tuples */
|
/* prepare for projection of tuples */
|
||||||
tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
|
tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
|
||||||
|
|
||||||
if (query->commandType == CMD_UTILITY)
|
|
||||||
{
|
|
||||||
/* Rewriter will not cope with utility statements */
|
|
||||||
if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt))
|
|
||||||
ExplainOneQuery(query, stmt, params, tstate);
|
|
||||||
else if (query->utilityStmt && IsA(query->utilityStmt, ExecuteStmt))
|
|
||||||
ExplainExecuteQuery(stmt, params, tstate);
|
|
||||||
else
|
|
||||||
do_text_output_oneline(tstate, "Utility statements have no plan structure");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Must acquire locks in case we didn't come fresh from the parser.
|
|
||||||
* XXX this also scribbles on query, another reason for copyObject
|
|
||||||
*/
|
|
||||||
AcquireRewriteLocks(query);
|
|
||||||
|
|
||||||
/* Rewrite through rule system */
|
|
||||||
rewritten = QueryRewrite(query);
|
|
||||||
|
|
||||||
if (rewritten == NIL)
|
if (rewritten == NIL)
|
||||||
{
|
{
|
||||||
/* In the case of an INSTEAD NOTHING, tell at least that */
|
/* In the case of an INSTEAD NOTHING, tell at least that */
|
||||||
|
@ -113,13 +102,13 @@ ExplainQuery(ExplainStmt *stmt, ParamListInfo params, DestReceiver *dest)
|
||||||
/* Explain every plan */
|
/* Explain every plan */
|
||||||
foreach(l, rewritten)
|
foreach(l, rewritten)
|
||||||
{
|
{
|
||||||
ExplainOneQuery(lfirst(l), stmt, params, tstate);
|
ExplainOneQuery((Query *) lfirst(l), false, 0,
|
||||||
|
stmt, queryString, params, tstate);
|
||||||
/* put a blank line between plans */
|
/* put a blank line between plans */
|
||||||
if (lnext(l) != NULL)
|
if (lnext(l) != NULL)
|
||||||
do_text_output_oneline(tstate, "");
|
do_text_output_oneline(tstate, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
end_tup_output(tstate);
|
end_tup_output(tstate);
|
||||||
}
|
}
|
||||||
|
@ -142,52 +131,23 @@ ExplainResultDesc(ExplainStmt *stmt)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ExplainOneQuery -
|
* ExplainOneQuery -
|
||||||
* print out the execution plan for one query
|
* print out the execution plan for one Query
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params,
|
ExplainOneQuery(Query *query, bool isCursor, int cursorOptions,
|
||||||
TupOutputState *tstate)
|
ExplainStmt *stmt, const char *queryString,
|
||||||
|
ParamListInfo params, TupOutputState *tstate)
|
||||||
{
|
{
|
||||||
PlannedStmt *plan;
|
PlannedStmt *plan;
|
||||||
QueryDesc *queryDesc;
|
QueryDesc *queryDesc;
|
||||||
bool isCursor = false;
|
|
||||||
int cursorOptions = 0;
|
|
||||||
|
|
||||||
/* planner will not cope with utility statements */
|
/* planner will not cope with utility statements */
|
||||||
if (query->commandType == CMD_UTILITY)
|
if (query->commandType == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
if (query->utilityStmt && IsA(query->utilityStmt, DeclareCursorStmt))
|
ExplainOneUtility(query->utilityStmt, stmt,
|
||||||
{
|
queryString, params, tstate);
|
||||||
DeclareCursorStmt *dcstmt;
|
|
||||||
List *rewritten;
|
|
||||||
|
|
||||||
dcstmt = (DeclareCursorStmt *) query->utilityStmt;
|
|
||||||
query = (Query *) dcstmt->query;
|
|
||||||
isCursor = true;
|
|
||||||
cursorOptions = dcstmt->options;
|
|
||||||
/* Still need to rewrite cursor command */
|
|
||||||
Assert(query->commandType == CMD_SELECT);
|
|
||||||
/* get locks (we assume ExplainQuery already copied tree) */
|
|
||||||
AcquireRewriteLocks(query);
|
|
||||||
rewritten = QueryRewrite(query);
|
|
||||||
if (list_length(rewritten) != 1)
|
|
||||||
elog(ERROR, "unexpected rewrite result");
|
|
||||||
query = (Query *) linitial(rewritten);
|
|
||||||
Assert(query->commandType == CMD_SELECT);
|
|
||||||
/* do not actually execute the underlying query! */
|
|
||||||
stmt->analyze = false;
|
|
||||||
}
|
|
||||||
else if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
|
|
||||||
{
|
|
||||||
do_text_output_oneline(tstate, "NOTIFY");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
do_text_output_oneline(tstate, "UTILITY");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* plan the query */
|
/* plan the query */
|
||||||
plan = planner(query, isCursor, cursorOptions, params);
|
plan = planner(query, isCursor, cursorOptions, params);
|
||||||
|
@ -210,6 +170,78 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, ParamListInfo params,
|
||||||
ExplainOnePlan(queryDesc, stmt, tstate);
|
ExplainOnePlan(queryDesc, stmt, tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExplainOneUtility -
|
||||||
|
* print out the execution plan for one utility statement
|
||||||
|
* (In general, utility statements don't have plans, but there are some
|
||||||
|
* we treat as special cases)
|
||||||
|
*
|
||||||
|
* This is exported because it's called back from prepare.c in the
|
||||||
|
* EXPLAIN EXECUTE case
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
|
||||||
|
const char *queryString, ParamListInfo params,
|
||||||
|
TupOutputState *tstate)
|
||||||
|
{
|
||||||
|
if (utilityStmt == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (IsA(utilityStmt, DeclareCursorStmt))
|
||||||
|
{
|
||||||
|
DeclareCursorStmt *dcstmt = (DeclareCursorStmt *) utilityStmt;
|
||||||
|
Oid *param_types;
|
||||||
|
int num_params;
|
||||||
|
Query *query;
|
||||||
|
List *rewritten;
|
||||||
|
ExplainStmt newstmt;
|
||||||
|
|
||||||
|
/* Convert parameter type data to the form parser wants */
|
||||||
|
getParamListTypes(params, ¶m_types, &num_params);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run parse analysis and rewrite. Note this also acquires sufficient
|
||||||
|
* locks on the source table(s).
|
||||||
|
*
|
||||||
|
* Because the parser and planner tend to scribble on their input, we
|
||||||
|
* make a preliminary copy of the source querytree. This prevents
|
||||||
|
* problems in the case that the DECLARE CURSOR is in a portal or
|
||||||
|
* plpgsql function and is executed repeatedly. (See also the same
|
||||||
|
* hack in COPY and PREPARE.) XXX FIXME someday.
|
||||||
|
*/
|
||||||
|
rewritten = pg_analyze_and_rewrite((Node *) copyObject(dcstmt->query),
|
||||||
|
queryString,
|
||||||
|
param_types, num_params);
|
||||||
|
|
||||||
|
/* We don't expect more or less than one result query */
|
||||||
|
if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
|
||||||
|
elog(ERROR, "unexpected rewrite result");
|
||||||
|
query = (Query *) linitial(rewritten);
|
||||||
|
if (query->commandType != CMD_SELECT)
|
||||||
|
elog(ERROR, "unexpected rewrite result");
|
||||||
|
|
||||||
|
/* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
|
||||||
|
if (query->into)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||||
|
errmsg("DECLARE CURSOR cannot specify INTO")));
|
||||||
|
|
||||||
|
/* do not actually execute the underlying query! */
|
||||||
|
memcpy(&newstmt, stmt, sizeof(ExplainStmt));
|
||||||
|
newstmt.analyze = false;
|
||||||
|
ExplainOneQuery(query, true, dcstmt->options, &newstmt,
|
||||||
|
queryString, params, tstate);
|
||||||
|
}
|
||||||
|
else if (IsA(utilityStmt, ExecuteStmt))
|
||||||
|
ExplainExecuteQuery((ExecuteStmt *) utilityStmt, stmt,
|
||||||
|
queryString, params, tstate);
|
||||||
|
else if (IsA(utilityStmt, NotifyStmt))
|
||||||
|
do_text_output_oneline(tstate, "NOTIFY");
|
||||||
|
else
|
||||||
|
do_text_output_oneline(tstate,
|
||||||
|
"Utility statements have no plan structure");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ExplainOnePlan -
|
* ExplainOnePlan -
|
||||||
* given a planned query, execute it if needed, and then print
|
* given a planned query, execute it if needed, and then print
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.156 2007/03/06 02:06:12 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.157 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -77,7 +77,6 @@ static bool relationHasPrimaryKey(Relation rel);
|
||||||
* 'attributeList': a list of IndexElem specifying columns and expressions
|
* 'attributeList': a list of IndexElem specifying columns and expressions
|
||||||
* to index on.
|
* to index on.
|
||||||
* 'predicate': the partial-index condition, or NULL if none.
|
* 'predicate': the partial-index condition, or NULL if none.
|
||||||
* 'rangetable': needed to interpret the predicate.
|
|
||||||
* 'options': reloptions from WITH (in list-of-DefElem form).
|
* 'options': reloptions from WITH (in list-of-DefElem form).
|
||||||
* 'unique': make the index enforce uniqueness.
|
* 'unique': make the index enforce uniqueness.
|
||||||
* 'primary': mark the index as a primary key in the catalogs.
|
* 'primary': mark the index as a primary key in the catalogs.
|
||||||
|
@ -99,7 +98,6 @@ DefineIndex(RangeVar *heapRelation,
|
||||||
char *tableSpaceName,
|
char *tableSpaceName,
|
||||||
List *attributeList,
|
List *attributeList,
|
||||||
Expr *predicate,
|
Expr *predicate,
|
||||||
List *rangetable,
|
|
||||||
List *options,
|
List *options,
|
||||||
bool unique,
|
bool unique,
|
||||||
bool primary,
|
bool primary,
|
||||||
|
@ -300,18 +298,6 @@ DefineIndex(RangeVar *heapRelation,
|
||||||
|
|
||||||
ReleaseSysCache(tuple);
|
ReleaseSysCache(tuple);
|
||||||
|
|
||||||
/*
|
|
||||||
* If a range table was created then check that only the base rel is
|
|
||||||
* mentioned.
|
|
||||||
*/
|
|
||||||
if (rangetable != NIL)
|
|
||||||
{
|
|
||||||
if (list_length(rangetable) != 1 || getrelid(1, rangetable) != relationId)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
|
||||||
errmsg("index expressions and predicates can refer only to the table being indexed")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Validate predicate, if given
|
* Validate predicate, if given
|
||||||
*/
|
*/
|
||||||
|
@ -1218,6 +1204,7 @@ ReindexTable(RangeVar *relation)
|
||||||
*
|
*
|
||||||
* To reduce the probability of deadlocks, each table is reindexed in a
|
* To reduce the probability of deadlocks, each table is reindexed in a
|
||||||
* separate transaction, so we can release the lock on it right away.
|
* separate transaction, so we can release the lock on it right away.
|
||||||
|
* That means this must not be called within a user transaction block!
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
|
ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
|
||||||
|
@ -1241,13 +1228,6 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
|
||||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
|
||||||
databaseName);
|
databaseName);
|
||||||
|
|
||||||
/*
|
|
||||||
* We cannot run inside a user transaction block; if we were inside a
|
|
||||||
* transaction, then our commit- and start-transaction-command calls would
|
|
||||||
* not have the intended effect!
|
|
||||||
*/
|
|
||||||
PreventTransactionChain((void *) databaseName, "REINDEX DATABASE");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a memory context that will survive forced transaction commits we
|
* Create a memory context that will survive forced transaction commits we
|
||||||
* do below. Since it is a child of PortalContext, it will go away
|
* do below. Since it is a child of PortalContext, it will go away
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.61 2007/02/20 17:32:14 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.62 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -38,8 +38,11 @@
|
||||||
* Execute SQL DECLARE CURSOR command.
|
* Execute SQL DECLARE CURSOR command.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params,
|
||||||
|
const char *queryString, bool isTopLevel)
|
||||||
{
|
{
|
||||||
|
Oid *param_types;
|
||||||
|
int num_params;
|
||||||
List *rewritten;
|
List *rewritten;
|
||||||
Query *query;
|
Query *query;
|
||||||
PlannedStmt *plan;
|
PlannedStmt *plan;
|
||||||
|
@ -61,40 +64,53 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
||||||
* user-visible effect).
|
* user-visible effect).
|
||||||
*/
|
*/
|
||||||
if (!(stmt->options & CURSOR_OPT_HOLD))
|
if (!(stmt->options & CURSOR_OPT_HOLD))
|
||||||
RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
|
RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Because the planner is not cool about not scribbling on its input, we
|
* Don't allow both SCROLL and NO SCROLL to be specified
|
||||||
|
*/
|
||||||
|
if ((stmt->options & CURSOR_OPT_SCROLL) &&
|
||||||
|
(stmt->options & CURSOR_OPT_NO_SCROLL))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||||
|
errmsg("cannot specify both SCROLL and NO SCROLL")));
|
||||||
|
|
||||||
|
/* Convert parameter type data to the form parser wants */
|
||||||
|
getParamListTypes(params, ¶m_types, &num_params);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run parse analysis and rewrite. Note this also acquires sufficient
|
||||||
|
* locks on the source table(s).
|
||||||
|
*
|
||||||
|
* Because the parser and planner tend to scribble on their input, we
|
||||||
* make a preliminary copy of the source querytree. This prevents
|
* make a preliminary copy of the source querytree. This prevents
|
||||||
* problems in the case that the DECLARE CURSOR is in a portal and is
|
* problems in the case that the DECLARE CURSOR is in a portal or plpgsql
|
||||||
* executed repeatedly. XXX the planner really shouldn't modify its input
|
* function and is executed repeatedly. (See also the same hack in
|
||||||
* ... FIXME someday.
|
* COPY and PREPARE.) XXX FIXME someday.
|
||||||
*/
|
*/
|
||||||
query = copyObject(stmt->query);
|
rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
|
||||||
|
queryString, param_types, num_params);
|
||||||
|
|
||||||
/*
|
/* We don't expect more or less than one result query */
|
||||||
* The query has been through parse analysis, but not rewriting or
|
|
||||||
* planning as yet. Note that the grammar ensured we have a SELECT query,
|
|
||||||
* so we are not expecting rule rewriting to do anything strange.
|
|
||||||
*/
|
|
||||||
AcquireRewriteLocks(query);
|
|
||||||
rewritten = QueryRewrite(query);
|
|
||||||
if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
|
if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
|
||||||
elog(ERROR, "unexpected rewrite result");
|
elog(ERROR, "unexpected rewrite result");
|
||||||
query = (Query *) linitial(rewritten);
|
query = (Query *) linitial(rewritten);
|
||||||
if (query->commandType != CMD_SELECT)
|
if (query->commandType != CMD_SELECT)
|
||||||
elog(ERROR, "unexpected rewrite result");
|
elog(ERROR, "unexpected rewrite result");
|
||||||
|
|
||||||
|
/* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
|
||||||
if (query->into)
|
if (query->into)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
||||||
errmsg("DECLARE CURSOR cannot specify INTO")));
|
errmsg("DECLARE CURSOR cannot specify INTO")));
|
||||||
|
|
||||||
if (query->rowMarks != NIL)
|
if (query->rowMarks != NIL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
|
errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
|
||||||
errdetail("Cursors must be READ ONLY.")));
|
errdetail("Cursors must be READ ONLY.")));
|
||||||
|
|
||||||
|
/* plan the query */
|
||||||
plan = planner(query, true, stmt->options, params);
|
plan = planner(query, true, stmt->options, params);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -106,23 +122,22 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
||||||
|
|
||||||
plan = copyObject(plan);
|
plan = copyObject(plan);
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX: debug_query_string is wrong here: the user might have submitted
|
|
||||||
* multiple semicolon delimited queries.
|
|
||||||
*/
|
|
||||||
PortalDefineQuery(portal,
|
PortalDefineQuery(portal,
|
||||||
NULL,
|
NULL,
|
||||||
debug_query_string ? pstrdup(debug_query_string) : NULL,
|
queryString,
|
||||||
"SELECT", /* cursor's query is always a SELECT */
|
"SELECT", /* cursor's query is always a SELECT */
|
||||||
list_make1(plan),
|
list_make1(plan),
|
||||||
PortalGetHeapMemory(portal));
|
NULL);
|
||||||
|
|
||||||
/*
|
/*----------
|
||||||
* Also copy the outer portal's parameter list into the inner portal's
|
* Also copy the outer portal's parameter list into the inner portal's
|
||||||
* memory context. We want to pass down the parameter values in case we
|
* memory context. We want to pass down the parameter values in case we
|
||||||
* had a command like DECLARE c CURSOR FOR SELECT ... WHERE foo = $1 This
|
* had a command like
|
||||||
* will have been parsed using the outer parameter set and the parameter
|
* DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
|
||||||
* value needs to be preserved for use when the cursor is executed.
|
* This will have been parsed using the outer parameter set and the
|
||||||
|
* parameter value needs to be preserved for use when the cursor is
|
||||||
|
* executed.
|
||||||
|
*----------
|
||||||
*/
|
*/
|
||||||
params = copyParamList(params);
|
params = copyParamList(params);
|
||||||
|
|
||||||
|
@ -314,7 +329,6 @@ PersistHoldablePortal(Portal portal)
|
||||||
Snapshot saveActiveSnapshot;
|
Snapshot saveActiveSnapshot;
|
||||||
ResourceOwner saveResourceOwner;
|
ResourceOwner saveResourceOwner;
|
||||||
MemoryContext savePortalContext;
|
MemoryContext savePortalContext;
|
||||||
MemoryContext saveQueryContext;
|
|
||||||
MemoryContext oldcxt;
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -356,14 +370,12 @@ PersistHoldablePortal(Portal portal)
|
||||||
saveActiveSnapshot = ActiveSnapshot;
|
saveActiveSnapshot = ActiveSnapshot;
|
||||||
saveResourceOwner = CurrentResourceOwner;
|
saveResourceOwner = CurrentResourceOwner;
|
||||||
savePortalContext = PortalContext;
|
savePortalContext = PortalContext;
|
||||||
saveQueryContext = QueryContext;
|
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
ActivePortal = portal;
|
ActivePortal = portal;
|
||||||
ActiveSnapshot = queryDesc->snapshot;
|
ActiveSnapshot = queryDesc->snapshot;
|
||||||
CurrentResourceOwner = portal->resowner;
|
CurrentResourceOwner = portal->resowner;
|
||||||
PortalContext = PortalGetHeapMemory(portal);
|
PortalContext = PortalGetHeapMemory(portal);
|
||||||
QueryContext = portal->queryContext;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(PortalContext);
|
MemoryContextSwitchTo(PortalContext);
|
||||||
|
|
||||||
|
@ -434,7 +446,6 @@ PersistHoldablePortal(Portal portal)
|
||||||
ActiveSnapshot = saveActiveSnapshot;
|
ActiveSnapshot = saveActiveSnapshot;
|
||||||
CurrentResourceOwner = saveResourceOwner;
|
CurrentResourceOwner = saveResourceOwner;
|
||||||
PortalContext = savePortalContext;
|
PortalContext = savePortalContext;
|
||||||
QueryContext = saveQueryContext;
|
|
||||||
|
|
||||||
PG_RE_THROW();
|
PG_RE_THROW();
|
||||||
}
|
}
|
||||||
|
@ -449,7 +460,6 @@ PersistHoldablePortal(Portal portal)
|
||||||
ActiveSnapshot = saveActiveSnapshot;
|
ActiveSnapshot = saveActiveSnapshot;
|
||||||
CurrentResourceOwner = saveResourceOwner;
|
CurrentResourceOwner = saveResourceOwner;
|
||||||
PortalContext = savePortalContext;
|
PortalContext = savePortalContext;
|
||||||
QueryContext = saveQueryContext;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can now release any subsidiary memory of the portal's heap context;
|
* We can now release any subsidiary memory of the portal's heap context;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.69 2007/02/20 17:32:14 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.70 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +22,10 @@
|
||||||
#include "commands/explain.h"
|
#include "commands/explain.h"
|
||||||
#include "commands/prepare.h"
|
#include "commands/prepare.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
|
#include "parser/analyze.h"
|
||||||
|
#include "parser/parse_coerce.h"
|
||||||
|
#include "parser/parse_expr.h"
|
||||||
|
#include "parser/parse_type.h"
|
||||||
#include "rewrite/rewriteHandler.h"
|
#include "rewrite/rewriteHandler.h"
|
||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
|
@ -39,20 +43,24 @@
|
||||||
static HTAB *prepared_queries = NULL;
|
static HTAB *prepared_queries = NULL;
|
||||||
|
|
||||||
static void InitQueryHashTable(void);
|
static void InitQueryHashTable(void);
|
||||||
static ParamListInfo EvaluateParams(EState *estate,
|
static ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
|
||||||
List *params, List *argtypes);
|
const char *queryString, EState *estate);
|
||||||
static Datum build_regtype_array(List *oid_list);
|
static Datum build_regtype_array(Oid *param_types, int num_params);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implements the 'PREPARE' utility statement.
|
* Implements the 'PREPARE' utility statement.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
PrepareQuery(PrepareStmt *stmt)
|
PrepareQuery(PrepareStmt *stmt, const char *queryString)
|
||||||
{
|
{
|
||||||
const char *commandTag;
|
Oid *argtypes = NULL;
|
||||||
|
int nargs;
|
||||||
|
List *queries;
|
||||||
Query *query;
|
Query *query;
|
||||||
|
const char *commandTag;
|
||||||
List *query_list,
|
List *query_list,
|
||||||
*plan_list;
|
*plan_list;
|
||||||
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Disallow empty-string statement name (conflicts with protocol-level
|
* Disallow empty-string statement name (conflicts with protocol-level
|
||||||
|
@ -63,7 +71,70 @@ PrepareQuery(PrepareStmt *stmt)
|
||||||
(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
|
(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
|
||||||
errmsg("invalid statement name: must not be empty")));
|
errmsg("invalid statement name: must not be empty")));
|
||||||
|
|
||||||
switch (stmt->query->commandType)
|
/* Transform list of TypeNames to array of type OIDs */
|
||||||
|
nargs = list_length(stmt->argtypes);
|
||||||
|
|
||||||
|
if (nargs)
|
||||||
|
{
|
||||||
|
ParseState *pstate;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* typenameTypeId wants a ParseState to carry the source query string.
|
||||||
|
* Is it worth refactoring its API to avoid this?
|
||||||
|
*/
|
||||||
|
pstate = make_parsestate(NULL);
|
||||||
|
pstate->p_sourcetext = queryString;
|
||||||
|
|
||||||
|
argtypes = (Oid *) palloc(nargs * sizeof(Oid));
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
foreach(l, stmt->argtypes)
|
||||||
|
{
|
||||||
|
TypeName *tn = lfirst(l);
|
||||||
|
Oid toid = typenameTypeId(pstate, tn);
|
||||||
|
|
||||||
|
argtypes[i++] = toid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Analyze the statement using these parameter types (any parameters
|
||||||
|
* passed in from above us will not be visible to it), allowing
|
||||||
|
* information about unknown parameters to be deduced from context.
|
||||||
|
*
|
||||||
|
* Because parse analysis scribbles on the raw querytree, we must make
|
||||||
|
* a copy to ensure we have a pristine raw tree to cache. FIXME someday.
|
||||||
|
*/
|
||||||
|
queries = parse_analyze_varparams((Node *) copyObject(stmt->query),
|
||||||
|
queryString,
|
||||||
|
&argtypes, &nargs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that all parameter types were determined.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < nargs; i++)
|
||||||
|
{
|
||||||
|
Oid argtype = argtypes[i];
|
||||||
|
|
||||||
|
if (argtype == InvalidOid || argtype == UNKNOWNOID)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
|
||||||
|
errmsg("could not determine data type of parameter $%d",
|
||||||
|
i + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shouldn't get any extra statements, since grammar only allows
|
||||||
|
* OptimizableStmt
|
||||||
|
*/
|
||||||
|
if (list_length(queries) != 1)
|
||||||
|
elog(ERROR, "unexpected extra stuff in prepared statement");
|
||||||
|
|
||||||
|
query = (Query *) linitial(queries);
|
||||||
|
Assert(IsA(query, Query));
|
||||||
|
|
||||||
|
switch (query->commandType)
|
||||||
{
|
{
|
||||||
case CMD_SELECT:
|
case CMD_SELECT:
|
||||||
commandTag = "SELECT";
|
commandTag = "SELECT";
|
||||||
|
@ -85,38 +156,22 @@ PrepareQuery(PrepareStmt *stmt)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse analysis is already done, but we must still rewrite and plan the
|
|
||||||
* query.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Because the planner is not cool about not scribbling on its input, we
|
|
||||||
* make a preliminary copy of the source querytree. This prevents
|
|
||||||
* problems in the case that the PREPARE is in a portal or plpgsql
|
|
||||||
* function and is executed repeatedly. (See also the same hack in
|
|
||||||
* DECLARE CURSOR and EXPLAIN.) XXX the planner really shouldn't modify
|
|
||||||
* its input ... FIXME someday.
|
|
||||||
*/
|
|
||||||
query = copyObject(stmt->query);
|
|
||||||
|
|
||||||
/* Rewrite the query. The result could be 0, 1, or many queries. */
|
/* Rewrite the query. The result could be 0, 1, or many queries. */
|
||||||
AcquireRewriteLocks(query);
|
|
||||||
query_list = QueryRewrite(query);
|
query_list = QueryRewrite(query);
|
||||||
|
|
||||||
/* Generate plans for queries. Snapshot is already set. */
|
/* Generate plans for queries. Snapshot is already set. */
|
||||||
plan_list = pg_plan_queries(query_list, NULL, false);
|
plan_list = pg_plan_queries(query_list, NULL, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save the results. We don't have the query string for this PREPARE, but
|
* Save the results.
|
||||||
* we do have the string we got from the client, so use that.
|
|
||||||
*/
|
*/
|
||||||
StorePreparedStatement(stmt->name,
|
StorePreparedStatement(stmt->name,
|
||||||
debug_query_string,
|
stmt->query,
|
||||||
|
queryString,
|
||||||
commandTag,
|
commandTag,
|
||||||
|
argtypes,
|
||||||
|
nargs,
|
||||||
plan_list,
|
plan_list,
|
||||||
stmt->argtype_oids,
|
|
||||||
true,
|
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,13 +179,13 @@ PrepareQuery(PrepareStmt *stmt)
|
||||||
* Implements the 'EXECUTE' utility statement.
|
* Implements the 'EXECUTE' utility statement.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
|
||||||
|
ParamListInfo params,
|
||||||
DestReceiver *dest, char *completionTag)
|
DestReceiver *dest, char *completionTag)
|
||||||
{
|
{
|
||||||
PreparedStatement *entry;
|
PreparedStatement *entry;
|
||||||
char *query_string;
|
CachedPlan *cplan;
|
||||||
List *plan_list;
|
List *plan_list;
|
||||||
MemoryContext qcontext;
|
|
||||||
ParamListInfo paramLI = NULL;
|
ParamListInfo paramLI = NULL;
|
||||||
EState *estate = NULL;
|
EState *estate = NULL;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
|
@ -138,20 +193,15 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
||||||
/* Look it up in the hash table */
|
/* Look it up in the hash table */
|
||||||
entry = FetchPreparedStatement(stmt->name, true);
|
entry = FetchPreparedStatement(stmt->name, true);
|
||||||
|
|
||||||
/*
|
/* Shouldn't have a non-fully-planned plancache entry */
|
||||||
* Punt if not fully planned. (Currently, that only happens for the
|
if (!entry->plansource->fully_planned)
|
||||||
* protocol-level unnamed statement, which can't be accessed from SQL;
|
|
||||||
* so there's no point in doing more than a quick check here.)
|
|
||||||
*/
|
|
||||||
if (!entry->fully_planned)
|
|
||||||
elog(ERROR, "EXECUTE does not support unplanned prepared statements");
|
elog(ERROR, "EXECUTE does not support unplanned prepared statements");
|
||||||
|
/* Shouldn't get any non-fixed-result cached plan, either */
|
||||||
query_string = entry->query_string;
|
if (!entry->plansource->fixed_result)
|
||||||
plan_list = entry->stmt_list;
|
elog(ERROR, "EXECUTE does not support variable-result cached plans");
|
||||||
qcontext = entry->context;
|
|
||||||
|
|
||||||
/* Evaluate parameters, if any */
|
/* Evaluate parameters, if any */
|
||||||
if (entry->argtype_list != NIL)
|
if (entry->plansource->num_params > 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Need an EState to evaluate parameters; must not delete it till end
|
* Need an EState to evaluate parameters; must not delete it till end
|
||||||
|
@ -159,7 +209,8 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
||||||
*/
|
*/
|
||||||
estate = CreateExecutorState();
|
estate = CreateExecutorState();
|
||||||
estate->es_param_list_info = params;
|
estate->es_param_list_info = params;
|
||||||
paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list);
|
paramLI = EvaluateParams(entry, stmt->params,
|
||||||
|
queryString, estate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a new portal to run the query in */
|
/* Create a new portal to run the query in */
|
||||||
|
@ -168,22 +219,23 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
||||||
portal->visible = false;
|
portal->visible = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For CREATE TABLE / AS EXECUTE, make a copy of the stored query so that
|
* For CREATE TABLE / AS EXECUTE, we must make a copy of the stored query
|
||||||
* we can modify its destination (yech, but this has always been ugly).
|
* so that we can modify its destination (yech, but this has always been
|
||||||
* For regular EXECUTE we can just use the stored query where it sits,
|
* ugly). For regular EXECUTE we can just use the cached query, since the
|
||||||
* since the executor is read-only.
|
* executor is read-only.
|
||||||
*/
|
*/
|
||||||
if (stmt->into)
|
if (stmt->into)
|
||||||
{
|
{
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
PlannedStmt *pstmt;
|
PlannedStmt *pstmt;
|
||||||
|
|
||||||
qcontext = PortalGetHeapMemory(portal);
|
/* Replan if needed, and increment plan refcount transiently */
|
||||||
oldContext = MemoryContextSwitchTo(qcontext);
|
cplan = RevalidateCachedPlan(entry->plansource, true);
|
||||||
|
|
||||||
if (query_string)
|
/* Copy plan into portal's context, and modify */
|
||||||
query_string = pstrdup(query_string);
|
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||||
plan_list = copyObject(plan_list);
|
|
||||||
|
plan_list = copyObject(cplan->stmt_list);
|
||||||
|
|
||||||
if (list_length(plan_list) != 1)
|
if (list_length(plan_list) != 1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -198,21 +250,32 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
||||||
pstmt->into = copyObject(stmt->into);
|
pstmt->into = copyObject(stmt->into);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
|
||||||
|
/* We no longer need the cached plan refcount ... */
|
||||||
|
ReleaseCachedPlan(cplan, true);
|
||||||
|
/* ... and we don't want the portal to depend on it, either */
|
||||||
|
cplan = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Replan if needed, and increment plan refcount for portal */
|
||||||
|
cplan = RevalidateCachedPlan(entry->plansource, false);
|
||||||
|
plan_list = cplan->stmt_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
PortalDefineQuery(portal,
|
PortalDefineQuery(portal,
|
||||||
NULL,
|
NULL,
|
||||||
query_string,
|
entry->plansource->query_string,
|
||||||
entry->commandTag,
|
entry->plansource->commandTag,
|
||||||
plan_list,
|
plan_list,
|
||||||
qcontext);
|
cplan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Run the portal to completion.
|
* Run the portal to completion.
|
||||||
*/
|
*/
|
||||||
PortalStart(portal, paramLI, ActiveSnapshot);
|
PortalStart(portal, paramLI, ActiveSnapshot);
|
||||||
|
|
||||||
(void) PortalRun(portal, FETCH_ALL, dest, dest, completionTag);
|
(void) PortalRun(portal, FETCH_ALL, false, dest, dest, completionTag);
|
||||||
|
|
||||||
PortalDrop(portal, false);
|
PortalDrop(portal, false);
|
||||||
|
|
||||||
|
@ -223,42 +286,106 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluates a list of parameters, using the given executor state. It
|
* EvaluateParams: evaluate a list of parameters.
|
||||||
* requires a list of the parameter expressions themselves, and a list of
|
*
|
||||||
* their types. It returns a filled-in ParamListInfo -- this can later
|
* pstmt: statement we are getting parameters for.
|
||||||
* be passed to CreateQueryDesc(), which allows the executor to make use
|
* params: list of given parameter expressions (raw parser output!)
|
||||||
* of the parameters during query execution.
|
* queryString: source text for error messages.
|
||||||
|
* estate: executor state to use.
|
||||||
|
*
|
||||||
|
* Returns a filled-in ParamListInfo -- this can later be passed to
|
||||||
|
* CreateQueryDesc(), which allows the executor to make use of the parameters
|
||||||
|
* during query execution.
|
||||||
*/
|
*/
|
||||||
static ParamListInfo
|
static ParamListInfo
|
||||||
EvaluateParams(EState *estate, List *params, List *argtypes)
|
EvaluateParams(PreparedStatement *pstmt, List *params,
|
||||||
|
const char *queryString, EState *estate)
|
||||||
{
|
{
|
||||||
int nargs = list_length(argtypes);
|
Oid *param_types = pstmt->plansource->param_types;
|
||||||
|
int num_params = pstmt->plansource->num_params;
|
||||||
|
int nparams = list_length(params);
|
||||||
|
ParseState *pstate;
|
||||||
ParamListInfo paramLI;
|
ParamListInfo paramLI;
|
||||||
List *exprstates;
|
List *exprstates;
|
||||||
ListCell *le,
|
ListCell *l;
|
||||||
*la;
|
int i;
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
/* Parser should have caught this error, but check for safety */
|
if (nparams != num_params)
|
||||||
if (list_length(params) != nargs)
|
ereport(ERROR,
|
||||||
elog(ERROR, "wrong number of arguments");
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("wrong number of parameters for prepared statement \"%s\"",
|
||||||
|
pstmt->stmt_name),
|
||||||
|
errdetail("Expected %d parameters but got %d.",
|
||||||
|
num_params, nparams)));
|
||||||
|
|
||||||
if (nargs == 0)
|
/* Quick exit if no parameters */
|
||||||
|
if (num_params == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have to run parse analysis for the expressions. Since the
|
||||||
|
* parser is not cool about scribbling on its input, copy first.
|
||||||
|
*/
|
||||||
|
params = (List *) copyObject(params);
|
||||||
|
|
||||||
|
pstate = make_parsestate(NULL);
|
||||||
|
pstate->p_sourcetext = queryString;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
foreach(l, params)
|
||||||
|
{
|
||||||
|
Node *expr = lfirst(l);
|
||||||
|
Oid expected_type_id = param_types[i];
|
||||||
|
Oid given_type_id;
|
||||||
|
|
||||||
|
expr = transformExpr(pstate, expr);
|
||||||
|
|
||||||
|
/* Cannot contain subselects or aggregates */
|
||||||
|
if (pstate->p_hasSubLinks)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot use subquery in EXECUTE parameter")));
|
||||||
|
if (pstate->p_hasAggs)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_GROUPING_ERROR),
|
||||||
|
errmsg("cannot use aggregate function in EXECUTE parameter")));
|
||||||
|
|
||||||
|
given_type_id = exprType(expr);
|
||||||
|
|
||||||
|
expr = coerce_to_target_type(pstate, expr, given_type_id,
|
||||||
|
expected_type_id, -1,
|
||||||
|
COERCION_ASSIGNMENT,
|
||||||
|
COERCE_IMPLICIT_CAST);
|
||||||
|
|
||||||
|
if (expr == NULL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
|
||||||
|
i + 1,
|
||||||
|
format_type_be(given_type_id),
|
||||||
|
format_type_be(expected_type_id)),
|
||||||
|
errhint("You will need to rewrite or cast the expression.")));
|
||||||
|
|
||||||
|
lfirst(l) = expr;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare the expressions for execution */
|
||||||
exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
|
exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
|
||||||
|
|
||||||
/* sizeof(ParamListInfoData) includes the first array element */
|
/* sizeof(ParamListInfoData) includes the first array element */
|
||||||
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
|
paramLI = (ParamListInfo)
|
||||||
(nargs - 1) *sizeof(ParamExternData));
|
palloc(sizeof(ParamListInfoData) +
|
||||||
paramLI->numParams = nargs;
|
(num_params - 1) *sizeof(ParamExternData));
|
||||||
|
paramLI->numParams = num_params;
|
||||||
|
|
||||||
forboth(le, exprstates, la, argtypes)
|
i = 0;
|
||||||
|
foreach(l, exprstates)
|
||||||
{
|
{
|
||||||
ExprState *n = lfirst(le);
|
ExprState *n = lfirst(l);
|
||||||
ParamExternData *prm = ¶mLI->params[i];
|
ParamExternData *prm = ¶mLI->params[i];
|
||||||
|
|
||||||
prm->ptype = lfirst_oid(la);
|
prm->ptype = param_types[i];
|
||||||
prm->pflags = 0;
|
prm->pflags = 0;
|
||||||
prm->value = ExecEvalExprSwitchContext(n,
|
prm->value = ExecEvalExprSwitchContext(n,
|
||||||
GetPerTupleExprContext(estate),
|
GetPerTupleExprContext(estate),
|
||||||
|
@ -293,8 +420,9 @@ InitQueryHashTable(void)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store all the data pertaining to a query in the hash table using
|
* Store all the data pertaining to a query in the hash table using
|
||||||
* the specified key. A copy of the data is made in a memory context belonging
|
* the specified key. All the given data is copied into either the hashtable
|
||||||
* to the hash entry, so the caller can dispose of their copy.
|
* entry or the underlying plancache entry, so the caller can dispose of its
|
||||||
|
* copy.
|
||||||
*
|
*
|
||||||
* Exception: commandTag is presumed to be a pointer to a constant string,
|
* Exception: commandTag is presumed to be a pointer to a constant string,
|
||||||
* or possibly NULL, so it need not be copied. Note that commandTag should
|
* or possibly NULL, so it need not be copied. Note that commandTag should
|
||||||
|
@ -302,17 +430,16 @@ InitQueryHashTable(void)
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
StorePreparedStatement(const char *stmt_name,
|
StorePreparedStatement(const char *stmt_name,
|
||||||
|
Node *raw_parse_tree,
|
||||||
const char *query_string,
|
const char *query_string,
|
||||||
const char *commandTag,
|
const char *commandTag,
|
||||||
|
Oid *param_types,
|
||||||
|
int num_params,
|
||||||
List *stmt_list,
|
List *stmt_list,
|
||||||
List *argtype_list,
|
|
||||||
bool fully_planned,
|
|
||||||
bool from_sql)
|
bool from_sql)
|
||||||
{
|
{
|
||||||
PreparedStatement *entry;
|
PreparedStatement *entry;
|
||||||
MemoryContext oldcxt,
|
CachedPlanSource *plansource;
|
||||||
entrycxt;
|
|
||||||
char *qstring;
|
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
/* Initialize the hash table, if necessary */
|
/* Initialize the hash table, if necessary */
|
||||||
|
@ -328,24 +455,15 @@ StorePreparedStatement(const char *stmt_name,
|
||||||
errmsg("prepared statement \"%s\" already exists",
|
errmsg("prepared statement \"%s\" already exists",
|
||||||
stmt_name)));
|
stmt_name)));
|
||||||
|
|
||||||
/* Make a permanent memory context for the hashtable entry */
|
/* Create a plancache entry */
|
||||||
entrycxt = AllocSetContextCreate(TopMemoryContext,
|
plansource = CreateCachedPlan(raw_parse_tree,
|
||||||
stmt_name,
|
query_string,
|
||||||
ALLOCSET_SMALL_MINSIZE,
|
commandTag,
|
||||||
ALLOCSET_SMALL_INITSIZE,
|
param_types,
|
||||||
ALLOCSET_SMALL_MAXSIZE);
|
num_params,
|
||||||
|
stmt_list,
|
||||||
oldcxt = MemoryContextSwitchTo(entrycxt);
|
true,
|
||||||
|
true);
|
||||||
/*
|
|
||||||
* We need to copy the data so that it is stored in the correct memory
|
|
||||||
* context. Do this before making hashtable entry, so that an
|
|
||||||
* out-of-memory failure only wastes memory and doesn't leave us with an
|
|
||||||
* incomplete (ie corrupt) hashtable entry.
|
|
||||||
*/
|
|
||||||
qstring = query_string ? pstrdup(query_string) : NULL;
|
|
||||||
stmt_list = (List *) copyObject(stmt_list);
|
|
||||||
argtype_list = list_copy(argtype_list);
|
|
||||||
|
|
||||||
/* Now we can add entry to hash table */
|
/* Now we can add entry to hash table */
|
||||||
entry = (PreparedStatement *) hash_search(prepared_queries,
|
entry = (PreparedStatement *) hash_search(prepared_queries,
|
||||||
|
@ -358,22 +476,18 @@ StorePreparedStatement(const char *stmt_name,
|
||||||
elog(ERROR, "duplicate prepared statement \"%s\"",
|
elog(ERROR, "duplicate prepared statement \"%s\"",
|
||||||
stmt_name);
|
stmt_name);
|
||||||
|
|
||||||
/* Fill in the hash table entry with copied data */
|
/* Fill in the hash table entry */
|
||||||
entry->query_string = qstring;
|
entry->plansource = plansource;
|
||||||
entry->commandTag = commandTag;
|
|
||||||
entry->stmt_list = stmt_list;
|
|
||||||
entry->argtype_list = argtype_list;
|
|
||||||
entry->fully_planned = fully_planned;
|
|
||||||
entry->from_sql = from_sql;
|
entry->from_sql = from_sql;
|
||||||
entry->context = entrycxt;
|
|
||||||
entry->prepare_time = GetCurrentStatementStartTimestamp();
|
entry->prepare_time = GetCurrentStatementStartTimestamp();
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lookup an existing query in the hash table. If the query does not
|
* Lookup an existing query in the hash table. If the query does not
|
||||||
* actually exist, throw ereport(ERROR) or return NULL per second parameter.
|
* actually exist, throw ereport(ERROR) or return NULL per second parameter.
|
||||||
|
*
|
||||||
|
* Note: this does not force the referenced plancache entry to be valid,
|
||||||
|
* since not all callers care.
|
||||||
*/
|
*/
|
||||||
PreparedStatement *
|
PreparedStatement *
|
||||||
FetchPreparedStatement(const char *stmt_name, bool throwError)
|
FetchPreparedStatement(const char *stmt_name, bool throwError)
|
||||||
|
@ -401,20 +515,6 @@ FetchPreparedStatement(const char *stmt_name, bool throwError)
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Look up a prepared statement given the name (giving error if not found).
|
|
||||||
* If found, return the list of argument type OIDs.
|
|
||||||
*/
|
|
||||||
List *
|
|
||||||
FetchPreparedStatementParams(const char *stmt_name)
|
|
||||||
{
|
|
||||||
PreparedStatement *entry;
|
|
||||||
|
|
||||||
entry = FetchPreparedStatement(stmt_name, true);
|
|
||||||
|
|
||||||
return entry->argtype_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a prepared statement, determine the result tupledesc it will
|
* Given a prepared statement, determine the result tupledesc it will
|
||||||
* produce. Returns NULL if the execution will not return tuples.
|
* produce. Returns NULL if the execution will not return tuples.
|
||||||
|
@ -424,85 +524,15 @@ FetchPreparedStatementParams(const char *stmt_name)
|
||||||
TupleDesc
|
TupleDesc
|
||||||
FetchPreparedStatementResultDesc(PreparedStatement *stmt)
|
FetchPreparedStatementResultDesc(PreparedStatement *stmt)
|
||||||
{
|
{
|
||||||
Node *node;
|
/*
|
||||||
Query *query;
|
* Since we don't allow prepared statements' result tupdescs to change,
|
||||||
PlannedStmt *pstmt;
|
* there's no need for a revalidate call here.
|
||||||
|
|
||||||
switch (ChoosePortalStrategy(stmt->stmt_list))
|
|
||||||
{
|
|
||||||
case PORTAL_ONE_SELECT:
|
|
||||||
node = (Node *) linitial(stmt->stmt_list);
|
|
||||||
if (IsA(node, Query))
|
|
||||||
{
|
|
||||||
query = (Query *) node;
|
|
||||||
return ExecCleanTypeFromTL(query->targetList, false);
|
|
||||||
}
|
|
||||||
if (IsA(node, PlannedStmt))
|
|
||||||
{
|
|
||||||
pstmt = (PlannedStmt *) node;
|
|
||||||
return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
|
|
||||||
}
|
|
||||||
/* other cases shouldn't happen, but return NULL */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PORTAL_ONE_RETURNING:
|
|
||||||
node = PortalListGetPrimaryStmt(stmt->stmt_list);
|
|
||||||
if (IsA(node, Query))
|
|
||||||
{
|
|
||||||
query = (Query *) node;
|
|
||||||
Assert(query->returningList);
|
|
||||||
return ExecCleanTypeFromTL(query->returningList, false);
|
|
||||||
}
|
|
||||||
if (IsA(node, PlannedStmt))
|
|
||||||
{
|
|
||||||
pstmt = (PlannedStmt *) node;
|
|
||||||
Assert(pstmt->returningLists);
|
|
||||||
return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
|
|
||||||
}
|
|
||||||
/* other cases shouldn't happen, but return NULL */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PORTAL_UTIL_SELECT:
|
|
||||||
node = (Node *) linitial(stmt->stmt_list);
|
|
||||||
if (IsA(node, Query))
|
|
||||||
{
|
|
||||||
query = (Query *) node;
|
|
||||||
Assert(query->utilityStmt);
|
|
||||||
return UtilityTupleDescriptor(query->utilityStmt);
|
|
||||||
}
|
|
||||||
/* else it's a bare utility statement */
|
|
||||||
return UtilityTupleDescriptor(node);
|
|
||||||
|
|
||||||
case PORTAL_MULTI_QUERY:
|
|
||||||
/* will not return tuples */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given a prepared statement, determine whether it will return tuples.
|
|
||||||
*
|
|
||||||
* Note: this is used rather than just testing the result of
|
|
||||||
* FetchPreparedStatementResultDesc() because that routine can fail if
|
|
||||||
* invoked in an aborted transaction. This one is safe to use in any
|
|
||||||
* context. Be sure to keep the two routines in sync!
|
|
||||||
*/
|
*/
|
||||||
bool
|
Assert(stmt->plansource->fixed_result);
|
||||||
PreparedStatementReturnsTuples(PreparedStatement *stmt)
|
if (stmt->plansource->resultDesc)
|
||||||
{
|
return CreateTupleDescCopy(stmt->plansource->resultDesc);
|
||||||
switch (ChoosePortalStrategy(stmt->stmt_list))
|
else
|
||||||
{
|
return NULL;
|
||||||
case PORTAL_ONE_SELECT:
|
|
||||||
case PORTAL_ONE_RETURNING:
|
|
||||||
case PORTAL_UTIL_SELECT:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case PORTAL_MULTI_QUERY:
|
|
||||||
/* will not return tuples */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -510,16 +540,32 @@ PreparedStatementReturnsTuples(PreparedStatement *stmt)
|
||||||
* targetlist. Returns NIL if the statement doesn't have a determinable
|
* targetlist. Returns NIL if the statement doesn't have a determinable
|
||||||
* targetlist.
|
* targetlist.
|
||||||
*
|
*
|
||||||
* Note: do not modify the result.
|
* Note: this is pretty ugly, but since it's only used in corner cases like
|
||||||
|
* Describe Statement on an EXECUTE command, we don't worry too much about
|
||||||
|
* efficiency.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
FetchPreparedStatementTargetList(PreparedStatement *stmt)
|
FetchPreparedStatementTargetList(PreparedStatement *stmt)
|
||||||
{
|
{
|
||||||
/* no point in looking if it doesn't return tuples */
|
List *tlist;
|
||||||
if (ChoosePortalStrategy(stmt->stmt_list) == PORTAL_MULTI_QUERY)
|
CachedPlan *cplan;
|
||||||
|
|
||||||
|
/* No point in looking if it doesn't return tuples */
|
||||||
|
if (stmt->plansource->resultDesc == NULL)
|
||||||
return NIL;
|
return NIL;
|
||||||
/* get the primary statement and find out what it returns */
|
|
||||||
return FetchStatementTargetList(PortalListGetPrimaryStmt(stmt->stmt_list));
|
/* Make sure the plan is up to date */
|
||||||
|
cplan = RevalidateCachedPlan(stmt->plansource, true);
|
||||||
|
|
||||||
|
/* Get the primary statement and find out what it returns */
|
||||||
|
tlist = FetchStatementTargetList(PortalListGetPrimaryStmt(cplan->stmt_list));
|
||||||
|
|
||||||
|
/* Copy into caller's context so we can release the plancache entry */
|
||||||
|
tlist = (List *) copyObject(tlist);
|
||||||
|
|
||||||
|
ReleaseCachedPlan(cplan, true);
|
||||||
|
|
||||||
|
return tlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -547,12 +593,8 @@ DropPreparedStatement(const char *stmt_name, bool showError)
|
||||||
|
|
||||||
if (entry)
|
if (entry)
|
||||||
{
|
{
|
||||||
/* Drop any open portals that depend on this prepared statement */
|
/* Release the plancache entry */
|
||||||
Assert(MemoryContextIsValid(entry->context));
|
DropCachedPlan(entry->plansource);
|
||||||
DropDependentPortals(entry->context);
|
|
||||||
|
|
||||||
/* Flush the context holding the subsidiary data */
|
|
||||||
MemoryContextDelete(entry->context);
|
|
||||||
|
|
||||||
/* Now we can remove the hash table entry */
|
/* Now we can remove the hash table entry */
|
||||||
hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
|
hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
|
||||||
|
@ -563,34 +605,34 @@ DropPreparedStatement(const char *stmt_name, bool showError)
|
||||||
* Implements the 'EXPLAIN EXECUTE' utility statement.
|
* Implements the 'EXPLAIN EXECUTE' utility statement.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
|
||||||
TupOutputState *tstate)
|
const char *queryString,
|
||||||
|
ParamListInfo params, TupOutputState *tstate)
|
||||||
{
|
{
|
||||||
ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
|
|
||||||
PreparedStatement *entry;
|
PreparedStatement *entry;
|
||||||
|
CachedPlan *cplan;
|
||||||
List *plan_list;
|
List *plan_list;
|
||||||
ListCell *p;
|
ListCell *p;
|
||||||
ParamListInfo paramLI = NULL;
|
ParamListInfo paramLI = NULL;
|
||||||
EState *estate = NULL;
|
EState *estate = NULL;
|
||||||
|
|
||||||
/* explain.c should only call me for EXECUTE stmt */
|
|
||||||
Assert(execstmt && IsA(execstmt, ExecuteStmt));
|
|
||||||
|
|
||||||
/* Look it up in the hash table */
|
/* Look it up in the hash table */
|
||||||
entry = FetchPreparedStatement(execstmt->name, true);
|
entry = FetchPreparedStatement(execstmt->name, true);
|
||||||
|
|
||||||
/*
|
/* Shouldn't have a non-fully-planned plancache entry */
|
||||||
* Punt if not fully planned. (Currently, that only happens for the
|
if (!entry->plansource->fully_planned)
|
||||||
* protocol-level unnamed statement, which can't be accessed from SQL;
|
|
||||||
* so there's no point in doing more than a quick check here.)
|
|
||||||
*/
|
|
||||||
if (!entry->fully_planned)
|
|
||||||
elog(ERROR, "EXPLAIN EXECUTE does not support unplanned prepared statements");
|
elog(ERROR, "EXPLAIN EXECUTE does not support unplanned prepared statements");
|
||||||
|
/* Shouldn't get any non-fixed-result cached plan, either */
|
||||||
|
if (!entry->plansource->fixed_result)
|
||||||
|
elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
|
||||||
|
|
||||||
plan_list = entry->stmt_list;
|
/* Replan if needed, and acquire a transient refcount */
|
||||||
|
cplan = RevalidateCachedPlan(entry->plansource, true);
|
||||||
|
|
||||||
|
plan_list = cplan->stmt_list;
|
||||||
|
|
||||||
/* Evaluate parameters, if any */
|
/* Evaluate parameters, if any */
|
||||||
if (entry->argtype_list != NIL)
|
if (entry->plansource->num_params)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Need an EState to evaluate parameters; must not delete it till end
|
* Need an EState to evaluate parameters; must not delete it till end
|
||||||
|
@ -598,8 +640,8 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
||||||
*/
|
*/
|
||||||
estate = CreateExecutorState();
|
estate = CreateExecutorState();
|
||||||
estate->es_param_list_info = params;
|
estate->es_param_list_info = params;
|
||||||
paramLI = EvaluateParams(estate, execstmt->params,
|
paramLI = EvaluateParams(entry, execstmt->params,
|
||||||
entry->argtype_list);
|
queryString, estate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Explain each query */
|
/* Explain each query */
|
||||||
|
@ -610,14 +652,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
||||||
|
|
||||||
is_last_query = (lnext(p) == NULL);
|
is_last_query = (lnext(p) == NULL);
|
||||||
|
|
||||||
if (!IsA(pstmt, PlannedStmt))
|
if (IsA(pstmt, PlannedStmt))
|
||||||
{
|
|
||||||
if (IsA(pstmt, NotifyStmt))
|
|
||||||
do_text_output_oneline(tstate, "NOTIFY");
|
|
||||||
else
|
|
||||||
do_text_output_oneline(tstate, "UTILITY");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
QueryDesc *qdesc;
|
QueryDesc *qdesc;
|
||||||
|
|
||||||
|
@ -651,6 +686,11 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
||||||
|
|
||||||
ExplainOnePlan(qdesc, stmt, tstate);
|
ExplainOnePlan(qdesc, stmt, tstate);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExplainOneUtility((Node *) pstmt, stmt, queryString,
|
||||||
|
params, tstate);
|
||||||
|
}
|
||||||
|
|
||||||
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
|
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
|
||||||
|
|
||||||
|
@ -661,6 +701,8 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
||||||
|
|
||||||
if (estate)
|
if (estate)
|
||||||
FreeExecutorState(estate);
|
FreeExecutorState(estate);
|
||||||
|
|
||||||
|
ReleaseCachedPlan(cplan, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -739,14 +781,15 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
|
||||||
values[0] = DirectFunctionCall1(textin,
|
values[0] = DirectFunctionCall1(textin,
|
||||||
CStringGetDatum(prep_stmt->stmt_name));
|
CStringGetDatum(prep_stmt->stmt_name));
|
||||||
|
|
||||||
if (prep_stmt->query_string == NULL)
|
if (prep_stmt->plansource->query_string == NULL)
|
||||||
nulls[1] = true;
|
nulls[1] = true;
|
||||||
else
|
else
|
||||||
values[1] = DirectFunctionCall1(textin,
|
values[1] = DirectFunctionCall1(textin,
|
||||||
CStringGetDatum(prep_stmt->query_string));
|
CStringGetDatum(prep_stmt->plansource->query_string));
|
||||||
|
|
||||||
values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
|
values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
|
||||||
values[3] = build_regtype_array(prep_stmt->argtype_list);
|
values[3] = build_regtype_array(prep_stmt->plansource->param_types,
|
||||||
|
prep_stmt->plansource->num_params);
|
||||||
values[4] = BoolGetDatum(prep_stmt->from_sql);
|
values[4] = BoolGetDatum(prep_stmt->from_sql);
|
||||||
|
|
||||||
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
|
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
|
||||||
|
@ -758,29 +801,23 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This utility function takes a List of Oids, and returns a Datum
|
* This utility function takes a C array of Oids, and returns a Datum
|
||||||
* pointing to a one-dimensional Postgres array of regtypes. The empty
|
* pointing to a one-dimensional Postgres array of regtypes. An empty
|
||||||
* list is returned as a zero-element array, not NULL.
|
* array is returned as a zero-element array, not NULL.
|
||||||
*/
|
*/
|
||||||
static Datum
|
static Datum
|
||||||
build_regtype_array(List *oid_list)
|
build_regtype_array(Oid *param_types, int num_params)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
|
||||||
int len;
|
|
||||||
int i;
|
|
||||||
Datum *tmp_ary;
|
Datum *tmp_ary;
|
||||||
ArrayType *result;
|
ArrayType *result;
|
||||||
|
int i;
|
||||||
|
|
||||||
len = list_length(oid_list);
|
tmp_ary = (Datum *) palloc(num_params * sizeof(Datum));
|
||||||
tmp_ary = (Datum *) palloc(len * sizeof(Datum));
|
|
||||||
|
|
||||||
i = 0;
|
for (i = 0; i < num_params; i++)
|
||||||
foreach(lc, oid_list)
|
tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
|
||||||
{
|
|
||||||
tmp_ary[i++] = ObjectIdGetDatum(lfirst_oid(lc));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX: this hardcodes assumptions about the regtype type */
|
/* XXX: this hardcodes assumptions about the regtype type */
|
||||||
result = construct_array(tmp_ary, len, REGTYPEOID, 4, true, 'i');
|
result = construct_array(tmp_ary, num_params, REGTYPEOID, 4, true, 'i');
|
||||||
return PointerGetDatum(result);
|
return PointerGetDatum(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.43 2007/02/01 19:10:26 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.44 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -38,7 +38,7 @@ static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerI
|
||||||
* CREATE SCHEMA
|
* CREATE SCHEMA
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
CreateSchemaCommand(CreateSchemaStmt *stmt)
|
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
|
||||||
{
|
{
|
||||||
const char *schemaName = stmt->schemaname;
|
const char *schemaName = stmt->schemaname;
|
||||||
const char *authId = stmt->authid;
|
const char *authId = stmt->authid;
|
||||||
|
@ -122,7 +122,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
|
||||||
List *querytree_list;
|
List *querytree_list;
|
||||||
ListCell *querytree_item;
|
ListCell *querytree_item;
|
||||||
|
|
||||||
querytree_list = parse_analyze(parsetree, NULL, NULL, 0);
|
querytree_list = parse_analyze(parsetree, queryString, NULL, 0);
|
||||||
|
|
||||||
foreach(querytree_item, querytree_list)
|
foreach(querytree_item, querytree_list)
|
||||||
{
|
{
|
||||||
|
@ -131,7 +131,12 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
|
||||||
/* schemas should contain only utility stmts */
|
/* schemas should contain only utility stmts */
|
||||||
Assert(querytree->commandType == CMD_UTILITY);
|
Assert(querytree->commandType == CMD_UTILITY);
|
||||||
/* do this step */
|
/* do this step */
|
||||||
ProcessUtility(querytree->utilityStmt, NULL, None_Receiver, NULL);
|
ProcessUtility(querytree->utilityStmt,
|
||||||
|
queryString,
|
||||||
|
NULL,
|
||||||
|
false, /* not top level */
|
||||||
|
None_Receiver,
|
||||||
|
NULL);
|
||||||
/* make sure later steps can see the object created here */
|
/* make sure later steps can see the object created here */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.216 2007/03/06 02:06:13 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.217 2007/03/13 00:33:39 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -3696,6 +3696,13 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
|
||||||
/* suppress notices when rebuilding existing index */
|
/* suppress notices when rebuilding existing index */
|
||||||
quiet = is_rebuild;
|
quiet = is_rebuild;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run parse analysis. We don't have convenient access to the query text
|
||||||
|
* here, but it's probably not worth worrying about.
|
||||||
|
*/
|
||||||
|
stmt = analyzeIndexStmt(stmt, NULL);
|
||||||
|
|
||||||
|
/* ... and do it */
|
||||||
DefineIndex(stmt->relation, /* relation */
|
DefineIndex(stmt->relation, /* relation */
|
||||||
stmt->idxname, /* index name */
|
stmt->idxname, /* index name */
|
||||||
InvalidOid, /* no predefined OID */
|
InvalidOid, /* no predefined OID */
|
||||||
|
@ -3703,7 +3710,6 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
|
||||||
stmt->tableSpace,
|
stmt->tableSpace,
|
||||||
stmt->indexParams, /* parameters */
|
stmt->indexParams, /* parameters */
|
||||||
(Expr *) stmt->whereClause,
|
(Expr *) stmt->whereClause,
|
||||||
stmt->rangetable,
|
|
||||||
stmt->options,
|
stmt->options,
|
||||||
stmt->unique,
|
stmt->unique,
|
||||||
stmt->primary,
|
stmt->primary,
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.43 2007/03/06 02:06:13 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.44 2007/03/13 00:33:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -198,11 +198,6 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||||
char *linkloc;
|
char *linkloc;
|
||||||
Oid ownerId;
|
Oid ownerId;
|
||||||
|
|
||||||
/* validate */
|
|
||||||
|
|
||||||
/* don't call this in a transaction block */
|
|
||||||
PreventTransactionChain((void *) stmt, "CREATE TABLESPACE");
|
|
||||||
|
|
||||||
/* Must be super user */
|
/* Must be super user */
|
||||||
if (!superuser())
|
if (!superuser())
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -385,9 +380,6 @@ DropTableSpace(DropTableSpaceStmt *stmt)
|
||||||
ScanKeyData entry[1];
|
ScanKeyData entry[1];
|
||||||
Oid tablespaceoid;
|
Oid tablespaceoid;
|
||||||
|
|
||||||
/* don't call this in a transaction block */
|
|
||||||
PreventTransactionChain((void *) stmt, "DROP TABLESPACE");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the target tuple
|
* Find the target tuple
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.347 2007/03/08 17:03:31 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.348 2007/03/13 00:33:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -257,13 +257,14 @@ static Size PageGetFreeSpaceWithFillFactor(Relation relation, Page page);
|
||||||
* relation OIDs to be processed, and vacstmt->relation is ignored.
|
* relation OIDs to be processed, and vacstmt->relation is ignored.
|
||||||
* (The non-NIL case is currently only used by autovacuum.)
|
* (The non-NIL case is currently only used by autovacuum.)
|
||||||
*
|
*
|
||||||
|
* isTopLevel should be passed down from ProcessUtility.
|
||||||
|
*
|
||||||
* It is the caller's responsibility that both vacstmt and relids
|
* It is the caller's responsibility that both vacstmt and relids
|
||||||
* (if given) be allocated in a memory context that won't disappear
|
* (if given) be allocated in a memory context that won't disappear
|
||||||
* at transaction commit. In fact this context must be QueryContext
|
* at transaction commit.
|
||||||
* to avoid complaints from PreventTransactionChain.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
vacuum(VacuumStmt *vacstmt, List *relids)
|
vacuum(VacuumStmt *vacstmt, List *relids, bool isTopLevel)
|
||||||
{
|
{
|
||||||
const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
|
const char *stmttype = vacstmt->vacuum ? "VACUUM" : "ANALYZE";
|
||||||
volatile MemoryContext anl_context = NULL;
|
volatile MemoryContext anl_context = NULL;
|
||||||
|
@ -293,11 +294,11 @@ vacuum(VacuumStmt *vacstmt, List *relids)
|
||||||
*/
|
*/
|
||||||
if (vacstmt->vacuum)
|
if (vacstmt->vacuum)
|
||||||
{
|
{
|
||||||
PreventTransactionChain((void *) vacstmt, stmttype);
|
PreventTransactionChain(isTopLevel, stmttype);
|
||||||
in_outer_xact = false;
|
in_outer_xact = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
in_outer_xact = IsInTransactionChain((void *) vacstmt);
|
in_outer_xact = IsInTransactionChain(isTopLevel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send info about dead objects to the statistics collector, unless we are
|
* Send info about dead objects to the statistics collector, unless we are
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.99 2007/01/05 22:19:27 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.100 2007/03/13 00:33:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
|
#include "parser/analyze.h"
|
||||||
#include "parser/parse_expr.h"
|
#include "parser/parse_expr.h"
|
||||||
#include "parser/parse_relation.h"
|
#include "parser/parse_relation.h"
|
||||||
#include "rewrite/rewriteDefine.h"
|
#include "rewrite/rewriteDefine.h"
|
||||||
|
@ -258,54 +259,23 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
static RuleStmt *
|
|
||||||
FormViewRetrieveRule(const RangeVar *view, Query *viewParse, bool replace)
|
|
||||||
{
|
|
||||||
RuleStmt *rule;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a RuleStmt that corresponds to the suitable rewrite rule args
|
|
||||||
* for DefineQueryRewrite();
|
|
||||||
*/
|
|
||||||
rule = makeNode(RuleStmt);
|
|
||||||
rule->relation = copyObject((RangeVar *) view);
|
|
||||||
rule->rulename = pstrdup(ViewSelectRuleName);
|
|
||||||
rule->whereClause = NULL;
|
|
||||||
rule->event = CMD_SELECT;
|
|
||||||
rule->instead = true;
|
|
||||||
rule->actions = list_make1(viewParse);
|
|
||||||
rule->replace = replace;
|
|
||||||
|
|
||||||
return rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
DefineViewRules(const RangeVar *view, Query *viewParse, bool replace)
|
DefineViewRules(const RangeVar *view, Query *viewParse, bool replace)
|
||||||
{
|
{
|
||||||
RuleStmt *retrieve_rule;
|
/*
|
||||||
|
* Set up the ON SELECT rule. Since the query has already been through
|
||||||
#ifdef NOTYET
|
* parse analysis, we use DefineQueryRewrite() directly.
|
||||||
RuleStmt *replace_rule;
|
*/
|
||||||
RuleStmt *append_rule;
|
DefineQueryRewrite(pstrdup(ViewSelectRuleName),
|
||||||
RuleStmt *delete_rule;
|
(RangeVar *) copyObject((RangeVar *) view),
|
||||||
#endif
|
NULL,
|
||||||
|
CMD_SELECT,
|
||||||
retrieve_rule = FormViewRetrieveRule(view, viewParse, replace);
|
true,
|
||||||
|
replace,
|
||||||
#ifdef NOTYET
|
list_make1(viewParse));
|
||||||
replace_rule = FormViewReplaceRule(view, viewParse);
|
/*
|
||||||
append_rule = FormViewAppendRule(view, viewParse);
|
* Someday: automatic ON INSERT, etc
|
||||||
delete_rule = FormViewDeleteRule(view, viewParse);
|
*/
|
||||||
#endif
|
|
||||||
|
|
||||||
DefineQueryRewrite(retrieve_rule);
|
|
||||||
|
|
||||||
#ifdef NOTYET
|
|
||||||
DefineQueryRewrite(replace_rule);
|
|
||||||
DefineQueryRewrite(append_rule);
|
|
||||||
DefineQueryRewrite(delete_rule);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------
|
/*---------------------------------------------------------------
|
||||||
|
@ -374,31 +344,77 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
|
||||||
return viewParse;
|
return viewParse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*
|
||||||
* DefineView
|
* DefineView
|
||||||
*
|
* Execute a CREATE VIEW command.
|
||||||
* - takes a "viewname", "parsetree" pair and then
|
|
||||||
* 1) construct the "virtual" relation
|
|
||||||
* 2) commit the command but NOT the transaction,
|
|
||||||
* so that the relation exists
|
|
||||||
* before the rules are defined.
|
|
||||||
* 2) define the "n" rules specified in the PRS2 paper
|
|
||||||
* over the "virtual" relation
|
|
||||||
*-------------------------------------------------------------------
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
DefineView(RangeVar *view, Query *viewParse, bool replace)
|
DefineView(ViewStmt *stmt, const char *queryString)
|
||||||
{
|
{
|
||||||
|
List *stmts;
|
||||||
|
Query *viewParse;
|
||||||
Oid viewOid;
|
Oid viewOid;
|
||||||
|
RangeVar *view;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run parse analysis to convert the raw parse tree to a Query. Note
|
||||||
|
* this also acquires sufficient locks on the source table(s).
|
||||||
|
*
|
||||||
|
* Since parse analysis scribbles on its input, copy the raw parse tree;
|
||||||
|
* this ensures we don't corrupt a prepared statement, for example.
|
||||||
|
*/
|
||||||
|
stmts = parse_analyze((Node *) copyObject(stmt->query),
|
||||||
|
queryString, NULL, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The grammar should ensure that the result is a single SELECT Query.
|
||||||
|
*/
|
||||||
|
if (list_length(stmts) != 1)
|
||||||
|
elog(ERROR, "unexpected parse analysis result");
|
||||||
|
viewParse = (Query *) linitial(stmts);
|
||||||
|
if (!IsA(viewParse, Query) ||
|
||||||
|
viewParse->commandType != CMD_SELECT)
|
||||||
|
elog(ERROR, "unexpected parse analysis result");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a list of column names was given, run through and insert these into
|
||||||
|
* the actual query tree. - thomas 2000-03-08
|
||||||
|
*/
|
||||||
|
if (stmt->aliases != NIL)
|
||||||
|
{
|
||||||
|
ListCell *alist_item = list_head(stmt->aliases);
|
||||||
|
ListCell *targetList;
|
||||||
|
|
||||||
|
foreach(targetList, viewParse->targetList)
|
||||||
|
{
|
||||||
|
TargetEntry *te = (TargetEntry *) lfirst(targetList);
|
||||||
|
|
||||||
|
Assert(IsA(te, TargetEntry));
|
||||||
|
/* junk columns don't get aliases */
|
||||||
|
if (te->resjunk)
|
||||||
|
continue;
|
||||||
|
te->resname = pstrdup(strVal(lfirst(alist_item)));
|
||||||
|
alist_item = lnext(alist_item);
|
||||||
|
if (alist_item == NULL)
|
||||||
|
break; /* done assigning aliases */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alist_item != NULL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("CREATE VIEW specifies more column "
|
||||||
|
"names than columns")));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the user didn't explicitly ask for a temporary view, check whether
|
* If the user didn't explicitly ask for a temporary view, check whether
|
||||||
* we need one implicitly.
|
* we need one implicitly.
|
||||||
*/
|
*/
|
||||||
if (!view->istemp)
|
view = stmt->view;
|
||||||
|
if (!view->istemp && isViewOnTempTable(viewParse))
|
||||||
{
|
{
|
||||||
view->istemp = isViewOnTempTable(viewParse);
|
view = copyObject(view); /* don't corrupt original command */
|
||||||
if (view->istemp)
|
view->istemp = true;
|
||||||
ereport(NOTICE,
|
ereport(NOTICE,
|
||||||
(errmsg("view \"%s\" will be a temporary view",
|
(errmsg("view \"%s\" will be a temporary view",
|
||||||
view->relname)));
|
view->relname)));
|
||||||
|
@ -410,7 +426,8 @@ DefineView(RangeVar *view, Query *viewParse, bool replace)
|
||||||
* NOTE: if it already exists and replace is false, the xact will be
|
* NOTE: if it already exists and replace is false, the xact will be
|
||||||
* aborted.
|
* aborted.
|
||||||
*/
|
*/
|
||||||
viewOid = DefineVirtualRelation(view, viewParse->targetList, replace);
|
viewOid = DefineVirtualRelation(view, viewParse->targetList,
|
||||||
|
stmt->replace);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The relation we have just created is not visible to any other commands
|
* The relation we have just created is not visible to any other commands
|
||||||
|
@ -428,7 +445,7 @@ DefineView(RangeVar *view, Query *viewParse, bool replace)
|
||||||
/*
|
/*
|
||||||
* Now create the rules associated with the view.
|
* Now create the rules associated with the view.
|
||||||
*/
|
*/
|
||||||
DefineViewRules(view, viewParse, replace);
|
DefineViewRules(view, viewParse, stmt->replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.111 2007/02/20 17:32:15 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.112 2007/03/13 00:33:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -58,6 +58,8 @@ typedef struct local_es
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
char *src; /* function body text (for error msgs) */
|
||||||
|
|
||||||
Oid *argtypes; /* resolved types of arguments */
|
Oid *argtypes; /* resolved types of arguments */
|
||||||
Oid rettype; /* actual return type */
|
Oid rettype; /* actual return type */
|
||||||
int16 typlen; /* length of the return type */
|
int16 typlen; /* length of the return type */
|
||||||
|
@ -82,7 +84,8 @@ static execution_state *init_execution_state(List *queryTree_list,
|
||||||
bool readonly_func);
|
bool readonly_func);
|
||||||
static void init_sql_fcache(FmgrInfo *finfo);
|
static void init_sql_fcache(FmgrInfo *finfo);
|
||||||
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
|
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
|
||||||
static TupleTableSlot *postquel_getnext(execution_state *es);
|
static TupleTableSlot *postquel_getnext(execution_state *es,
|
||||||
|
SQLFunctionCachePtr fcache);
|
||||||
static void postquel_end(execution_state *es);
|
static void postquel_end(execution_state *es);
|
||||||
static void postquel_sub_params(SQLFunctionCachePtr fcache,
|
static void postquel_sub_params(SQLFunctionCachePtr fcache,
|
||||||
FunctionCallInfo fcinfo);
|
FunctionCallInfo fcinfo);
|
||||||
|
@ -156,7 +159,6 @@ init_sql_fcache(FmgrInfo *finfo)
|
||||||
Form_pg_proc procedureStruct;
|
Form_pg_proc procedureStruct;
|
||||||
SQLFunctionCachePtr fcache;
|
SQLFunctionCachePtr fcache;
|
||||||
Oid *argOidVect;
|
Oid *argOidVect;
|
||||||
char *src;
|
|
||||||
int nargs;
|
int nargs;
|
||||||
List *queryTree_list;
|
List *queryTree_list;
|
||||||
Datum tmp;
|
Datum tmp;
|
||||||
|
@ -233,7 +235,7 @@ init_sql_fcache(FmgrInfo *finfo)
|
||||||
fcache->argtypes = argOidVect;
|
fcache->argtypes = argOidVect;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse and rewrite the queries in the function text.
|
* And of course we need the function body text.
|
||||||
*/
|
*/
|
||||||
tmp = SysCacheGetAttr(PROCOID,
|
tmp = SysCacheGetAttr(PROCOID,
|
||||||
procedureTuple,
|
procedureTuple,
|
||||||
|
@ -241,9 +243,12 @@ init_sql_fcache(FmgrInfo *finfo)
|
||||||
&isNull);
|
&isNull);
|
||||||
if (isNull)
|
if (isNull)
|
||||||
elog(ERROR, "null prosrc for function %u", foid);
|
elog(ERROR, "null prosrc for function %u", foid);
|
||||||
src = DatumGetCString(DirectFunctionCall1(textout, tmp));
|
fcache->src = DatumGetCString(DirectFunctionCall1(textout, tmp));
|
||||||
|
|
||||||
queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
|
/*
|
||||||
|
* Parse and rewrite the queries in the function text.
|
||||||
|
*/
|
||||||
|
queryTree_list = pg_parse_and_rewrite(fcache->src, argOidVect, nargs);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that the function returns the type it claims to. Although
|
* Check that the function returns the type it claims to. Although
|
||||||
|
@ -270,8 +275,6 @@ init_sql_fcache(FmgrInfo *finfo)
|
||||||
fcache->func_state = init_execution_state(queryTree_list,
|
fcache->func_state = init_execution_state(queryTree_list,
|
||||||
fcache->readonly_func);
|
fcache->readonly_func);
|
||||||
|
|
||||||
pfree(src);
|
|
||||||
|
|
||||||
ReleaseSysCache(procedureTuple);
|
ReleaseSysCache(procedureTuple);
|
||||||
|
|
||||||
finfo->fn_extra = (void *) fcache;
|
finfo->fn_extra = (void *) fcache;
|
||||||
|
@ -331,7 +334,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
|
||||||
}
|
}
|
||||||
|
|
||||||
static TupleTableSlot *
|
static TupleTableSlot *
|
||||||
postquel_getnext(execution_state *es)
|
postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
|
||||||
{
|
{
|
||||||
TupleTableSlot *result;
|
TupleTableSlot *result;
|
||||||
Snapshot saveActiveSnapshot;
|
Snapshot saveActiveSnapshot;
|
||||||
|
@ -345,8 +348,12 @@ postquel_getnext(execution_state *es)
|
||||||
|
|
||||||
if (es->qd->operation == CMD_UTILITY)
|
if (es->qd->operation == CMD_UTILITY)
|
||||||
{
|
{
|
||||||
ProcessUtility(es->qd->utilitystmt, es->qd->params,
|
ProcessUtility(es->qd->utilitystmt,
|
||||||
es->qd->dest, NULL);
|
fcache->src,
|
||||||
|
es->qd->params,
|
||||||
|
false, /* not top level */
|
||||||
|
es->qd->dest,
|
||||||
|
NULL);
|
||||||
result = NULL;
|
result = NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -465,7 +472,7 @@ postquel_execute(execution_state *es,
|
||||||
if (es->status == F_EXEC_START)
|
if (es->status == F_EXEC_START)
|
||||||
postquel_start(es, fcache);
|
postquel_start(es, fcache);
|
||||||
|
|
||||||
slot = postquel_getnext(es);
|
slot = postquel_getnext(es, fcache);
|
||||||
|
|
||||||
if (TupIsNull(slot))
|
if (TupIsNull(slot))
|
||||||
{
|
{
|
||||||
|
@ -754,21 +761,11 @@ sql_exec_error_callback(void *arg)
|
||||||
* If there is a syntax error position, convert to internal syntax error
|
* If there is a syntax error position, convert to internal syntax error
|
||||||
*/
|
*/
|
||||||
syntaxerrposition = geterrposition();
|
syntaxerrposition = geterrposition();
|
||||||
if (syntaxerrposition > 0)
|
if (syntaxerrposition > 0 && fcache->src)
|
||||||
{
|
{
|
||||||
bool isnull;
|
|
||||||
Datum tmp;
|
|
||||||
char *prosrc;
|
|
||||||
|
|
||||||
tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc,
|
|
||||||
&isnull);
|
|
||||||
if (isnull)
|
|
||||||
elog(ERROR, "null prosrc");
|
|
||||||
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
|
|
||||||
errposition(0);
|
errposition(0);
|
||||||
internalerrposition(syntaxerrposition);
|
internalerrposition(syntaxerrposition);
|
||||||
internalerrquery(prosrc);
|
internalerrquery(fcache->src);
|
||||||
pfree(prosrc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.170 2007/02/20 17:32:15 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.171 2007/03/13 00:33:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -927,7 +927,7 @@ SPI_cursor_open(const char *name, void *plan,
|
||||||
spiplan->query,
|
spiplan->query,
|
||||||
CreateCommandTag(PortalListGetPrimaryStmt(stmt_list)),
|
CreateCommandTag(PortalListGetPrimaryStmt(stmt_list)),
|
||||||
stmt_list,
|
stmt_list,
|
||||||
PortalGetHeapMemory(portal));
|
NULL);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
|
@ -1471,7 +1471,12 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ProcessUtility(stmt, paramLI, dest, NULL);
|
ProcessUtility(stmt,
|
||||||
|
NULL, /* XXX provide query string? */
|
||||||
|
paramLI,
|
||||||
|
false, /* not top level */
|
||||||
|
dest,
|
||||||
|
NULL);
|
||||||
/* Update "processed" if stmt returned tuples */
|
/* Update "processed" if stmt returned tuples */
|
||||||
if (_SPI_current->tuptable)
|
if (_SPI_current->tuptable)
|
||||||
_SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
|
_SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.369 2007/02/27 01:11:25 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.370 2007/03/13 00:33:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -2142,7 +2142,6 @@ _copyIndexStmt(IndexStmt *from)
|
||||||
COPY_NODE_FIELD(indexParams);
|
COPY_NODE_FIELD(indexParams);
|
||||||
COPY_NODE_FIELD(options);
|
COPY_NODE_FIELD(options);
|
||||||
COPY_NODE_FIELD(whereClause);
|
COPY_NODE_FIELD(whereClause);
|
||||||
COPY_NODE_FIELD(rangetable);
|
|
||||||
COPY_SCALAR_FIELD(unique);
|
COPY_SCALAR_FIELD(unique);
|
||||||
COPY_SCALAR_FIELD(primary);
|
COPY_SCALAR_FIELD(primary);
|
||||||
COPY_SCALAR_FIELD(isconstraint);
|
COPY_SCALAR_FIELD(isconstraint);
|
||||||
|
@ -2785,7 +2784,6 @@ _copyPrepareStmt(PrepareStmt *from)
|
||||||
|
|
||||||
COPY_STRING_FIELD(name);
|
COPY_STRING_FIELD(name);
|
||||||
COPY_NODE_FIELD(argtypes);
|
COPY_NODE_FIELD(argtypes);
|
||||||
COPY_NODE_FIELD(argtype_oids);
|
|
||||||
COPY_NODE_FIELD(query);
|
COPY_NODE_FIELD(query);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.300 2007/02/22 22:00:23 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.301 2007/03/13 00:33:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -994,7 +994,6 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
|
||||||
COMPARE_NODE_FIELD(indexParams);
|
COMPARE_NODE_FIELD(indexParams);
|
||||||
COMPARE_NODE_FIELD(options);
|
COMPARE_NODE_FIELD(options);
|
||||||
COMPARE_NODE_FIELD(whereClause);
|
COMPARE_NODE_FIELD(whereClause);
|
||||||
COMPARE_NODE_FIELD(rangetable);
|
|
||||||
COMPARE_SCALAR_FIELD(unique);
|
COMPARE_SCALAR_FIELD(unique);
|
||||||
COMPARE_SCALAR_FIELD(primary);
|
COMPARE_SCALAR_FIELD(primary);
|
||||||
COMPARE_SCALAR_FIELD(isconstraint);
|
COMPARE_SCALAR_FIELD(isconstraint);
|
||||||
|
@ -1536,7 +1535,6 @@ _equalPrepareStmt(PrepareStmt *a, PrepareStmt *b)
|
||||||
{
|
{
|
||||||
COMPARE_STRING_FIELD(name);
|
COMPARE_STRING_FIELD(name);
|
||||||
COMPARE_NODE_FIELD(argtypes);
|
COMPARE_NODE_FIELD(argtypes);
|
||||||
COMPARE_NODE_FIELD(argtype_oids);
|
|
||||||
COMPARE_NODE_FIELD(query);
|
COMPARE_NODE_FIELD(query);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.302 2007/02/27 01:11:25 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.303 2007/03/13 00:33:40 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
|
@ -1505,7 +1505,6 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
|
||||||
WRITE_NODE_FIELD(indexParams);
|
WRITE_NODE_FIELD(indexParams);
|
||||||
WRITE_NODE_FIELD(options);
|
WRITE_NODE_FIELD(options);
|
||||||
WRITE_NODE_FIELD(whereClause);
|
WRITE_NODE_FIELD(whereClause);
|
||||||
WRITE_NODE_FIELD(rangetable);
|
|
||||||
WRITE_BOOL_FIELD(unique);
|
WRITE_BOOL_FIELD(unique);
|
||||||
WRITE_BOOL_FIELD(primary);
|
WRITE_BOOL_FIELD(primary);
|
||||||
WRITE_BOOL_FIELD(isconstraint);
|
WRITE_BOOL_FIELD(isconstraint);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.8 2007/01/05 22:19:30 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.9 2007/03/13 00:33:41 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -60,3 +60,34 @@ copyParamList(ParamListInfo from)
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract an array of parameter type OIDs from a ParamListInfo.
|
||||||
|
*
|
||||||
|
* The result is allocated in CurrentMemoryContext.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
getParamListTypes(ParamListInfo params,
|
||||||
|
Oid **param_types, int *num_params)
|
||||||
|
{
|
||||||
|
Oid *ptypes;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (params == NULL || params->numParams <= 0)
|
||||||
|
{
|
||||||
|
*param_types = NULL;
|
||||||
|
*num_params = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
|
||||||
|
*param_types = ptypes;
|
||||||
|
*num_params = params->numParams;
|
||||||
|
|
||||||
|
for (i = 0; i < params->numParams; i++)
|
||||||
|
{
|
||||||
|
ParamExternData *prm = ¶ms->params[i];
|
||||||
|
|
||||||
|
ptypes[i] = prm->ptype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.237 2007/03/06 22:45:16 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.238 2007/03/13 00:33:41 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
|
@ -3603,38 +3603,6 @@ query_tree_walker(Query *query,
|
||||||
return true;
|
return true;
|
||||||
if (range_table_walker(query->rtable, walker, context, flags))
|
if (range_table_walker(query->rtable, walker, context, flags))
|
||||||
return true;
|
return true;
|
||||||
if (query->utilityStmt)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Certain utility commands contain general-purpose Querys embedded in
|
|
||||||
* them --- if this is one, invoke the walker on the sub-Query.
|
|
||||||
*/
|
|
||||||
if (IsA(query->utilityStmt, CopyStmt))
|
|
||||||
{
|
|
||||||
if (walker(((CopyStmt *) query->utilityStmt)->query, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IsA(query->utilityStmt, DeclareCursorStmt))
|
|
||||||
{
|
|
||||||
if (walker(((DeclareCursorStmt *) query->utilityStmt)->query, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IsA(query->utilityStmt, ExplainStmt))
|
|
||||||
{
|
|
||||||
if (walker(((ExplainStmt *) query->utilityStmt)->query, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IsA(query->utilityStmt, PrepareStmt))
|
|
||||||
{
|
|
||||||
if (walker(((PrepareStmt *) query->utilityStmt)->query, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IsA(query->utilityStmt, ViewStmt))
|
|
||||||
{
|
|
||||||
if (walker(((ViewStmt *) query->utilityStmt)->query, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,26 @@
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* analyze.c
|
* analyze.c
|
||||||
* transform the parse tree into a query tree
|
* transform the raw parse tree into a query tree
|
||||||
|
*
|
||||||
|
* For optimizable statements, we are careful to obtain a suitable lock on
|
||||||
|
* each referenced table, and other modules of the backend preserve or
|
||||||
|
* re-obtain these locks before depending on the results. It is therefore
|
||||||
|
* okay to do significant semantic analysis of these statements. For
|
||||||
|
* utility commands, no locks are obtained here (and if they were, we could
|
||||||
|
* not be sure we'd still have them at execution). Hence the general rule
|
||||||
|
* for utility commands is to just dump them into a Query node untransformed.
|
||||||
|
* parse_analyze does do some purely syntactic transformations on CREATE TABLE
|
||||||
|
* and ALTER TABLE, but that's about it. In cases where this module contains
|
||||||
|
* mechanisms that are useful for utility statements, we provide separate
|
||||||
|
* subroutines that should be called at the beginning of utility execution;
|
||||||
|
* an example is analyzeIndexStmt.
|
||||||
|
*
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.361 2007/02/20 17:32:16 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.362 2007/03/13 00:33:41 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -93,26 +107,17 @@ typedef struct
|
||||||
static List *do_parse_analyze(Node *parseTree, ParseState *pstate);
|
static List *do_parse_analyze(Node *parseTree, ParseState *pstate);
|
||||||
static Query *transformStmt(ParseState *pstate, Node *stmt,
|
static Query *transformStmt(ParseState *pstate, Node *stmt,
|
||||||
List **extras_before, List **extras_after);
|
List **extras_before, List **extras_after);
|
||||||
static Query *transformViewStmt(ParseState *pstate, ViewStmt *stmt,
|
|
||||||
List **extras_before, List **extras_after);
|
|
||||||
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
||||||
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||||
List **extras_before, List **extras_after);
|
List **extras_before, List **extras_after);
|
||||||
static List *transformInsertRow(ParseState *pstate, List *exprlist,
|
static List *transformInsertRow(ParseState *pstate, List *exprlist,
|
||||||
List *stmtcols, List *icolumns, List *attrnos);
|
List *stmtcols, List *icolumns, List *attrnos);
|
||||||
static List *transformReturningList(ParseState *pstate, List *returningList);
|
static List *transformReturningList(ParseState *pstate, List *returningList);
|
||||||
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
|
|
||||||
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
|
|
||||||
List **extras_before, List **extras_after);
|
|
||||||
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
|
static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
|
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
||||||
static Query *transformDeclareCursorStmt(ParseState *pstate,
|
|
||||||
DeclareCursorStmt *stmt);
|
|
||||||
static Query *transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt);
|
|
||||||
static Query *transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt);
|
|
||||||
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
|
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
|
||||||
List **extras_before, List **extras_after);
|
List **extras_before, List **extras_after);
|
||||||
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||||
|
@ -155,7 +160,7 @@ static bool check_parameter_resolution_walker(Node *node,
|
||||||
*
|
*
|
||||||
* The result is a List of Query nodes (we need a list since some commands
|
* The result is a List of Query nodes (we need a list since some commands
|
||||||
* produce multiple Queries). Optimizable statements require considerable
|
* produce multiple Queries). Optimizable statements require considerable
|
||||||
* transformation, while many utility-type statements are simply hung off
|
* transformation, while most utility-type statements are simply hung off
|
||||||
* a dummy CMD_UTILITY Query node.
|
* a dummy CMD_UTILITY Query node.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
|
@ -315,59 +320,12 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
||||||
extras_before, extras_after);
|
extras_before, extras_after);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_IndexStmt:
|
|
||||||
result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_RuleStmt:
|
|
||||||
result = transformRuleStmt(pstate, (RuleStmt *) parseTree,
|
|
||||||
extras_before, extras_after);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_ViewStmt:
|
|
||||||
result = transformViewStmt(pstate, (ViewStmt *) parseTree,
|
|
||||||
extras_before, extras_after);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_ExplainStmt:
|
|
||||||
{
|
|
||||||
ExplainStmt *n = (ExplainStmt *) parseTree;
|
|
||||||
|
|
||||||
result = makeNode(Query);
|
|
||||||
result->commandType = CMD_UTILITY;
|
|
||||||
n->query = transformStmt(pstate, (Node *) n->query,
|
|
||||||
extras_before, extras_after);
|
|
||||||
result->utilityStmt = (Node *) parseTree;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_CopyStmt:
|
|
||||||
{
|
|
||||||
CopyStmt *n = (CopyStmt *) parseTree;
|
|
||||||
|
|
||||||
result = makeNode(Query);
|
|
||||||
result->commandType = CMD_UTILITY;
|
|
||||||
if (n->query)
|
|
||||||
n->query = transformStmt(pstate, (Node *) n->query,
|
|
||||||
extras_before, extras_after);
|
|
||||||
result->utilityStmt = (Node *) parseTree;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_AlterTableStmt:
|
case T_AlterTableStmt:
|
||||||
result = transformAlterTableStmt(pstate,
|
result = transformAlterTableStmt(pstate,
|
||||||
(AlterTableStmt *) parseTree,
|
(AlterTableStmt *) parseTree,
|
||||||
extras_before, extras_after);
|
extras_before, extras_after);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_PrepareStmt:
|
|
||||||
result = transformPrepareStmt(pstate, (PrepareStmt *) parseTree);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_ExecuteStmt:
|
|
||||||
result = transformExecuteStmt(pstate, (ExecuteStmt *) parseTree);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Optimizable statements
|
* Optimizable statements
|
||||||
*/
|
*/
|
||||||
|
@ -397,16 +355,11 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_DeclareCursorStmt:
|
|
||||||
result = transformDeclareCursorStmt(pstate,
|
|
||||||
(DeclareCursorStmt *) parseTree);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* other statements don't require any transformation-- just return
|
* other statements don't require any transformation; just return
|
||||||
* the original parsetree, yea!
|
* the original parsetree with a Query node plastered on top.
|
||||||
*/
|
*/
|
||||||
result = makeNode(Query);
|
result = makeNode(Query);
|
||||||
result->commandType = CMD_UTILITY;
|
result->commandType = CMD_UTILITY;
|
||||||
|
@ -432,54 +385,6 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Query *
|
|
||||||
transformViewStmt(ParseState *pstate, ViewStmt *stmt,
|
|
||||||
List **extras_before, List **extras_after)
|
|
||||||
{
|
|
||||||
Query *result = makeNode(Query);
|
|
||||||
|
|
||||||
result->commandType = CMD_UTILITY;
|
|
||||||
result->utilityStmt = (Node *) stmt;
|
|
||||||
|
|
||||||
stmt->query = transformStmt(pstate, (Node *) stmt->query,
|
|
||||||
extras_before, extras_after);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If a list of column names was given, run through and insert these into
|
|
||||||
* the actual query tree. - thomas 2000-03-08
|
|
||||||
*
|
|
||||||
* Outer loop is over targetlist to make it easier to skip junk targetlist
|
|
||||||
* entries.
|
|
||||||
*/
|
|
||||||
if (stmt->aliases != NIL)
|
|
||||||
{
|
|
||||||
ListCell *alist_item = list_head(stmt->aliases);
|
|
||||||
ListCell *targetList;
|
|
||||||
|
|
||||||
foreach(targetList, stmt->query->targetList)
|
|
||||||
{
|
|
||||||
TargetEntry *te = (TargetEntry *) lfirst(targetList);
|
|
||||||
|
|
||||||
Assert(IsA(te, TargetEntry));
|
|
||||||
/* junk columns don't get aliases */
|
|
||||||
if (te->resjunk)
|
|
||||||
continue;
|
|
||||||
te->resname = pstrdup(strVal(lfirst(alist_item)));
|
|
||||||
alist_item = lnext(alist_item);
|
|
||||||
if (alist_item == NULL)
|
|
||||||
break; /* done assigning aliases */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alist_item != NULL)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("CREATE VIEW specifies more column "
|
|
||||||
"names than columns")));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformDeleteStmt -
|
* transformDeleteStmt -
|
||||||
* transforms a Delete Statement
|
* transforms a Delete Statement
|
||||||
|
@ -1278,8 +1183,13 @@ transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
|
||||||
/*
|
/*
|
||||||
* transformInhRelation
|
* transformInhRelation
|
||||||
*
|
*
|
||||||
* Change the LIKE <subtable> portion of a CREATE TABLE statement into the
|
* Change the LIKE <subtable> portion of a CREATE TABLE statement into
|
||||||
* column definitions which recreate the user defined column portions of <subtable>.
|
* column definitions which recreate the user defined column portions of
|
||||||
|
* <subtable>.
|
||||||
|
*
|
||||||
|
* Note: because we do this at parse analysis time, any change in the
|
||||||
|
* referenced table between parse analysis and execution won't be reflected
|
||||||
|
* into the new table. Is this OK?
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
|
transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
|
||||||
|
@ -1644,7 +1554,9 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
||||||
* that strikes me as too anal-retentive. - tgl 2001-02-14
|
* that strikes me as too anal-retentive. - tgl 2001-02-14
|
||||||
*
|
*
|
||||||
* XXX in ALTER TABLE case, it'd be nice to look for duplicate
|
* XXX in ALTER TABLE case, it'd be nice to look for duplicate
|
||||||
* pre-existing indexes, too.
|
* pre-existing indexes, too. However, that seems to risk race
|
||||||
|
* conditions since we can't be sure the command will be executed
|
||||||
|
* immediately.
|
||||||
*/
|
*/
|
||||||
Assert(cxt->alist == NIL);
|
Assert(cxt->alist == NIL);
|
||||||
if (cxt->pkey != NULL)
|
if (cxt->pkey != NULL)
|
||||||
|
@ -1746,37 +1658,55 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformIndexStmt -
|
* analyzeIndexStmt - perform parse analysis for CREATE INDEX
|
||||||
* transforms the qualification of the index statement
|
*
|
||||||
|
* Note that this has to be performed during execution not parse analysis, so
|
||||||
|
* it's called by ProcessUtility. (Most other callers don't need to bother,
|
||||||
|
* because this is a no-op for an index not using either index expressions or
|
||||||
|
* a predicate expression.)
|
||||||
*/
|
*/
|
||||||
static Query *
|
IndexStmt *
|
||||||
transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
analyzeIndexStmt(IndexStmt *stmt, const char *queryString)
|
||||||
{
|
{
|
||||||
Query *qry;
|
Relation rel;
|
||||||
RangeTblEntry *rte = NULL;
|
ParseState *pstate;
|
||||||
|
RangeTblEntry *rte;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
|
||||||
qry = makeNode(Query);
|
|
||||||
qry->commandType = CMD_UTILITY;
|
|
||||||
|
|
||||||
/* take care of the where clause */
|
|
||||||
if (stmt->whereClause)
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
* Put the parent table into the rtable so that the WHERE clause can
|
* We must not scribble on the passed-in IndexStmt, so copy it. (This
|
||||||
* refer to its fields without qualification. Note that this only
|
* is overkill, but easy.)
|
||||||
* works if the parent table already exists --- so we can't easily
|
*/
|
||||||
* support predicates on indexes created implicitly by CREATE TABLE.
|
stmt = (IndexStmt *) copyObject(stmt);
|
||||||
* Fortunately, that's not necessary.
|
|
||||||
|
/*
|
||||||
|
* Open the parent table with appropriate locking. We must do this
|
||||||
|
* because addRangeTableEntry() would acquire only AccessShareLock,
|
||||||
|
* leaving DefineIndex() needing to do a lock upgrade with consequent
|
||||||
|
* risk of deadlock. Make sure this stays in sync with the type of
|
||||||
|
* lock DefineIndex() wants.
|
||||||
|
*/
|
||||||
|
rel = heap_openrv(stmt->relation,
|
||||||
|
(stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
|
||||||
|
|
||||||
|
/* Set up pstate */
|
||||||
|
pstate = make_parsestate(NULL);
|
||||||
|
pstate->p_sourcetext = queryString;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put the parent table into the rtable so that the expressions can
|
||||||
|
* refer to its fields without qualification.
|
||||||
*/
|
*/
|
||||||
rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
|
rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
|
||||||
|
|
||||||
/* no to join list, yes to namespaces */
|
/* no to join list, yes to namespaces */
|
||||||
addRTEtoQuery(pstate, rte, false, true, true);
|
addRTEtoQuery(pstate, rte, false, true, true);
|
||||||
|
|
||||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,
|
/* take care of the where clause */
|
||||||
|
if (stmt->whereClause)
|
||||||
|
stmt->whereClause = transformWhereClause(pstate,
|
||||||
|
stmt->whereClause,
|
||||||
"WHERE");
|
"WHERE");
|
||||||
}
|
|
||||||
|
|
||||||
/* take care of any index expressions */
|
/* take care of any index expressions */
|
||||||
foreach(l, stmt->indexParams)
|
foreach(l, stmt->indexParams)
|
||||||
|
@ -1785,14 +1715,6 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
||||||
|
|
||||||
if (ielem->expr)
|
if (ielem->expr)
|
||||||
{
|
{
|
||||||
/* Set up rtable as for predicate, see notes above */
|
|
||||||
if (rte == NULL)
|
|
||||||
{
|
|
||||||
rte = addRangeTableEntry(pstate, stmt->relation, NULL,
|
|
||||||
false, true);
|
|
||||||
/* no to join list, yes to namespaces */
|
|
||||||
addRTEtoQuery(pstate, rte, false, true, true);
|
|
||||||
}
|
|
||||||
ielem->expr = transformExpr(pstate, ielem->expr);
|
ielem->expr = transformExpr(pstate, ielem->expr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1807,32 +1729,44 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
/*
|
||||||
stmt->rangetable = pstate->p_rtable;
|
* Check that only the base rel is mentioned.
|
||||||
|
*/
|
||||||
|
if (list_length(pstate->p_rtable) != 1)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
||||||
|
errmsg("index expressions and predicates can refer only to the table being indexed")));
|
||||||
|
|
||||||
qry->utilityStmt = (Node *) stmt;
|
release_pstate_resources(pstate);
|
||||||
|
pfree(pstate);
|
||||||
|
|
||||||
return qry;
|
/* Close relation, but keep the lock */
|
||||||
|
heap_close(rel, NoLock);
|
||||||
|
|
||||||
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformRuleStmt -
|
* analyzeRuleStmt -
|
||||||
* transform a Create Rule Statement. The actions is a list of parse
|
* transform a Create Rule Statement. The action is a list of parse
|
||||||
* trees which is transformed into a list of query trees.
|
* trees which is transformed into a list of query trees, and we also
|
||||||
|
* transform the WHERE clause if any.
|
||||||
|
*
|
||||||
|
* Note that this has to be performed during execution not parse analysis,
|
||||||
|
* so it's called by DefineRule. Also note that we must not scribble on
|
||||||
|
* the passed-in RuleStmt, so we do copyObject() on the actions and WHERE
|
||||||
|
* clause.
|
||||||
*/
|
*/
|
||||||
static Query *
|
void
|
||||||
transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
analyzeRuleStmt(RuleStmt *stmt, const char *queryString,
|
||||||
List **extras_before, List **extras_after)
|
List **actions, Node **whereClause)
|
||||||
{
|
{
|
||||||
Query *qry;
|
|
||||||
Relation rel;
|
Relation rel;
|
||||||
|
ParseState *pstate;
|
||||||
RangeTblEntry *oldrte;
|
RangeTblEntry *oldrte;
|
||||||
RangeTblEntry *newrte;
|
RangeTblEntry *newrte;
|
||||||
|
|
||||||
qry = makeNode(Query);
|
|
||||||
qry->commandType = CMD_UTILITY;
|
|
||||||
qry->utilityStmt = (Node *) stmt;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To avoid deadlock, make sure the first thing we do is grab
|
* To avoid deadlock, make sure the first thing we do is grab
|
||||||
* AccessExclusiveLock on the target relation. This will be needed by
|
* AccessExclusiveLock on the target relation. This will be needed by
|
||||||
|
@ -1841,12 +1775,15 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
*/
|
*/
|
||||||
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
|
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
|
||||||
|
|
||||||
|
/* Set up pstate */
|
||||||
|
pstate = make_parsestate(NULL);
|
||||||
|
pstate->p_sourcetext = queryString;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
|
* NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.
|
||||||
* Set up their RTEs in the main pstate for use in parsing the rule
|
* Set up their RTEs in the main pstate for use in parsing the rule
|
||||||
* qualification.
|
* qualification.
|
||||||
*/
|
*/
|
||||||
Assert(pstate->p_rtable == NIL);
|
|
||||||
oldrte = addRangeTableEntryForRelation(pstate, rel,
|
oldrte = addRangeTableEntryForRelation(pstate, rel,
|
||||||
makeAlias("*OLD*", NIL),
|
makeAlias("*OLD*", NIL),
|
||||||
false, false);
|
false, false);
|
||||||
|
@ -1886,7 +1823,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* take care of the where clause */
|
/* take care of the where clause */
|
||||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,
|
*whereClause = transformWhereClause(pstate,
|
||||||
|
(Node *) copyObject(stmt->whereClause),
|
||||||
"WHERE");
|
"WHERE");
|
||||||
|
|
||||||
if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
|
if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
|
||||||
|
@ -1900,9 +1838,6 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
(errcode(ERRCODE_GROUPING_ERROR),
|
(errcode(ERRCODE_GROUPING_ERROR),
|
||||||
errmsg("cannot use aggregate function in rule WHERE condition")));
|
errmsg("cannot use aggregate function in rule WHERE condition")));
|
||||||
|
|
||||||
/* save info about sublinks in where clause */
|
|
||||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 'instead nothing' rules with a qualification need a query rangetable so
|
* 'instead nothing' rules with a qualification need a query rangetable so
|
||||||
* the rewrite handler can add the negated rule qualification to the
|
* the rewrite handler can add the negated rule qualification to the
|
||||||
|
@ -1917,7 +1852,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
nothing_qry->rtable = pstate->p_rtable;
|
nothing_qry->rtable = pstate->p_rtable;
|
||||||
nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
|
nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
|
||||||
|
|
||||||
stmt->actions = list_make1(nothing_qry);
|
*actions = list_make1(nothing_qry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1930,12 +1865,20 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
foreach(l, stmt->actions)
|
foreach(l, stmt->actions)
|
||||||
{
|
{
|
||||||
Node *action = (Node *) lfirst(l);
|
Node *action = (Node *) lfirst(l);
|
||||||
ParseState *sub_pstate = make_parsestate(pstate->parentParseState);
|
ParseState *sub_pstate = make_parsestate(NULL);
|
||||||
Query *sub_qry,
|
Query *sub_qry,
|
||||||
*top_subqry;
|
*top_subqry;
|
||||||
|
List *extras_before = NIL;
|
||||||
|
List *extras_after = NIL;
|
||||||
bool has_old,
|
bool has_old,
|
||||||
has_new;
|
has_new;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since outer ParseState isn't parent of inner, have to pass
|
||||||
|
* down the query text by hand.
|
||||||
|
*/
|
||||||
|
sub_pstate->p_sourcetext = queryString;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up OLD/NEW in the rtable for this statement. The entries
|
* Set up OLD/NEW in the rtable for this statement. The entries
|
||||||
* are added only to relnamespace, not varnamespace, because we
|
* are added only to relnamespace, not varnamespace, because we
|
||||||
|
@ -1955,8 +1898,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
addRTEtoQuery(sub_pstate, newrte, false, true, false);
|
addRTEtoQuery(sub_pstate, newrte, false, true, false);
|
||||||
|
|
||||||
/* Transform the rule action statement */
|
/* Transform the rule action statement */
|
||||||
top_subqry = transformStmt(sub_pstate, action,
|
top_subqry = transformStmt(sub_pstate,
|
||||||
extras_before, extras_after);
|
(Node *) copyObject(action),
|
||||||
|
&extras_before, &extras_after);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We cannot support utility-statement actions (eg NOTIFY) with
|
* We cannot support utility-statement actions (eg NOTIFY) with
|
||||||
|
@ -1964,7 +1908,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
* the utility action execute conditionally.
|
* the utility action execute conditionally.
|
||||||
*/
|
*/
|
||||||
if (top_subqry->commandType == CMD_UTILITY &&
|
if (top_subqry->commandType == CMD_UTILITY &&
|
||||||
stmt->whereClause != NULL)
|
*whereClause != NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
|
errmsg("rules with WHERE conditions can only have SELECT, INSERT, UPDATE, or DELETE actions")));
|
||||||
|
@ -1982,7 +1926,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
* perhaps be relaxed someday, but for now, we may as well reject
|
* perhaps be relaxed someday, but for now, we may as well reject
|
||||||
* such a rule immediately.
|
* such a rule immediately.
|
||||||
*/
|
*/
|
||||||
if (sub_qry->setOperations != NULL && stmt->whereClause != NULL)
|
if (sub_qry->setOperations != NULL && *whereClause != NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
|
errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
|
||||||
|
@ -1992,10 +1936,10 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
*/
|
*/
|
||||||
has_old =
|
has_old =
|
||||||
rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
|
rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
|
||||||
rangeTableEntry_used(stmt->whereClause, PRS2_OLD_VARNO, 0);
|
rangeTableEntry_used(*whereClause, PRS2_OLD_VARNO, 0);
|
||||||
has_new =
|
has_new =
|
||||||
rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
|
rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
|
||||||
rangeTableEntry_used(stmt->whereClause, PRS2_NEW_VARNO, 0);
|
rangeTableEntry_used(*whereClause, PRS2_NEW_VARNO, 0);
|
||||||
|
|
||||||
switch (stmt->event)
|
switch (stmt->event)
|
||||||
{
|
{
|
||||||
|
@ -2063,27 +2007,28 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
|
||||||
sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
|
sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newactions = list_concat(newactions, extras_before);
|
||||||
newactions = lappend(newactions, top_subqry);
|
newactions = lappend(newactions, top_subqry);
|
||||||
|
newactions = list_concat(newactions, extras_after);
|
||||||
|
|
||||||
release_pstate_resources(sub_pstate);
|
release_pstate_resources(sub_pstate);
|
||||||
pfree(sub_pstate);
|
pfree(sub_pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt->actions = newactions;
|
*actions = newactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
release_pstate_resources(pstate);
|
||||||
|
pfree(pstate);
|
||||||
|
|
||||||
/* Close relation, but keep the exclusive lock */
|
/* Close relation, but keep the exclusive lock */
|
||||||
heap_close(rel, NoLock);
|
heap_close(rel, NoLock);
|
||||||
|
|
||||||
return qry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformSelectStmt -
|
* transformSelectStmt -
|
||||||
* transforms a Select Statement
|
* transforms a Select Statement
|
||||||
*
|
|
||||||
* Note: this is also used for DECLARE CURSOR statements.
|
|
||||||
*/
|
*/
|
||||||
static Query *
|
static Query *
|
||||||
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||||
|
@ -2991,6 +2936,11 @@ transformReturningList(ParseState *pstate, List *returningList)
|
||||||
/*
|
/*
|
||||||
* transformAlterTableStmt -
|
* transformAlterTableStmt -
|
||||||
* transform an Alter Table Statement
|
* transform an Alter Table Statement
|
||||||
|
*
|
||||||
|
* CAUTION: resist the temptation to do any work here that depends on the
|
||||||
|
* current state of the table. Actual execution of the command might not
|
||||||
|
* occur till some future transaction. Hence, we do only purely syntactic
|
||||||
|
* transformations here, comparable to the processing of CREATE TABLE.
|
||||||
*/
|
*/
|
||||||
static Query *
|
static Query *
|
||||||
transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||||
|
@ -3162,184 +3112,6 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||||
return qry;
|
return qry;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Query *
|
|
||||||
transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
|
|
||||||
{
|
|
||||||
Query *result = makeNode(Query);
|
|
||||||
List *extras_before = NIL,
|
|
||||||
*extras_after = NIL;
|
|
||||||
|
|
||||||
result->commandType = CMD_UTILITY;
|
|
||||||
result->utilityStmt = (Node *) stmt;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Don't allow both SCROLL and NO SCROLL to be specified
|
|
||||||
*/
|
|
||||||
if ((stmt->options & CURSOR_OPT_SCROLL) &&
|
|
||||||
(stmt->options & CURSOR_OPT_NO_SCROLL))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
|
||||||
errmsg("cannot specify both SCROLL and NO SCROLL")));
|
|
||||||
|
|
||||||
stmt->query = (Node *) transformStmt(pstate, stmt->query,
|
|
||||||
&extras_before, &extras_after);
|
|
||||||
|
|
||||||
/* Shouldn't get any extras, since grammar only allows SelectStmt */
|
|
||||||
if (extras_before || extras_after)
|
|
||||||
elog(ERROR, "unexpected extra stuff in cursor statement");
|
|
||||||
if (!IsA(stmt->query, Query) ||
|
|
||||||
((Query *) stmt->query)->commandType != CMD_SELECT)
|
|
||||||
elog(ERROR, "unexpected non-SELECT command in cursor statement");
|
|
||||||
|
|
||||||
/* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
|
|
||||||
if (((Query *) stmt->query)->into)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
|
|
||||||
errmsg("DECLARE CURSOR cannot specify INTO")));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static Query *
|
|
||||||
transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
|
|
||||||
{
|
|
||||||
Query *result = makeNode(Query);
|
|
||||||
List *argtype_oids; /* argtype OIDs in a list */
|
|
||||||
Oid *argtoids = NULL; /* and as an array */
|
|
||||||
int nargs;
|
|
||||||
List *queries;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
result->commandType = CMD_UTILITY;
|
|
||||||
result->utilityStmt = (Node *) stmt;
|
|
||||||
|
|
||||||
/* Transform list of TypeNames to list (and array) of type OIDs */
|
|
||||||
nargs = list_length(stmt->argtypes);
|
|
||||||
|
|
||||||
if (nargs)
|
|
||||||
{
|
|
||||||
ListCell *l;
|
|
||||||
|
|
||||||
argtoids = (Oid *) palloc(nargs * sizeof(Oid));
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
foreach(l, stmt->argtypes)
|
|
||||||
{
|
|
||||||
TypeName *tn = lfirst(l);
|
|
||||||
Oid toid = typenameTypeId(pstate, tn);
|
|
||||||
|
|
||||||
argtoids[i++] = toid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Analyze the statement using these parameter types (any parameters
|
|
||||||
* passed in from above us will not be visible to it), allowing
|
|
||||||
* information about unknown parameters to be deduced from context.
|
|
||||||
*/
|
|
||||||
queries = parse_analyze_varparams((Node *) stmt->query,
|
|
||||||
pstate->p_sourcetext,
|
|
||||||
&argtoids, &nargs);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shouldn't get any extra statements, since grammar only allows
|
|
||||||
* OptimizableStmt
|
|
||||||
*/
|
|
||||||
if (list_length(queries) != 1)
|
|
||||||
elog(ERROR, "unexpected extra stuff in prepared statement");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check that all parameter types were determined, and convert the array
|
|
||||||
* of OIDs into a list for storage.
|
|
||||||
*/
|
|
||||||
argtype_oids = NIL;
|
|
||||||
for (i = 0; i < nargs; i++)
|
|
||||||
{
|
|
||||||
Oid argtype = argtoids[i];
|
|
||||||
|
|
||||||
if (argtype == InvalidOid || argtype == UNKNOWNOID)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
|
|
||||||
errmsg("could not determine data type of parameter $%d",
|
|
||||||
i + 1)));
|
|
||||||
|
|
||||||
argtype_oids = lappend_oid(argtype_oids, argtype);
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt->argtype_oids = argtype_oids;
|
|
||||||
stmt->query = linitial(queries);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Query *
|
|
||||||
transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
|
|
||||||
{
|
|
||||||
Query *result = makeNode(Query);
|
|
||||||
List *paramtypes;
|
|
||||||
|
|
||||||
result->commandType = CMD_UTILITY;
|
|
||||||
result->utilityStmt = (Node *) stmt;
|
|
||||||
|
|
||||||
paramtypes = FetchPreparedStatementParams(stmt->name);
|
|
||||||
|
|
||||||
if (stmt->params || paramtypes)
|
|
||||||
{
|
|
||||||
int nparams = list_length(stmt->params);
|
|
||||||
int nexpected = list_length(paramtypes);
|
|
||||||
ListCell *l,
|
|
||||||
*l2;
|
|
||||||
int i = 1;
|
|
||||||
|
|
||||||
if (nparams != nexpected)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("wrong number of parameters for prepared statement \"%s\"",
|
|
||||||
stmt->name),
|
|
||||||
errdetail("Expected %d parameters but got %d.",
|
|
||||||
nexpected, nparams)));
|
|
||||||
|
|
||||||
forboth(l, stmt->params, l2, paramtypes)
|
|
||||||
{
|
|
||||||
Node *expr = lfirst(l);
|
|
||||||
Oid expected_type_id = lfirst_oid(l2);
|
|
||||||
Oid given_type_id;
|
|
||||||
|
|
||||||
expr = transformExpr(pstate, expr);
|
|
||||||
|
|
||||||
/* Cannot contain subselects or aggregates */
|
|
||||||
if (pstate->p_hasSubLinks)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("cannot use subquery in EXECUTE parameter")));
|
|
||||||
if (pstate->p_hasAggs)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_GROUPING_ERROR),
|
|
||||||
errmsg("cannot use aggregate function in EXECUTE parameter")));
|
|
||||||
|
|
||||||
given_type_id = exprType(expr);
|
|
||||||
|
|
||||||
expr = coerce_to_target_type(pstate, expr, given_type_id,
|
|
||||||
expected_type_id, -1,
|
|
||||||
COERCION_ASSIGNMENT,
|
|
||||||
COERCE_IMPLICIT_CAST);
|
|
||||||
|
|
||||||
if (expr == NULL)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
||||||
errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
|
|
||||||
i,
|
|
||||||
format_type_be(given_type_id),
|
|
||||||
format_type_be(expected_type_id)),
|
|
||||||
errhint("You will need to rewrite or cast the expression.")));
|
|
||||||
|
|
||||||
lfirst(l) = expr;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* exported so planner can check again after rewriting, query pullup, etc */
|
/* exported so planner can check again after rewriting, query pullup, etc */
|
||||||
void
|
void
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.580 2007/02/20 17:32:16 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.581 2007/03/13 00:33:41 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
|
@ -1662,7 +1662,7 @@ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
|
||||||
{
|
{
|
||||||
CopyStmt *n = makeNode(CopyStmt);
|
CopyStmt *n = makeNode(CopyStmt);
|
||||||
n->relation = NULL;
|
n->relation = NULL;
|
||||||
n->query = (Query *) $2;
|
n->query = $2;
|
||||||
n->attlist = NIL;
|
n->attlist = NIL;
|
||||||
n->is_from = false;
|
n->is_from = false;
|
||||||
n->filename = $4;
|
n->filename = $4;
|
||||||
|
@ -4959,22 +4959,22 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
|
||||||
AS SelectStmt opt_check_option
|
AS SelectStmt opt_check_option
|
||||||
{
|
{
|
||||||
ViewStmt *n = makeNode(ViewStmt);
|
ViewStmt *n = makeNode(ViewStmt);
|
||||||
n->replace = false;
|
|
||||||
n->view = $4;
|
n->view = $4;
|
||||||
n->view->istemp = $2;
|
n->view->istemp = $2;
|
||||||
n->aliases = $5;
|
n->aliases = $5;
|
||||||
n->query = (Query *) $7;
|
n->query = $7;
|
||||||
|
n->replace = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list
|
| CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list
|
||||||
AS SelectStmt opt_check_option
|
AS SelectStmt opt_check_option
|
||||||
{
|
{
|
||||||
ViewStmt *n = makeNode(ViewStmt);
|
ViewStmt *n = makeNode(ViewStmt);
|
||||||
n->replace = true;
|
|
||||||
n->view = $6;
|
n->view = $6;
|
||||||
n->view->istemp = $4;
|
n->view->istemp = $4;
|
||||||
n->aliases = $7;
|
n->aliases = $7;
|
||||||
n->query = (Query *) $9;
|
n->query = $9;
|
||||||
|
n->replace = true;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -5406,7 +5406,7 @@ ExplainStmt: EXPLAIN opt_analyze opt_verbose ExplainableStmt
|
||||||
ExplainStmt *n = makeNode(ExplainStmt);
|
ExplainStmt *n = makeNode(ExplainStmt);
|
||||||
n->analyze = $2;
|
n->analyze = $2;
|
||||||
n->verbose = $3;
|
n->verbose = $3;
|
||||||
n->query = (Query*)$4;
|
n->query = $4;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -5437,7 +5437,7 @@ PrepareStmt: PREPARE name prep_type_clause AS PreparableStmt
|
||||||
PrepareStmt *n = makeNode(PrepareStmt);
|
PrepareStmt *n = makeNode(PrepareStmt);
|
||||||
n->name = $2;
|
n->name = $2;
|
||||||
n->argtypes = $3;
|
n->argtypes = $3;
|
||||||
n->query = (Query *) $5;
|
n->query = $5;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.33 2007/03/07 13:35:02 alvherre Exp $
|
* $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.34 2007/03/13 00:33:41 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1248,13 +1248,6 @@ autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
|
||||||
|
|
||||||
vacstmt = makeNode(VacuumStmt);
|
vacstmt = makeNode(VacuumStmt);
|
||||||
|
|
||||||
/*
|
|
||||||
* Point QueryContext to the autovac memory context to fake out the
|
|
||||||
* PreventTransactionChain check inside vacuum(). Note that this is also
|
|
||||||
* why we palloc vacstmt instead of just using a local variable.
|
|
||||||
*/
|
|
||||||
QueryContext = CurrentMemoryContext;
|
|
||||||
|
|
||||||
/* Set up command parameters */
|
/* Set up command parameters */
|
||||||
vacstmt->vacuum = dovacuum;
|
vacstmt->vacuum = dovacuum;
|
||||||
vacstmt->full = false;
|
vacstmt->full = false;
|
||||||
|
@ -1267,7 +1260,7 @@ autovacuum_do_vac_analyze(Oid relid, bool dovacuum, bool doanalyze,
|
||||||
/* Let pgstat know what we're doing */
|
/* Let pgstat know what we're doing */
|
||||||
autovac_report_activity(vacstmt, relid);
|
autovac_report_activity(vacstmt, relid);
|
||||||
|
|
||||||
vacuum(vacstmt, list_make1_oid(relid));
|
vacuum(vacstmt, list_make1_oid(relid), true);
|
||||||
|
|
||||||
pfree(vacstmt);
|
pfree(vacstmt);
|
||||||
MemoryContextSwitchTo(old_cxt);
|
MemoryContextSwitchTo(old_cxt);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.117 2007/02/01 19:10:27 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.118 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
|
#include "parser/analyze.h"
|
||||||
#include "parser/parse_expr.h"
|
#include "parser/parse_expr.h"
|
||||||
#include "rewrite/rewriteDefine.h"
|
#include "rewrite/rewriteDefine.h"
|
||||||
#include "rewrite/rewriteManip.h"
|
#include "rewrite/rewriteManip.h"
|
||||||
|
@ -177,15 +178,46 @@ InsertRule(char *rulname,
|
||||||
return rewriteObjectId;
|
return rewriteObjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DefineRule
|
||||||
|
* Execute a CREATE RULE command.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
DefineQueryRewrite(RuleStmt *stmt)
|
DefineRule(RuleStmt *stmt, const char *queryString)
|
||||||
|
{
|
||||||
|
List *actions;
|
||||||
|
Node *whereClause;
|
||||||
|
|
||||||
|
/* Parse analysis ... */
|
||||||
|
analyzeRuleStmt(stmt, queryString, &actions, &whereClause);
|
||||||
|
|
||||||
|
/* ... and execution */
|
||||||
|
DefineQueryRewrite(stmt->rulename,
|
||||||
|
stmt->relation,
|
||||||
|
whereClause,
|
||||||
|
stmt->event,
|
||||||
|
stmt->instead,
|
||||||
|
stmt->replace,
|
||||||
|
actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DefineQueryRewrite
|
||||||
|
* Create a rule
|
||||||
|
*
|
||||||
|
* This is essentially the same as DefineRule() except that the rule's
|
||||||
|
* action and qual have already been passed through parse analysis.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
DefineQueryRewrite(char *rulename,
|
||||||
|
RangeVar *event_obj,
|
||||||
|
Node *event_qual,
|
||||||
|
CmdType event_type,
|
||||||
|
bool is_instead,
|
||||||
|
bool replace,
|
||||||
|
List *action)
|
||||||
{
|
{
|
||||||
RangeVar *event_obj = stmt->relation;
|
|
||||||
Node *event_qual = stmt->whereClause;
|
|
||||||
CmdType event_type = stmt->event;
|
|
||||||
bool is_instead = stmt->instead;
|
|
||||||
bool replace = stmt->replace;
|
|
||||||
List *action = stmt->actions;
|
|
||||||
Relation event_relation;
|
Relation event_relation;
|
||||||
Oid ev_relid;
|
Oid ev_relid;
|
||||||
Oid ruleId;
|
Oid ruleId;
|
||||||
|
@ -304,7 +336,7 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||||
/*
|
/*
|
||||||
* ... and finally the rule must be named _RETURN.
|
* ... and finally the rule must be named _RETURN.
|
||||||
*/
|
*/
|
||||||
if (strcmp(stmt->rulename, ViewSelectRuleName) != 0)
|
if (strcmp(rulename, ViewSelectRuleName) != 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* In versions before 7.3, the expected name was _RETviewname. For
|
* In versions before 7.3, the expected name was _RETviewname. For
|
||||||
|
@ -315,14 +347,14 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||||
* worry about where a multibyte character might have gotten
|
* worry about where a multibyte character might have gotten
|
||||||
* truncated.
|
* truncated.
|
||||||
*/
|
*/
|
||||||
if (strncmp(stmt->rulename, "_RET", 4) != 0 ||
|
if (strncmp(rulename, "_RET", 4) != 0 ||
|
||||||
strncmp(stmt->rulename + 4, event_obj->relname,
|
strncmp(rulename + 4, event_obj->relname,
|
||||||
NAMEDATALEN - 4 - 4) != 0)
|
NAMEDATALEN - 4 - 4) != 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
errmsg("view rule for \"%s\" must be named \"%s\"",
|
errmsg("view rule for \"%s\" must be named \"%s\"",
|
||||||
event_obj->relname, ViewSelectRuleName)));
|
event_obj->relname, ViewSelectRuleName)));
|
||||||
stmt->rulename = pstrdup(ViewSelectRuleName);
|
rulename = pstrdup(ViewSelectRuleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -411,7 +443,7 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||||
/* discard rule if it's null action and not INSTEAD; it's a no-op */
|
/* discard rule if it's null action and not INSTEAD; it's a no-op */
|
||||||
if (action != NIL || is_instead)
|
if (action != NIL || is_instead)
|
||||||
{
|
{
|
||||||
ruleId = InsertRule(stmt->rulename,
|
ruleId = InsertRule(rulename,
|
||||||
event_type,
|
event_type,
|
||||||
ev_relid,
|
ev_relid,
|
||||||
event_attno,
|
event_attno,
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.527 2007/03/03 19:32:54 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.528 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* this is the "main" module of the postgres backend and
|
||||||
|
@ -140,8 +140,9 @@ static bool ignore_till_sync = false;
|
||||||
* We keep it separate from the hashtable kept by commands/prepare.c
|
* We keep it separate from the hashtable kept by commands/prepare.c
|
||||||
* in order to reduce overhead for short-lived queries.
|
* in order to reduce overhead for short-lived queries.
|
||||||
*/
|
*/
|
||||||
|
static CachedPlanSource *unnamed_stmt_psrc = NULL;
|
||||||
|
/* workspace for building a new unnamed statement in */
|
||||||
static MemoryContext unnamed_stmt_context = NULL;
|
static MemoryContext unnamed_stmt_context = NULL;
|
||||||
static PreparedStatement *unnamed_stmt_pstmt = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
static bool EchoQuery = false; /* default don't echo */
|
static bool EchoQuery = false; /* default don't echo */
|
||||||
|
@ -173,6 +174,7 @@ static void finish_xact_command(void);
|
||||||
static bool IsTransactionExitStmt(Node *parsetree);
|
static bool IsTransactionExitStmt(Node *parsetree);
|
||||||
static bool IsTransactionExitStmtList(List *parseTrees);
|
static bool IsTransactionExitStmtList(List *parseTrees);
|
||||||
static bool IsTransactionStmtList(List *parseTrees);
|
static bool IsTransactionStmtList(List *parseTrees);
|
||||||
|
static void drop_unnamed_stmt(void);
|
||||||
static void SigHupHandler(SIGNAL_ARGS);
|
static void SigHupHandler(SIGNAL_ARGS);
|
||||||
static void log_disconnections(int code, Datum arg);
|
static void log_disconnections(int code, Datum arg);
|
||||||
|
|
||||||
|
@ -794,21 +796,13 @@ exec_simple_query(const char *query_string)
|
||||||
* statement and portal; this ensures we recover any storage used by prior
|
* statement and portal; this ensures we recover any storage used by prior
|
||||||
* unnamed operations.)
|
* unnamed operations.)
|
||||||
*/
|
*/
|
||||||
unnamed_stmt_pstmt = NULL;
|
drop_unnamed_stmt();
|
||||||
if (unnamed_stmt_context)
|
|
||||||
{
|
|
||||||
DropDependentPortals(unnamed_stmt_context);
|
|
||||||
MemoryContextDelete(unnamed_stmt_context);
|
|
||||||
}
|
|
||||||
unnamed_stmt_context = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switch to appropriate context for constructing parsetrees.
|
* Switch to appropriate context for constructing parsetrees.
|
||||||
*/
|
*/
|
||||||
oldcontext = MemoryContextSwitchTo(MessageContext);
|
oldcontext = MemoryContextSwitchTo(MessageContext);
|
||||||
|
|
||||||
QueryContext = CurrentMemoryContext;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do basic parsing of the query or queries (this should be safe even if
|
* Do basic parsing of the query or queries (this should be safe even if
|
||||||
* we are in aborted transaction state!)
|
* we are in aborted transaction state!)
|
||||||
|
@ -906,7 +900,7 @@ exec_simple_query(const char *query_string)
|
||||||
query_string,
|
query_string,
|
||||||
commandTag,
|
commandTag,
|
||||||
plantree_list,
|
plantree_list,
|
||||||
MessageContext);
|
NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the portal. No parameters here.
|
* Start the portal. No parameters here.
|
||||||
|
@ -950,6 +944,7 @@ exec_simple_query(const char *query_string)
|
||||||
*/
|
*/
|
||||||
(void) PortalRun(portal,
|
(void) PortalRun(portal,
|
||||||
FETCH_ALL,
|
FETCH_ALL,
|
||||||
|
true, /* top level */
|
||||||
receiver,
|
receiver,
|
||||||
receiver,
|
receiver,
|
||||||
completionTag);
|
completionTag);
|
||||||
|
@ -1009,8 +1004,6 @@ exec_simple_query(const char *query_string)
|
||||||
if (!parsetree_list)
|
if (!parsetree_list)
|
||||||
NullCommand(dest);
|
NullCommand(dest);
|
||||||
|
|
||||||
QueryContext = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Emit duration logging if appropriate.
|
* Emit duration logging if appropriate.
|
||||||
*/
|
*/
|
||||||
|
@ -1049,10 +1042,10 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
{
|
{
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
List *parsetree_list;
|
List *parsetree_list;
|
||||||
|
Node *raw_parse_tree;
|
||||||
const char *commandTag;
|
const char *commandTag;
|
||||||
List *querytree_list,
|
List *querytree_list,
|
||||||
*stmt_list,
|
*stmt_list;
|
||||||
*param_list;
|
|
||||||
bool is_named;
|
bool is_named;
|
||||||
bool fully_planned;
|
bool fully_planned;
|
||||||
bool save_log_statement_stats = log_statement_stats;
|
bool save_log_statement_stats = log_statement_stats;
|
||||||
|
@ -1088,12 +1081,12 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
* We have two strategies depending on whether the prepared statement is
|
* We have two strategies depending on whether the prepared statement is
|
||||||
* named or not. For a named prepared statement, we do parsing in
|
* named or not. For a named prepared statement, we do parsing in
|
||||||
* MessageContext and copy the finished trees into the prepared
|
* MessageContext and copy the finished trees into the prepared
|
||||||
* statement's private context; then the reset of MessageContext releases
|
* statement's plancache entry; then the reset of MessageContext releases
|
||||||
* temporary space used by parsing and planning. For an unnamed prepared
|
* temporary space used by parsing and planning. For an unnamed prepared
|
||||||
* statement, we assume the statement isn't going to hang around long, so
|
* statement, we assume the statement isn't going to hang around long, so
|
||||||
* getting rid of temp space quickly is probably not worth the costs of
|
* getting rid of temp space quickly is probably not worth the costs of
|
||||||
* copying parse/plan trees. So in this case, we set up a special context
|
* copying parse/plan trees. So in this case, we create the plancache
|
||||||
* for the unnamed statement, and do all the parsing work therein.
|
* entry's context here, and do all the parsing work therein.
|
||||||
*/
|
*/
|
||||||
is_named = (stmt_name[0] != '\0');
|
is_named = (stmt_name[0] != '\0');
|
||||||
if (is_named)
|
if (is_named)
|
||||||
|
@ -1104,16 +1097,10 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Unnamed prepared statement --- release any prior unnamed stmt */
|
/* Unnamed prepared statement --- release any prior unnamed stmt */
|
||||||
unnamed_stmt_pstmt = NULL;
|
drop_unnamed_stmt();
|
||||||
if (unnamed_stmt_context)
|
/* Create context for parsing/planning */
|
||||||
{
|
|
||||||
DropDependentPortals(unnamed_stmt_context);
|
|
||||||
MemoryContextDelete(unnamed_stmt_context);
|
|
||||||
}
|
|
||||||
unnamed_stmt_context = NULL;
|
|
||||||
/* create context for parsing/planning */
|
|
||||||
unnamed_stmt_context =
|
unnamed_stmt_context =
|
||||||
AllocSetContextCreate(TopMemoryContext,
|
AllocSetContextCreate(CacheMemoryContext,
|
||||||
"unnamed prepared statement",
|
"unnamed prepared statement",
|
||||||
ALLOCSET_DEFAULT_MINSIZE,
|
ALLOCSET_DEFAULT_MINSIZE,
|
||||||
ALLOCSET_DEFAULT_INITSIZE,
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
|
@ -1121,8 +1108,6 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
oldcontext = MemoryContextSwitchTo(unnamed_stmt_context);
|
oldcontext = MemoryContextSwitchTo(unnamed_stmt_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryContext = CurrentMemoryContext;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do basic parsing of the query or queries (this should be safe even if
|
* Do basic parsing of the query or queries (this should be safe even if
|
||||||
* we are in aborted transaction state!)
|
* we are in aborted transaction state!)
|
||||||
|
@ -1141,13 +1126,14 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
|
|
||||||
if (parsetree_list != NIL)
|
if (parsetree_list != NIL)
|
||||||
{
|
{
|
||||||
Node *parsetree = (Node *) linitial(parsetree_list);
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
raw_parse_tree = (Node *) linitial(parsetree_list);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the command name for possible use in status display.
|
* Get the command name for possible use in status display.
|
||||||
*/
|
*/
|
||||||
commandTag = CreateCommandTag(parsetree);
|
commandTag = CreateCommandTag(raw_parse_tree);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are in an aborted transaction, reject all commands except
|
* If we are in an aborted transaction, reject all commands except
|
||||||
|
@ -1158,7 +1144,7 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
* state, but not many...)
|
* state, but not many...)
|
||||||
*/
|
*/
|
||||||
if (IsAbortedTransactionBlockState() &&
|
if (IsAbortedTransactionBlockState() &&
|
||||||
!IsTransactionExitStmt(parsetree))
|
!IsTransactionExitStmt(raw_parse_tree))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||||
errmsg("current transaction is aborted, "
|
errmsg("current transaction is aborted, "
|
||||||
|
@ -1168,20 +1154,22 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
* OK to analyze, rewrite, and plan this query. Note that the
|
* OK to analyze, rewrite, and plan this query. Note that the
|
||||||
* originally specified parameter set is not required to be complete,
|
* originally specified parameter set is not required to be complete,
|
||||||
* so we have to use parse_analyze_varparams().
|
* so we have to use parse_analyze_varparams().
|
||||||
|
*
|
||||||
|
* XXX must use copyObject here since parse analysis scribbles on
|
||||||
|
* its input, and we need the unmodified raw parse tree for possible
|
||||||
|
* replanning later.
|
||||||
*/
|
*/
|
||||||
if (log_parser_stats)
|
if (log_parser_stats)
|
||||||
ResetUsage();
|
ResetUsage();
|
||||||
|
|
||||||
querytree_list = parse_analyze_varparams(parsetree,
|
querytree_list = parse_analyze_varparams(copyObject(raw_parse_tree),
|
||||||
query_string,
|
query_string,
|
||||||
¶mTypes,
|
¶mTypes,
|
||||||
&numParams);
|
&numParams);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check all parameter types got determined, and convert array
|
* Check all parameter types got determined.
|
||||||
* representation to a list for storage.
|
|
||||||
*/
|
*/
|
||||||
param_list = NIL;
|
|
||||||
for (i = 0; i < numParams; i++)
|
for (i = 0; i < numParams; i++)
|
||||||
{
|
{
|
||||||
Oid ptype = paramTypes[i];
|
Oid ptype = paramTypes[i];
|
||||||
|
@ -1191,7 +1179,6 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
|
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
|
||||||
errmsg("could not determine data type of parameter $%d",
|
errmsg("could not determine data type of parameter $%d",
|
||||||
i + 1)));
|
i + 1)));
|
||||||
param_list = lappend_oid(param_list, ptype);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_parser_stats)
|
if (log_parser_stats)
|
||||||
|
@ -1217,9 +1204,9 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Empty input string. This is legal. */
|
/* Empty input string. This is legal. */
|
||||||
|
raw_parse_tree = NULL;
|
||||||
commandTag = NULL;
|
commandTag = NULL;
|
||||||
stmt_list = NIL;
|
stmt_list = NIL;
|
||||||
param_list = NIL;
|
|
||||||
fully_planned = true;
|
fully_planned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1232,35 +1219,33 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
if (is_named)
|
if (is_named)
|
||||||
{
|
{
|
||||||
StorePreparedStatement(stmt_name,
|
StorePreparedStatement(stmt_name,
|
||||||
|
raw_parse_tree,
|
||||||
query_string,
|
query_string,
|
||||||
commandTag,
|
commandTag,
|
||||||
|
paramTypes,
|
||||||
|
numParams,
|
||||||
stmt_list,
|
stmt_list,
|
||||||
param_list,
|
|
||||||
fully_planned,
|
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PreparedStatement *pstmt;
|
|
||||||
|
|
||||||
pstmt = (PreparedStatement *) palloc0(sizeof(PreparedStatement));
|
|
||||||
/* query_string needs to be copied into unnamed_stmt_context */
|
/* query_string needs to be copied into unnamed_stmt_context */
|
||||||
pstmt->query_string = pstrdup(query_string);
|
|
||||||
/* the rest is there already */
|
/* the rest is there already */
|
||||||
pstmt->commandTag = commandTag;
|
unnamed_stmt_psrc = FastCreateCachedPlan(raw_parse_tree,
|
||||||
pstmt->stmt_list = stmt_list;
|
pstrdup(query_string),
|
||||||
pstmt->argtype_list = param_list;
|
commandTag,
|
||||||
pstmt->fully_planned = fully_planned;
|
paramTypes,
|
||||||
pstmt->from_sql = false;
|
numParams,
|
||||||
pstmt->context = unnamed_stmt_context;
|
stmt_list,
|
||||||
/* Now the unnamed statement is complete and valid */
|
fully_planned,
|
||||||
unnamed_stmt_pstmt = pstmt;
|
true,
|
||||||
|
unnamed_stmt_context);
|
||||||
|
/* context now belongs to the plancache entry */
|
||||||
|
unnamed_stmt_context = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
QueryContext = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We do NOT close the open transaction command here; that only happens
|
* We do NOT close the open transaction command here; that only happens
|
||||||
* when the client sends Sync. Instead, do CommandCounterIncrement just
|
* when the client sends Sync. Instead, do CommandCounterIncrement just
|
||||||
|
@ -1315,12 +1300,11 @@ exec_bind_message(StringInfo input_message)
|
||||||
int numParams;
|
int numParams;
|
||||||
int numRFormats;
|
int numRFormats;
|
||||||
int16 *rformats = NULL;
|
int16 *rformats = NULL;
|
||||||
PreparedStatement *pstmt;
|
CachedPlanSource *psrc;
|
||||||
|
CachedPlan *cplan;
|
||||||
Portal portal;
|
Portal portal;
|
||||||
ParamListInfo params;
|
ParamListInfo params;
|
||||||
List *query_list;
|
|
||||||
List *plan_list;
|
List *plan_list;
|
||||||
MemoryContext qContext;
|
|
||||||
bool save_log_statement_stats = log_statement_stats;
|
bool save_log_statement_stats = log_statement_stats;
|
||||||
char msec_str[32];
|
char msec_str[32];
|
||||||
|
|
||||||
|
@ -1335,12 +1319,17 @@ exec_bind_message(StringInfo input_message)
|
||||||
|
|
||||||
/* Find prepared statement */
|
/* Find prepared statement */
|
||||||
if (stmt_name[0] != '\0')
|
if (stmt_name[0] != '\0')
|
||||||
|
{
|
||||||
|
PreparedStatement *pstmt;
|
||||||
|
|
||||||
pstmt = FetchPreparedStatement(stmt_name, true);
|
pstmt = FetchPreparedStatement(stmt_name, true);
|
||||||
|
psrc = pstmt->plansource;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* special-case the unnamed statement */
|
/* special-case the unnamed statement */
|
||||||
pstmt = unnamed_stmt_pstmt;
|
psrc = unnamed_stmt_psrc;
|
||||||
if (!pstmt)
|
if (!psrc)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
|
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
|
||||||
errmsg("unnamed prepared statement does not exist")));
|
errmsg("unnamed prepared statement does not exist")));
|
||||||
|
@ -1349,7 +1338,7 @@ exec_bind_message(StringInfo input_message)
|
||||||
/*
|
/*
|
||||||
* Report query to various monitoring facilities.
|
* Report query to various monitoring facilities.
|
||||||
*/
|
*/
|
||||||
debug_query_string = pstmt->query_string ? pstmt->query_string : "<BIND>";
|
debug_query_string = psrc->query_string ? psrc->query_string : "<BIND>";
|
||||||
|
|
||||||
pgstat_report_activity(debug_query_string);
|
pgstat_report_activity(debug_query_string);
|
||||||
|
|
||||||
|
@ -1388,11 +1377,11 @@ exec_bind_message(StringInfo input_message)
|
||||||
errmsg("bind message has %d parameter formats but %d parameters",
|
errmsg("bind message has %d parameter formats but %d parameters",
|
||||||
numPFormats, numParams)));
|
numPFormats, numParams)));
|
||||||
|
|
||||||
if (numParams != list_length(pstmt->argtype_list))
|
if (numParams != psrc->num_params)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||||
errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
|
errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
|
||||||
numParams, stmt_name, list_length(pstmt->argtype_list))));
|
numParams, stmt_name, psrc->num_params)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are in aborted transaction state, the only portals we can
|
* If we are in aborted transaction state, the only portals we can
|
||||||
|
@ -1403,7 +1392,7 @@ exec_bind_message(StringInfo input_message)
|
||||||
* functions.
|
* functions.
|
||||||
*/
|
*/
|
||||||
if (IsAbortedTransactionBlockState() &&
|
if (IsAbortedTransactionBlockState() &&
|
||||||
(!IsTransactionExitStmtList(pstmt->stmt_list) ||
|
(!IsTransactionExitStmt(psrc->raw_parse_tree) ||
|
||||||
numParams != 0))
|
numParams != 0))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||||
|
@ -1424,7 +1413,6 @@ exec_bind_message(StringInfo input_message)
|
||||||
*/
|
*/
|
||||||
if (numParams > 0)
|
if (numParams > 0)
|
||||||
{
|
{
|
||||||
ListCell *l;
|
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
int paramno;
|
int paramno;
|
||||||
|
|
||||||
|
@ -1435,10 +1423,9 @@ exec_bind_message(StringInfo input_message)
|
||||||
(numParams - 1) *sizeof(ParamExternData));
|
(numParams - 1) *sizeof(ParamExternData));
|
||||||
params->numParams = numParams;
|
params->numParams = numParams;
|
||||||
|
|
||||||
paramno = 0;
|
for (paramno = 0; paramno < numParams; paramno++)
|
||||||
foreach(l, pstmt->argtype_list)
|
|
||||||
{
|
{
|
||||||
Oid ptype = lfirst_oid(l);
|
Oid ptype = psrc->param_types[paramno];
|
||||||
int32 plength;
|
int32 plength;
|
||||||
Datum pval;
|
Datum pval;
|
||||||
bool isNull;
|
bool isNull;
|
||||||
|
@ -1554,8 +1541,6 @@ exec_bind_message(StringInfo input_message)
|
||||||
*/
|
*/
|
||||||
params->params[paramno].pflags = PARAM_FLAG_CONST;
|
params->params[paramno].pflags = PARAM_FLAG_CONST;
|
||||||
params->params[paramno].ptype = ptype;
|
params->params[paramno].ptype = ptype;
|
||||||
|
|
||||||
paramno++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
@ -1576,46 +1561,62 @@ exec_bind_message(StringInfo input_message)
|
||||||
|
|
||||||
pq_getmsgend(input_message);
|
pq_getmsgend(input_message);
|
||||||
|
|
||||||
/*
|
if (psrc->fully_planned)
|
||||||
* If we didn't plan the query before, do it now. This allows the planner
|
|
||||||
* to make use of the concrete parameter values we now have. Because we
|
|
||||||
* use PARAM_FLAG_CONST, the plan is good only for this set of param
|
|
||||||
* values, and so we generate the plan in the portal's own memory context
|
|
||||||
* where it will be thrown away after use. As in exec_parse_message, we
|
|
||||||
* make no attempt to recover planner temporary memory until the end of
|
|
||||||
* the operation.
|
|
||||||
*
|
|
||||||
* XXX because the planner has a bad habit of scribbling on its input, we
|
|
||||||
* have to make a copy of the parse trees, just in case someone binds and
|
|
||||||
* executes an unnamed statement multiple times; this also means that the
|
|
||||||
* portal's queryContext becomes its own heap context rather than the
|
|
||||||
* prepared statement's context. FIXME someday
|
|
||||||
*/
|
|
||||||
if (pstmt->fully_planned)
|
|
||||||
{
|
{
|
||||||
plan_list = pstmt->stmt_list;
|
/*
|
||||||
qContext = pstmt->context;
|
* Revalidate the cached plan; this may result in replanning. Any
|
||||||
|
* cruft will be generated in MessageContext. The plan refcount
|
||||||
|
* will be assigned to the Portal, so it will be released at portal
|
||||||
|
* destruction.
|
||||||
|
*/
|
||||||
|
cplan = RevalidateCachedPlan(psrc, false);
|
||||||
|
plan_list = cplan->stmt_list;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
List *query_list;
|
||||||
|
|
||||||
qContext = PortalGetHeapMemory(portal);
|
/*
|
||||||
oldContext = MemoryContextSwitchTo(qContext);
|
* Revalidate the cached plan; this may result in redoing parse
|
||||||
query_list = copyObject(pstmt->stmt_list);
|
* analysis and rewriting (but not planning). Any cruft will be
|
||||||
|
* generated in MessageContext. The plan refcount is assigned to
|
||||||
|
* CurrentResourceOwner.
|
||||||
|
*/
|
||||||
|
cplan = RevalidateCachedPlan(psrc, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We didn't plan the query before, so do it now. This allows the
|
||||||
|
* planner to make use of the concrete parameter values we now have.
|
||||||
|
* Because we use PARAM_FLAG_CONST, the plan is good only for this set
|
||||||
|
* of param values, and so we generate the plan in the portal's own
|
||||||
|
* memory context where it will be thrown away after use. As in
|
||||||
|
* exec_parse_message, we make no attempt to recover planner temporary
|
||||||
|
* memory until the end of the operation.
|
||||||
|
*
|
||||||
|
* XXX because the planner has a bad habit of scribbling on its input,
|
||||||
|
* we have to make a copy of the parse trees. FIXME someday.
|
||||||
|
*/
|
||||||
|
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||||
|
query_list = copyObject(cplan->stmt_list);
|
||||||
plan_list = pg_plan_queries(query_list, params, true);
|
plan_list = pg_plan_queries(query_list, params, true);
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
|
||||||
|
/* We no longer need the cached plan refcount ... */
|
||||||
|
ReleaseCachedPlan(cplan, true);
|
||||||
|
/* ... and we don't want the portal to depend on it, either */
|
||||||
|
cplan = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define portal and start execution.
|
* Define portal and start execution.
|
||||||
*/
|
*/
|
||||||
PortalDefineQuery(portal,
|
PortalDefineQuery(portal,
|
||||||
*pstmt->stmt_name ? pstmt->stmt_name : NULL,
|
stmt_name[0] ? stmt_name : NULL,
|
||||||
pstmt->query_string,
|
psrc->query_string,
|
||||||
pstmt->commandTag,
|
psrc->commandTag,
|
||||||
plan_list,
|
plan_list,
|
||||||
qContext);
|
cplan);
|
||||||
|
|
||||||
PortalStart(portal, params, InvalidSnapshot);
|
PortalStart(portal, params, InvalidSnapshot);
|
||||||
|
|
||||||
|
@ -1647,7 +1648,7 @@ exec_bind_message(StringInfo input_message)
|
||||||
*stmt_name ? stmt_name : "<unnamed>",
|
*stmt_name ? stmt_name : "<unnamed>",
|
||||||
*portal_name ? "/" : "",
|
*portal_name ? "/" : "",
|
||||||
*portal_name ? portal_name : "",
|
*portal_name ? portal_name : "",
|
||||||
pstmt->query_string ? pstmt->query_string : "<source not stored>"),
|
psrc->query_string ? psrc->query_string : "<source not stored>"),
|
||||||
errhidestmt(true),
|
errhidestmt(true),
|
||||||
errdetail_params(params)));
|
errdetail_params(params)));
|
||||||
break;
|
break;
|
||||||
|
@ -1809,6 +1810,7 @@ exec_execute_message(const char *portal_name, long max_rows)
|
||||||
|
|
||||||
completed = PortalRun(portal,
|
completed = PortalRun(portal,
|
||||||
max_rows,
|
max_rows,
|
||||||
|
true, /* top level */
|
||||||
receiver,
|
receiver,
|
||||||
receiver,
|
receiver,
|
||||||
completionTag);
|
completionTag);
|
||||||
|
@ -1981,9 +1983,9 @@ errdetail_execute(List *raw_parsetree_list)
|
||||||
PreparedStatement *pstmt;
|
PreparedStatement *pstmt;
|
||||||
|
|
||||||
pstmt = FetchPreparedStatement(stmt->name, false);
|
pstmt = FetchPreparedStatement(stmt->name, false);
|
||||||
if (pstmt && pstmt->query_string)
|
if (pstmt && pstmt->plansource->query_string)
|
||||||
{
|
{
|
||||||
errdetail("prepare: %s", pstmt->query_string);
|
errdetail("prepare: %s", pstmt->plansource->query_string);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2064,10 +2066,9 @@ errdetail_params(ParamListInfo params)
|
||||||
static void
|
static void
|
||||||
exec_describe_statement_message(const char *stmt_name)
|
exec_describe_statement_message(const char *stmt_name)
|
||||||
{
|
{
|
||||||
PreparedStatement *pstmt;
|
CachedPlanSource *psrc;
|
||||||
TupleDesc tupdesc;
|
|
||||||
ListCell *l;
|
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start up a transaction command. (Note that this will normally change
|
* Start up a transaction command. (Note that this will normally change
|
||||||
|
@ -2080,28 +2081,37 @@ exec_describe_statement_message(const char *stmt_name)
|
||||||
|
|
||||||
/* Find prepared statement */
|
/* Find prepared statement */
|
||||||
if (stmt_name[0] != '\0')
|
if (stmt_name[0] != '\0')
|
||||||
|
{
|
||||||
|
PreparedStatement *pstmt;
|
||||||
|
|
||||||
pstmt = FetchPreparedStatement(stmt_name, true);
|
pstmt = FetchPreparedStatement(stmt_name, true);
|
||||||
|
psrc = pstmt->plansource;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* special-case the unnamed statement */
|
/* special-case the unnamed statement */
|
||||||
pstmt = unnamed_stmt_pstmt;
|
psrc = unnamed_stmt_psrc;
|
||||||
if (!pstmt)
|
if (!psrc)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
|
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
|
||||||
errmsg("unnamed prepared statement does not exist")));
|
errmsg("unnamed prepared statement does not exist")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prepared statements shouldn't have changeable result descs */
|
||||||
|
Assert(psrc->fixed_result);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are in aborted transaction state, we can't safely create a result
|
* If we are in aborted transaction state, we can't run
|
||||||
* tupledesc, because that needs catalog accesses. Hence, refuse to
|
* SendRowDescriptionMessage(), because that needs catalog accesses.
|
||||||
* Describe statements that return data. (We shouldn't just refuse all
|
* (We can't do RevalidateCachedPlan, either, but that's a lesser problem.)
|
||||||
* Describes, since that might break the ability of some clients to issue
|
* Hence, refuse to Describe statements that return data. (We shouldn't
|
||||||
* COMMIT or ROLLBACK commands, if they use code that blindly Describes
|
* just refuse all Describes, since that might break the ability of some
|
||||||
* whatever it does.) We can Describe parameters without doing anything
|
* clients to issue COMMIT or ROLLBACK commands, if they use code that
|
||||||
* dangerous, so we don't restrict that.
|
* blindly Describes whatever it does.) We can Describe parameters
|
||||||
|
* without doing anything dangerous, so we don't restrict that.
|
||||||
*/
|
*/
|
||||||
if (IsAbortedTransactionBlockState() &&
|
if (IsAbortedTransactionBlockState() &&
|
||||||
PreparedStatementReturnsTuples(pstmt))
|
psrc->resultDesc)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||||
errmsg("current transaction is aborted, "
|
errmsg("current transaction is aborted, "
|
||||||
|
@ -2114,11 +2124,11 @@ exec_describe_statement_message(const char *stmt_name)
|
||||||
* First describe the parameters...
|
* First describe the parameters...
|
||||||
*/
|
*/
|
||||||
pq_beginmessage(&buf, 't'); /* parameter description message type */
|
pq_beginmessage(&buf, 't'); /* parameter description message type */
|
||||||
pq_sendint(&buf, list_length(pstmt->argtype_list), 2);
|
pq_sendint(&buf, psrc->num_params, 2);
|
||||||
|
|
||||||
foreach(l, pstmt->argtype_list)
|
for (i = 0; i < psrc->num_params; i++)
|
||||||
{
|
{
|
||||||
Oid ptype = lfirst_oid(l);
|
Oid ptype = psrc->param_types[i];
|
||||||
|
|
||||||
pq_sendint(&buf, (int) ptype, 4);
|
pq_sendint(&buf, (int) ptype, 4);
|
||||||
}
|
}
|
||||||
|
@ -2127,11 +2137,21 @@ exec_describe_statement_message(const char *stmt_name)
|
||||||
/*
|
/*
|
||||||
* Next send RowDescription or NoData to describe the result...
|
* Next send RowDescription or NoData to describe the result...
|
||||||
*/
|
*/
|
||||||
tupdesc = FetchPreparedStatementResultDesc(pstmt);
|
if (psrc->resultDesc)
|
||||||
if (tupdesc)
|
{
|
||||||
SendRowDescriptionMessage(tupdesc,
|
CachedPlan *cplan;
|
||||||
FetchPreparedStatementTargetList(pstmt),
|
List *tlist;
|
||||||
NULL);
|
|
||||||
|
/* Make sure the plan is up to date */
|
||||||
|
cplan = RevalidateCachedPlan(psrc, true);
|
||||||
|
|
||||||
|
/* Get the primary statement and find out what it returns */
|
||||||
|
tlist = FetchStatementTargetList(PortalListGetPrimaryStmt(cplan->stmt_list));
|
||||||
|
|
||||||
|
SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
|
||||||
|
|
||||||
|
ReleaseCachedPlan(cplan, true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
pq_putemptymessage('n'); /* NoData */
|
pq_putemptymessage('n'); /* NoData */
|
||||||
|
|
||||||
|
@ -2308,6 +2328,24 @@ IsTransactionStmtList(List *parseTrees)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Release any existing unnamed prepared statement */
|
||||||
|
static void
|
||||||
|
drop_unnamed_stmt(void)
|
||||||
|
{
|
||||||
|
/* Release any completed unnamed statement */
|
||||||
|
if (unnamed_stmt_psrc)
|
||||||
|
DropCachedPlan(unnamed_stmt_psrc);
|
||||||
|
unnamed_stmt_psrc = NULL;
|
||||||
|
/*
|
||||||
|
* If we failed while trying to build a prior unnamed statement, we may
|
||||||
|
* have a memory context that wasn't assigned to a completed plancache
|
||||||
|
* entry. If so, drop it to avoid a permanent memory leak.
|
||||||
|
*/
|
||||||
|
if (unnamed_stmt_context)
|
||||||
|
MemoryContextDelete(unnamed_stmt_context);
|
||||||
|
unnamed_stmt_context = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* signal handler routines used in PostgresMain()
|
* signal handler routines used in PostgresMain()
|
||||||
|
@ -3313,7 +3351,6 @@ PostgresMain(int argc, char *argv[], const char *username)
|
||||||
*/
|
*/
|
||||||
MemoryContextSwitchTo(TopMemoryContext);
|
MemoryContextSwitchTo(TopMemoryContext);
|
||||||
FlushErrorState();
|
FlushErrorState();
|
||||||
QueryContext = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we were handling an extended-query-protocol message, initiate
|
* If we were handling an extended-query-protocol message, initiate
|
||||||
|
@ -3558,13 +3595,7 @@ PostgresMain(int argc, char *argv[], const char *username)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* special-case the unnamed statement */
|
/* special-case the unnamed statement */
|
||||||
unnamed_stmt_pstmt = NULL;
|
drop_unnamed_stmt();
|
||||||
if (unnamed_stmt_context)
|
|
||||||
{
|
|
||||||
DropDependentPortals(unnamed_stmt_context);
|
|
||||||
MemoryContextDelete(unnamed_stmt_context);
|
|
||||||
}
|
|
||||||
unnamed_stmt_context = NULL;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.114 2007/02/20 17:32:16 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.115 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -36,14 +36,14 @@ static void ProcessQuery(PlannedStmt *plan,
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
char *completionTag);
|
char *completionTag);
|
||||||
static void FillPortalStore(Portal portal);
|
static void FillPortalStore(Portal portal, bool isTopLevel);
|
||||||
static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
|
static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
|
||||||
DestReceiver *dest);
|
DestReceiver *dest);
|
||||||
static long PortalRunSelect(Portal portal, bool forward, long count,
|
static long PortalRunSelect(Portal portal, bool forward, long count,
|
||||||
DestReceiver *dest);
|
DestReceiver *dest);
|
||||||
static void PortalRunUtility(Portal portal, Node *utilityStmt,
|
static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
|
||||||
DestReceiver *dest, char *completionTag);
|
DestReceiver *dest, char *completionTag);
|
||||||
static void PortalRunMulti(Portal portal,
|
static void PortalRunMulti(Portal portal, bool isTopLevel,
|
||||||
DestReceiver *dest, DestReceiver *altdest,
|
DestReceiver *dest, DestReceiver *altdest,
|
||||||
char *completionTag);
|
char *completionTag);
|
||||||
static long DoPortalRunFetch(Portal portal,
|
static long DoPortalRunFetch(Portal portal,
|
||||||
|
@ -148,8 +148,7 @@ ProcessQuery(PlannedStmt *plan,
|
||||||
{
|
{
|
||||||
QueryDesc *queryDesc;
|
QueryDesc *queryDesc;
|
||||||
|
|
||||||
ereport(DEBUG3,
|
elog(DEBUG3, "ProcessQuery");
|
||||||
(errmsg_internal("ProcessQuery")));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Must always set snapshot for plannable queries. Note we assume that
|
* Must always set snapshot for plannable queries. Note we assume that
|
||||||
|
@ -232,8 +231,7 @@ ProcessQuery(PlannedStmt *plan,
|
||||||
* Select portal execution strategy given the intended statement list.
|
* Select portal execution strategy given the intended statement list.
|
||||||
*
|
*
|
||||||
* The list elements can be Querys, PlannedStmts, or utility statements.
|
* The list elements can be Querys, PlannedStmts, or utility statements.
|
||||||
* That's more general than portals need, but we use this for prepared
|
* That's more general than portals need, but plancache.c uses this too.
|
||||||
* statements as well.
|
|
||||||
*
|
*
|
||||||
* See the comments in portal.h.
|
* See the comments in portal.h.
|
||||||
*/
|
*/
|
||||||
|
@ -358,8 +356,7 @@ FetchPortalTargetList(Portal portal)
|
||||||
* Returns NIL if the statement doesn't have a determinable targetlist.
|
* Returns NIL if the statement doesn't have a determinable targetlist.
|
||||||
*
|
*
|
||||||
* This can be applied to a Query, a PlannedStmt, or a utility statement.
|
* This can be applied to a Query, a PlannedStmt, or a utility statement.
|
||||||
* That's more general than portals need, but we use this for prepared
|
* That's more general than portals need, but plancache.c uses this too.
|
||||||
* statements as well.
|
|
||||||
*
|
*
|
||||||
* Note: do not modify the result.
|
* Note: do not modify the result.
|
||||||
*
|
*
|
||||||
|
@ -452,11 +449,10 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
|
||||||
int eflags;
|
int eflags;
|
||||||
|
|
||||||
AssertArg(PortalIsValid(portal));
|
AssertArg(PortalIsValid(portal));
|
||||||
AssertState(portal->queryContext != NULL); /* query defined? */
|
AssertState(portal->status == PORTAL_DEFINED);
|
||||||
AssertState(portal->status == PORTAL_NEW); /* else extra PortalStart */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up global portal context pointers. (Should we set QueryContext?)
|
* Set up global portal context pointers.
|
||||||
*/
|
*/
|
||||||
saveActivePortal = ActivePortal;
|
saveActivePortal = ActivePortal;
|
||||||
saveActiveSnapshot = ActiveSnapshot;
|
saveActiveSnapshot = ActiveSnapshot;
|
||||||
|
@ -683,6 +679,9 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
|
||||||
* interpreted as "all rows". Note that count is ignored in multi-query
|
* interpreted as "all rows". Note that count is ignored in multi-query
|
||||||
* situations, where we always run the portal to completion.
|
* situations, where we always run the portal to completion.
|
||||||
*
|
*
|
||||||
|
* isTopLevel: true if query is being executed at backend "top level"
|
||||||
|
* (that is, directly from a client command message)
|
||||||
|
*
|
||||||
* dest: where to send output of primary (canSetTag) query
|
* dest: where to send output of primary (canSetTag) query
|
||||||
*
|
*
|
||||||
* altdest: where to send output of non-primary queries
|
* altdest: where to send output of non-primary queries
|
||||||
|
@ -695,7 +694,7 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
|
||||||
* suspended due to exhaustion of the count parameter.
|
* suspended due to exhaustion of the count parameter.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
PortalRun(Portal portal, long count,
|
PortalRun(Portal portal, long count, bool isTopLevel,
|
||||||
DestReceiver *dest, DestReceiver *altdest,
|
DestReceiver *dest, DestReceiver *altdest,
|
||||||
char *completionTag)
|
char *completionTag)
|
||||||
{
|
{
|
||||||
|
@ -706,7 +705,6 @@ PortalRun(Portal portal, long count,
|
||||||
Snapshot saveActiveSnapshot;
|
Snapshot saveActiveSnapshot;
|
||||||
ResourceOwner saveResourceOwner;
|
ResourceOwner saveResourceOwner;
|
||||||
MemoryContext savePortalContext;
|
MemoryContext savePortalContext;
|
||||||
MemoryContext saveQueryContext;
|
|
||||||
MemoryContext saveMemoryContext;
|
MemoryContext saveMemoryContext;
|
||||||
|
|
||||||
AssertArg(PortalIsValid(portal));
|
AssertArg(PortalIsValid(portal));
|
||||||
|
@ -717,8 +715,7 @@ PortalRun(Portal portal, long count,
|
||||||
|
|
||||||
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
|
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
|
||||||
{
|
{
|
||||||
ereport(DEBUG3,
|
elog(DEBUG3, "PortalRun");
|
||||||
(errmsg_internal("PortalRun")));
|
|
||||||
/* PORTAL_MULTI_QUERY logs its own stats per query */
|
/* PORTAL_MULTI_QUERY logs its own stats per query */
|
||||||
ResetUsage();
|
ResetUsage();
|
||||||
}
|
}
|
||||||
|
@ -752,7 +749,6 @@ PortalRun(Portal portal, long count,
|
||||||
saveActiveSnapshot = ActiveSnapshot;
|
saveActiveSnapshot = ActiveSnapshot;
|
||||||
saveResourceOwner = CurrentResourceOwner;
|
saveResourceOwner = CurrentResourceOwner;
|
||||||
savePortalContext = PortalContext;
|
savePortalContext = PortalContext;
|
||||||
saveQueryContext = QueryContext;
|
|
||||||
saveMemoryContext = CurrentMemoryContext;
|
saveMemoryContext = CurrentMemoryContext;
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
|
@ -760,7 +756,6 @@ PortalRun(Portal portal, long count,
|
||||||
ActiveSnapshot = NULL; /* will be set later */
|
ActiveSnapshot = NULL; /* will be set later */
|
||||||
CurrentResourceOwner = portal->resowner;
|
CurrentResourceOwner = portal->resowner;
|
||||||
PortalContext = PortalGetHeapMemory(portal);
|
PortalContext = PortalGetHeapMemory(portal);
|
||||||
QueryContext = portal->queryContext;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(PortalContext);
|
MemoryContextSwitchTo(PortalContext);
|
||||||
|
|
||||||
|
@ -790,7 +785,7 @@ PortalRun(Portal portal, long count,
|
||||||
* results in the portal's tuplestore.
|
* results in the portal's tuplestore.
|
||||||
*/
|
*/
|
||||||
if (!portal->holdStore)
|
if (!portal->holdStore)
|
||||||
FillPortalStore(portal);
|
FillPortalStore(portal, isTopLevel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now fetch desired portion of results.
|
* Now fetch desired portion of results.
|
||||||
|
@ -811,7 +806,8 @@ PortalRun(Portal portal, long count,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PORTAL_MULTI_QUERY:
|
case PORTAL_MULTI_QUERY:
|
||||||
PortalRunMulti(portal, dest, altdest, completionTag);
|
PortalRunMulti(portal, isTopLevel,
|
||||||
|
dest, altdest, completionTag);
|
||||||
|
|
||||||
/* Prevent portal's commands from being re-executed */
|
/* Prevent portal's commands from being re-executed */
|
||||||
portal->status = PORTAL_DONE;
|
portal->status = PORTAL_DONE;
|
||||||
|
@ -844,7 +840,6 @@ PortalRun(Portal portal, long count,
|
||||||
else
|
else
|
||||||
CurrentResourceOwner = saveResourceOwner;
|
CurrentResourceOwner = saveResourceOwner;
|
||||||
PortalContext = savePortalContext;
|
PortalContext = savePortalContext;
|
||||||
QueryContext = saveQueryContext;
|
|
||||||
|
|
||||||
PG_RE_THROW();
|
PG_RE_THROW();
|
||||||
}
|
}
|
||||||
|
@ -861,7 +856,6 @@ PortalRun(Portal portal, long count,
|
||||||
else
|
else
|
||||||
CurrentResourceOwner = saveResourceOwner;
|
CurrentResourceOwner = saveResourceOwner;
|
||||||
PortalContext = savePortalContext;
|
PortalContext = savePortalContext;
|
||||||
QueryContext = saveQueryContext;
|
|
||||||
|
|
||||||
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
|
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
|
||||||
ShowUsage("EXECUTOR STATISTICS");
|
ShowUsage("EXECUTOR STATISTICS");
|
||||||
|
@ -1025,7 +1019,7 @@ PortalRunSelect(Portal portal,
|
||||||
* This is used for PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT cases only.
|
* This is used for PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT cases only.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
FillPortalStore(Portal portal)
|
FillPortalStore(Portal portal, bool isTopLevel)
|
||||||
{
|
{
|
||||||
DestReceiver *treceiver;
|
DestReceiver *treceiver;
|
||||||
char completionTag[COMPLETION_TAG_BUFSIZE];
|
char completionTag[COMPLETION_TAG_BUFSIZE];
|
||||||
|
@ -1044,12 +1038,13 @@ FillPortalStore(Portal portal)
|
||||||
* MULTI_QUERY case, but send the primary query's output to the
|
* MULTI_QUERY case, but send the primary query's output to the
|
||||||
* tuplestore. Auxiliary query outputs are discarded.
|
* tuplestore. Auxiliary query outputs are discarded.
|
||||||
*/
|
*/
|
||||||
PortalRunMulti(portal, treceiver, None_Receiver, completionTag);
|
PortalRunMulti(portal, isTopLevel,
|
||||||
|
treceiver, None_Receiver, completionTag);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PORTAL_UTIL_SELECT:
|
case PORTAL_UTIL_SELECT:
|
||||||
PortalRunUtility(portal, (Node *) linitial(portal->stmts),
|
PortalRunUtility(portal, (Node *) linitial(portal->stmts),
|
||||||
treceiver, completionTag);
|
isTopLevel, treceiver, completionTag);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1137,11 +1132,10 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
|
||||||
* Execute a utility statement inside a portal.
|
* Execute a utility statement inside a portal.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PortalRunUtility(Portal portal, Node *utilityStmt,
|
PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
|
||||||
DestReceiver *dest, char *completionTag)
|
DestReceiver *dest, char *completionTag)
|
||||||
{
|
{
|
||||||
ereport(DEBUG3,
|
elog(DEBUG3, "ProcessUtility");
|
||||||
(errmsg_internal("ProcessUtility")));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set snapshot if utility stmt needs one. Most reliable way to do this
|
* Set snapshot if utility stmt needs one. Most reliable way to do this
|
||||||
|
@ -1173,7 +1167,12 @@ PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||||
else
|
else
|
||||||
ActiveSnapshot = NULL;
|
ActiveSnapshot = NULL;
|
||||||
|
|
||||||
ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag);
|
ProcessUtility(utilityStmt,
|
||||||
|
portal->sourceText,
|
||||||
|
portal->portalParams,
|
||||||
|
isTopLevel,
|
||||||
|
dest,
|
||||||
|
completionTag);
|
||||||
|
|
||||||
/* Some utility statements may change context on us */
|
/* Some utility statements may change context on us */
|
||||||
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||||
|
@ -1189,7 +1188,7 @@ PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||||
* or non-SELECT-like queries)
|
* or non-SELECT-like queries)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PortalRunMulti(Portal portal,
|
PortalRunMulti(Portal portal, bool isTopLevel,
|
||||||
DestReceiver *dest, DestReceiver *altdest,
|
DestReceiver *dest, DestReceiver *altdest,
|
||||||
char *completionTag)
|
char *completionTag)
|
||||||
{
|
{
|
||||||
|
@ -1260,9 +1259,9 @@ PortalRunMulti(Portal portal,
|
||||||
* portal.
|
* portal.
|
||||||
*/
|
*/
|
||||||
if (list_length(portal->stmts) == 1)
|
if (list_length(portal->stmts) == 1)
|
||||||
PortalRunUtility(portal, stmt, dest, completionTag);
|
PortalRunUtility(portal, stmt, isTopLevel, dest, completionTag);
|
||||||
else
|
else
|
||||||
PortalRunUtility(portal, stmt, altdest, NULL);
|
PortalRunUtility(portal, stmt, isTopLevel, altdest, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1305,6 +1304,8 @@ PortalRunMulti(Portal portal,
|
||||||
* PortalRunFetch
|
* PortalRunFetch
|
||||||
* Variant form of PortalRun that supports SQL FETCH directions.
|
* Variant form of PortalRun that supports SQL FETCH directions.
|
||||||
*
|
*
|
||||||
|
* Note: we presently assume that no callers of this want isTopLevel = true.
|
||||||
|
*
|
||||||
* Returns number of rows processed (suitable for use in result tag)
|
* Returns number of rows processed (suitable for use in result tag)
|
||||||
*/
|
*/
|
||||||
long
|
long
|
||||||
|
@ -1318,7 +1319,6 @@ PortalRunFetch(Portal portal,
|
||||||
Snapshot saveActiveSnapshot;
|
Snapshot saveActiveSnapshot;
|
||||||
ResourceOwner saveResourceOwner;
|
ResourceOwner saveResourceOwner;
|
||||||
MemoryContext savePortalContext;
|
MemoryContext savePortalContext;
|
||||||
MemoryContext saveQueryContext;
|
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
|
||||||
AssertArg(PortalIsValid(portal));
|
AssertArg(PortalIsValid(portal));
|
||||||
|
@ -1339,14 +1339,12 @@ PortalRunFetch(Portal portal,
|
||||||
saveActiveSnapshot = ActiveSnapshot;
|
saveActiveSnapshot = ActiveSnapshot;
|
||||||
saveResourceOwner = CurrentResourceOwner;
|
saveResourceOwner = CurrentResourceOwner;
|
||||||
savePortalContext = PortalContext;
|
savePortalContext = PortalContext;
|
||||||
saveQueryContext = QueryContext;
|
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
ActivePortal = portal;
|
ActivePortal = portal;
|
||||||
ActiveSnapshot = NULL; /* will be set later */
|
ActiveSnapshot = NULL; /* will be set later */
|
||||||
CurrentResourceOwner = portal->resowner;
|
CurrentResourceOwner = portal->resowner;
|
||||||
PortalContext = PortalGetHeapMemory(portal);
|
PortalContext = PortalGetHeapMemory(portal);
|
||||||
QueryContext = portal->queryContext;
|
|
||||||
|
|
||||||
oldContext = MemoryContextSwitchTo(PortalContext);
|
oldContext = MemoryContextSwitchTo(PortalContext);
|
||||||
|
|
||||||
|
@ -1364,7 +1362,7 @@ PortalRunFetch(Portal portal,
|
||||||
* results in the portal's tuplestore.
|
* results in the portal's tuplestore.
|
||||||
*/
|
*/
|
||||||
if (!portal->holdStore)
|
if (!portal->holdStore)
|
||||||
FillPortalStore(portal);
|
FillPortalStore(portal, false /* isTopLevel */);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now fetch desired portion of results.
|
* Now fetch desired portion of results.
|
||||||
|
@ -1388,7 +1386,6 @@ PortalRunFetch(Portal portal,
|
||||||
ActiveSnapshot = saveActiveSnapshot;
|
ActiveSnapshot = saveActiveSnapshot;
|
||||||
CurrentResourceOwner = saveResourceOwner;
|
CurrentResourceOwner = saveResourceOwner;
|
||||||
PortalContext = savePortalContext;
|
PortalContext = savePortalContext;
|
||||||
QueryContext = saveQueryContext;
|
|
||||||
|
|
||||||
PG_RE_THROW();
|
PG_RE_THROW();
|
||||||
}
|
}
|
||||||
|
@ -1403,7 +1400,6 @@ PortalRunFetch(Portal portal,
|
||||||
ActiveSnapshot = saveActiveSnapshot;
|
ActiveSnapshot = saveActiveSnapshot;
|
||||||
CurrentResourceOwner = saveResourceOwner;
|
CurrentResourceOwner = saveResourceOwner;
|
||||||
PortalContext = savePortalContext;
|
PortalContext = savePortalContext;
|
||||||
QueryContext = saveQueryContext;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.273 2007/02/20 17:32:16 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.274 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -44,6 +44,7 @@
|
||||||
#include "commands/vacuum.h"
|
#include "commands/vacuum.h"
|
||||||
#include "commands/view.h"
|
#include "commands/view.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "parser/analyze.h"
|
||||||
#include "postmaster/bgwriter.h"
|
#include "postmaster/bgwriter.h"
|
||||||
#include "rewrite/rewriteDefine.h"
|
#include "rewrite/rewriteDefine.h"
|
||||||
#include "rewrite/rewriteRemove.h"
|
#include "rewrite/rewriteRemove.h"
|
||||||
|
@ -368,7 +369,9 @@ check_xact_readonly(Node *parsetree)
|
||||||
* general utility function invoker
|
* general utility function invoker
|
||||||
*
|
*
|
||||||
* parsetree: the parse tree for the utility statement
|
* parsetree: the parse tree for the utility statement
|
||||||
|
* queryString: original source text of command (NULL if not available)
|
||||||
* params: parameters to use during execution
|
* params: parameters to use during execution
|
||||||
|
* isTopLevel: true if executing a "top level" (interactively issued) command
|
||||||
* dest: where to send results
|
* dest: where to send results
|
||||||
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
|
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
|
||||||
* in which to store a command completion status string.
|
* in which to store a command completion status string.
|
||||||
|
@ -379,7 +382,9 @@ check_xact_readonly(Node *parsetree)
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ProcessUtility(Node *parsetree,
|
ProcessUtility(Node *parsetree,
|
||||||
|
const char *queryString,
|
||||||
ParamListInfo params,
|
ParamListInfo params,
|
||||||
|
bool isTopLevel,
|
||||||
DestReceiver *dest,
|
DestReceiver *dest,
|
||||||
char *completionTag)
|
char *completionTag)
|
||||||
{
|
{
|
||||||
|
@ -444,12 +449,12 @@ ProcessUtility(Node *parsetree,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TRANS_STMT_COMMIT_PREPARED:
|
case TRANS_STMT_COMMIT_PREPARED:
|
||||||
PreventTransactionChain(stmt, "COMMIT PREPARED");
|
PreventTransactionChain(isTopLevel, "COMMIT PREPARED");
|
||||||
FinishPreparedTransaction(stmt->gid, true);
|
FinishPreparedTransaction(stmt->gid, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TRANS_STMT_ROLLBACK_PREPARED:
|
case TRANS_STMT_ROLLBACK_PREPARED:
|
||||||
PreventTransactionChain(stmt, "ROLLBACK PREPARED");
|
PreventTransactionChain(isTopLevel, "ROLLBACK PREPARED");
|
||||||
FinishPreparedTransaction(stmt->gid, false);
|
FinishPreparedTransaction(stmt->gid, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -462,7 +467,7 @@ ProcessUtility(Node *parsetree,
|
||||||
ListCell *cell;
|
ListCell *cell;
|
||||||
char *name = NULL;
|
char *name = NULL;
|
||||||
|
|
||||||
RequireTransactionChain((void *) stmt, "SAVEPOINT");
|
RequireTransactionChain(isTopLevel, "SAVEPOINT");
|
||||||
|
|
||||||
foreach(cell, stmt->options)
|
foreach(cell, stmt->options)
|
||||||
{
|
{
|
||||||
|
@ -479,12 +484,12 @@ ProcessUtility(Node *parsetree,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TRANS_STMT_RELEASE:
|
case TRANS_STMT_RELEASE:
|
||||||
RequireTransactionChain((void *) stmt, "RELEASE SAVEPOINT");
|
RequireTransactionChain(isTopLevel, "RELEASE SAVEPOINT");
|
||||||
ReleaseSavepoint(stmt->options);
|
ReleaseSavepoint(stmt->options);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TRANS_STMT_ROLLBACK_TO:
|
case TRANS_STMT_ROLLBACK_TO:
|
||||||
RequireTransactionChain((void *) stmt, "ROLLBACK TO SAVEPOINT");
|
RequireTransactionChain(isTopLevel, "ROLLBACK TO SAVEPOINT");
|
||||||
RollbackToSavepoint(stmt->options);
|
RollbackToSavepoint(stmt->options);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -500,7 +505,8 @@ ProcessUtility(Node *parsetree,
|
||||||
* Portal (cursor) manipulation
|
* Portal (cursor) manipulation
|
||||||
*/
|
*/
|
||||||
case T_DeclareCursorStmt:
|
case T_DeclareCursorStmt:
|
||||||
PerformCursorOpen((DeclareCursorStmt *) parsetree, params);
|
PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
|
||||||
|
queryString, isTopLevel);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ClosePortalStmt:
|
case T_ClosePortalStmt:
|
||||||
|
@ -520,7 +526,8 @@ ProcessUtility(Node *parsetree,
|
||||||
* relation and attribute manipulation
|
* relation and attribute manipulation
|
||||||
*/
|
*/
|
||||||
case T_CreateSchemaStmt:
|
case T_CreateSchemaStmt:
|
||||||
CreateSchemaCommand((CreateSchemaStmt *) parsetree);
|
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
|
||||||
|
queryString);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_CreateStmt:
|
case T_CreateStmt:
|
||||||
|
@ -540,10 +547,12 @@ ProcessUtility(Node *parsetree,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_CreateTableSpaceStmt:
|
case T_CreateTableSpaceStmt:
|
||||||
|
PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
|
||||||
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
|
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_DropTableSpaceStmt:
|
case T_DropTableSpaceStmt:
|
||||||
|
PreventTransactionChain(isTopLevel, "DROP TABLESPACE");
|
||||||
DropTableSpace((DropTableSpaceStmt *) parsetree);
|
DropTableSpace((DropTableSpaceStmt *) parsetree);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -640,8 +649,9 @@ ProcessUtility(Node *parsetree,
|
||||||
|
|
||||||
case T_CopyStmt:
|
case T_CopyStmt:
|
||||||
{
|
{
|
||||||
uint64 processed = DoCopy((CopyStmt *) parsetree);
|
uint64 processed;
|
||||||
|
|
||||||
|
processed = DoCopy((CopyStmt *) parsetree, queryString);
|
||||||
if (completionTag)
|
if (completionTag)
|
||||||
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
|
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
|
||||||
"COPY " UINT64_FORMAT, processed);
|
"COPY " UINT64_FORMAT, processed);
|
||||||
|
@ -649,11 +659,11 @@ ProcessUtility(Node *parsetree,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_PrepareStmt:
|
case T_PrepareStmt:
|
||||||
PrepareQuery((PrepareStmt *) parsetree);
|
PrepareQuery((PrepareStmt *) parsetree, queryString);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ExecuteStmt:
|
case T_ExecuteStmt:
|
||||||
ExecuteQuery((ExecuteStmt *) parsetree, params,
|
ExecuteQuery((ExecuteStmt *) parsetree, queryString, params,
|
||||||
dest, completionTag);
|
dest, completionTag);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -770,11 +780,7 @@ ProcessUtility(Node *parsetree,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ViewStmt: /* CREATE VIEW */
|
case T_ViewStmt: /* CREATE VIEW */
|
||||||
{
|
DefineView((ViewStmt *) parsetree, queryString);
|
||||||
ViewStmt *stmt = (ViewStmt *) parsetree;
|
|
||||||
|
|
||||||
DefineView(stmt->view, stmt->query, stmt->replace);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_CreateFunctionStmt: /* CREATE FUNCTION */
|
case T_CreateFunctionStmt: /* CREATE FUNCTION */
|
||||||
|
@ -790,10 +796,15 @@ ProcessUtility(Node *parsetree,
|
||||||
IndexStmt *stmt = (IndexStmt *) parsetree;
|
IndexStmt *stmt = (IndexStmt *) parsetree;
|
||||||
|
|
||||||
if (stmt->concurrent)
|
if (stmt->concurrent)
|
||||||
PreventTransactionChain(stmt, "CREATE INDEX CONCURRENTLY");
|
PreventTransactionChain(isTopLevel,
|
||||||
|
"CREATE INDEX CONCURRENTLY");
|
||||||
|
|
||||||
CheckRelationOwnership(stmt->relation, true);
|
CheckRelationOwnership(stmt->relation, true);
|
||||||
|
|
||||||
|
/* Run parse analysis ... */
|
||||||
|
stmt = analyzeIndexStmt(stmt, queryString);
|
||||||
|
|
||||||
|
/* ... and do it */
|
||||||
DefineIndex(stmt->relation, /* relation */
|
DefineIndex(stmt->relation, /* relation */
|
||||||
stmt->idxname, /* index name */
|
stmt->idxname, /* index name */
|
||||||
InvalidOid, /* no predefined OID */
|
InvalidOid, /* no predefined OID */
|
||||||
|
@ -801,7 +812,6 @@ ProcessUtility(Node *parsetree,
|
||||||
stmt->tableSpace,
|
stmt->tableSpace,
|
||||||
stmt->indexParams, /* parameters */
|
stmt->indexParams, /* parameters */
|
||||||
(Expr *) stmt->whereClause,
|
(Expr *) stmt->whereClause,
|
||||||
stmt->rangetable,
|
|
||||||
stmt->options,
|
stmt->options,
|
||||||
stmt->unique,
|
stmt->unique,
|
||||||
stmt->primary,
|
stmt->primary,
|
||||||
|
@ -815,7 +825,7 @@ ProcessUtility(Node *parsetree,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_RuleStmt: /* CREATE RULE */
|
case T_RuleStmt: /* CREATE RULE */
|
||||||
DefineQueryRewrite((RuleStmt *) parsetree);
|
DefineRule((RuleStmt *) parsetree, queryString);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_CreateSeqStmt:
|
case T_CreateSeqStmt:
|
||||||
|
@ -850,6 +860,7 @@ ProcessUtility(Node *parsetree,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_CreatedbStmt:
|
case T_CreatedbStmt:
|
||||||
|
PreventTransactionChain(isTopLevel, "CREATE DATABASE");
|
||||||
createdb((CreatedbStmt *) parsetree);
|
createdb((CreatedbStmt *) parsetree);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -865,6 +876,7 @@ ProcessUtility(Node *parsetree,
|
||||||
{
|
{
|
||||||
DropdbStmt *stmt = (DropdbStmt *) parsetree;
|
DropdbStmt *stmt = (DropdbStmt *) parsetree;
|
||||||
|
|
||||||
|
PreventTransactionChain(isTopLevel, "DROP DATABASE");
|
||||||
dropdb(stmt->dbname, stmt->missing_ok);
|
dropdb(stmt->dbname, stmt->missing_ok);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -905,15 +917,15 @@ ProcessUtility(Node *parsetree,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ClusterStmt:
|
case T_ClusterStmt:
|
||||||
cluster((ClusterStmt *) parsetree);
|
cluster((ClusterStmt *) parsetree, isTopLevel);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_VacuumStmt:
|
case T_VacuumStmt:
|
||||||
vacuum((VacuumStmt *) parsetree, NIL);
|
vacuum((VacuumStmt *) parsetree, NIL, isTopLevel);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ExplainStmt:
|
case T_ExplainStmt:
|
||||||
ExplainQuery((ExplainStmt *) parsetree, params, dest);
|
ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_VariableSetStmt:
|
case T_VariableSetStmt:
|
||||||
|
@ -1079,6 +1091,14 @@ ProcessUtility(Node *parsetree,
|
||||||
ReindexTable(stmt->relation);
|
ReindexTable(stmt->relation);
|
||||||
break;
|
break;
|
||||||
case OBJECT_DATABASE:
|
case OBJECT_DATABASE:
|
||||||
|
/*
|
||||||
|
* This cannot run inside a user transaction block;
|
||||||
|
* if we were inside a transaction, then its commit-
|
||||||
|
* and start-transaction-command calls would not have
|
||||||
|
* the intended effect!
|
||||||
|
*/
|
||||||
|
PreventTransactionChain(isTopLevel,
|
||||||
|
"REINDEX DATABASE");
|
||||||
ReindexDatabase(stmt->name,
|
ReindexDatabase(stmt->name,
|
||||||
stmt->do_system, stmt->do_user);
|
stmt->do_system, stmt->do_user);
|
||||||
break;
|
break;
|
||||||
|
@ -1166,16 +1186,8 @@ UtilityReturnsTuples(Node *parsetree)
|
||||||
entry = FetchPreparedStatement(stmt->name, false);
|
entry = FetchPreparedStatement(stmt->name, false);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return false; /* not our business to raise error */
|
return false; /* not our business to raise error */
|
||||||
switch (ChoosePortalStrategy(entry->stmt_list))
|
if (entry->plansource->resultDesc)
|
||||||
{
|
|
||||||
case PORTAL_ONE_SELECT:
|
|
||||||
case PORTAL_ONE_RETURNING:
|
|
||||||
case PORTAL_UTIL_SELECT:
|
|
||||||
return true;
|
return true;
|
||||||
case PORTAL_MULTI_QUERY:
|
|
||||||
/* will not return tuples */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2134,7 +2146,7 @@ GetCommandLogLevel(Node *parsetree)
|
||||||
|
|
||||||
/* Look through an EXPLAIN ANALYZE to the contained stmt */
|
/* Look through an EXPLAIN ANALYZE to the contained stmt */
|
||||||
if (stmt->analyze)
|
if (stmt->analyze)
|
||||||
return GetCommandLogLevel((Node *) stmt->query);
|
return GetCommandLogLevel(stmt->query);
|
||||||
/* Plain EXPLAIN isn't so interesting */
|
/* Plain EXPLAIN isn't so interesting */
|
||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
}
|
}
|
||||||
|
@ -2245,30 +2257,21 @@ GetCommandLogLevel(Node *parsetree)
|
||||||
PrepareStmt *stmt = (PrepareStmt *) parsetree;
|
PrepareStmt *stmt = (PrepareStmt *) parsetree;
|
||||||
|
|
||||||
/* Look through a PREPARE to the contained stmt */
|
/* Look through a PREPARE to the contained stmt */
|
||||||
return GetCommandLogLevel((Node *) stmt->query);
|
lev = GetCommandLogLevel(stmt->query);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_ExecuteStmt:
|
case T_ExecuteStmt:
|
||||||
{
|
{
|
||||||
ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
|
ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
|
||||||
PreparedStatement *pstmt;
|
PreparedStatement *ps;
|
||||||
ListCell *l;
|
|
||||||
|
|
||||||
/* Look through an EXECUTE to the referenced stmt(s) */
|
/* Look through an EXECUTE to the referenced stmt */
|
||||||
|
ps = FetchPreparedStatement(stmt->name, false);
|
||||||
|
if (ps)
|
||||||
|
lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
|
||||||
|
else
|
||||||
lev = LOGSTMT_ALL;
|
lev = LOGSTMT_ALL;
|
||||||
pstmt = FetchPreparedStatement(stmt->name, false);
|
|
||||||
if (pstmt)
|
|
||||||
{
|
|
||||||
foreach(l, pstmt->stmt_list)
|
|
||||||
{
|
|
||||||
Node *substmt = (Node *) lfirst(l);
|
|
||||||
LogStmtLevel stmt_lev;
|
|
||||||
|
|
||||||
stmt_lev = GetCommandLogLevel(substmt);
|
|
||||||
lev = Min(lev, stmt_lev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# Makefile for utils/cache
|
# Makefile for utils/cache
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# IDENTIFICATION
|
||||||
# $PostgreSQL: pgsql/src/backend/utils/cache/Makefile,v 1.20 2007/01/20 17:16:13 petere Exp $
|
# $PostgreSQL: pgsql/src/backend/utils/cache/Makefile,v 1.21 2007/03/13 00:33:42 tgl Exp $
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ subdir = src/backend/utils/cache
|
||||||
top_builddir = ../../../..
|
top_builddir = ../../../..
|
||||||
include $(top_builddir)/src/Makefile.global
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o typcache.o
|
OBJS = catcache.o inval.o plancache.o relcache.o \
|
||||||
|
syscache.o lsyscache.o typcache.o
|
||||||
|
|
||||||
all: SUBSYS.o
|
all: SUBSYS.o
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,862 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* plancache.c
|
||||||
|
* Plan cache management.
|
||||||
|
*
|
||||||
|
* We can store a cached plan in either fully-planned format, or just
|
||||||
|
* parsed-and-rewritten if the caller wishes to postpone planning until
|
||||||
|
* actual parameter values are available. CachedPlanSource has the same
|
||||||
|
* contents either way, but CachedPlan contains a list of PlannedStmts
|
||||||
|
* and bare utility statements in the first case, or a list of Query nodes
|
||||||
|
* in the second case.
|
||||||
|
*
|
||||||
|
* The plan cache manager itself is principally responsible for tracking
|
||||||
|
* whether cached plans should be invalidated because of schema changes in
|
||||||
|
* the tables they depend on. When (and if) the next demand for a cached
|
||||||
|
* plan occurs, the query will be replanned. Note that this could result
|
||||||
|
* in an error, for example if a column referenced by the query is no
|
||||||
|
* longer present. The creator of a cached plan can specify whether it
|
||||||
|
* is allowable for the query to change output tupdesc on replan (this
|
||||||
|
* could happen with "SELECT *" for example) --- if so, it's up to the
|
||||||
|
* caller to notice changes and cope with them.
|
||||||
|
*
|
||||||
|
* Currently, we use only relcache invalidation events to invalidate plans.
|
||||||
|
* This means that changes such as modification of a function definition do
|
||||||
|
* not invalidate plans using the function. This is not 100% OK --- for
|
||||||
|
* example, changing a SQL function that's been inlined really ought to
|
||||||
|
* cause invalidation of the plan that it's been inlined into --- but the
|
||||||
|
* cost of tracking additional types of object seems much higher than the
|
||||||
|
* gain, so we're just ignoring them for now.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.1 2007/03/13 00:33:42 tgl Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "utils/plancache.h"
|
||||||
|
#include "executor/executor.h"
|
||||||
|
#include "optimizer/clauses.h"
|
||||||
|
#include "storage/lmgr.h"
|
||||||
|
#include "tcop/pquery.h"
|
||||||
|
#include "tcop/tcopprot.h"
|
||||||
|
#include "tcop/utility.h"
|
||||||
|
#include "utils/inval.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
|
#include "utils/resowner.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
void (*callback) ();
|
||||||
|
void *arg;
|
||||||
|
} ScanQueryWalkerContext;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Oid inval_relid;
|
||||||
|
CachedPlan *plan;
|
||||||
|
} InvalRelidContext;
|
||||||
|
|
||||||
|
|
||||||
|
static List *cached_plans_list = NIL;
|
||||||
|
|
||||||
|
static void StoreCachedPlan(CachedPlanSource *plansource, List *stmt_list,
|
||||||
|
MemoryContext plan_context);
|
||||||
|
static void AcquireExecutorLocks(List *stmt_list, bool acquire);
|
||||||
|
static void AcquirePlannerLocks(List *stmt_list, bool acquire);
|
||||||
|
static void LockRelid(Oid relid, LOCKMODE lockmode, void *arg);
|
||||||
|
static void UnlockRelid(Oid relid, LOCKMODE lockmode, void *arg);
|
||||||
|
static void ScanQueryForRelids(Query *parsetree,
|
||||||
|
void (*callback) (),
|
||||||
|
void *arg);
|
||||||
|
static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context);
|
||||||
|
static bool rowmark_member(List *rowMarks, int rt_index);
|
||||||
|
static TupleDesc ComputeResultDesc(List *stmt_list);
|
||||||
|
static void PlanCacheCallback(Datum arg, Oid relid);
|
||||||
|
static void InvalRelid(Oid relid, LOCKMODE lockmode,
|
||||||
|
InvalRelidContext *context);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InitPlanCache: initialize module during InitPostgres.
|
||||||
|
*
|
||||||
|
* All we need to do is hook into inval.c's callback list.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
InitPlanCache(void)
|
||||||
|
{
|
||||||
|
CacheRegisterRelcacheCallback(PlanCacheCallback, (Datum) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CreateCachedPlan: initially create a plan cache entry.
|
||||||
|
*
|
||||||
|
* The caller must already have successfully parsed/planned the query;
|
||||||
|
* about all that we do here is copy it into permanent storage.
|
||||||
|
*
|
||||||
|
* raw_parse_tree: output of raw_parser()
|
||||||
|
* query_string: original query text (can be NULL if not available, but
|
||||||
|
* that is discouraged because it degrades error message quality)
|
||||||
|
* commandTag: compile-time-constant tag for query, or NULL if empty query
|
||||||
|
* param_types: array of parameter type OIDs, or NULL if none
|
||||||
|
* num_params: number of parameters
|
||||||
|
* stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
|
||||||
|
* fully_planned: are we caching planner or rewriter output?
|
||||||
|
* fixed_result: TRUE to disallow changes in result tupdesc
|
||||||
|
*/
|
||||||
|
CachedPlanSource *
|
||||||
|
CreateCachedPlan(Node *raw_parse_tree,
|
||||||
|
const char *query_string,
|
||||||
|
const char *commandTag,
|
||||||
|
Oid *param_types,
|
||||||
|
int num_params,
|
||||||
|
List *stmt_list,
|
||||||
|
bool fully_planned,
|
||||||
|
bool fixed_result)
|
||||||
|
{
|
||||||
|
CachedPlanSource *plansource;
|
||||||
|
MemoryContext source_context;
|
||||||
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a dedicated memory context for the CachedPlanSource and its
|
||||||
|
* subsidiary data. We expect it can be pretty small.
|
||||||
|
*/
|
||||||
|
source_context = AllocSetContextCreate(CacheMemoryContext,
|
||||||
|
"CachedPlanSource",
|
||||||
|
ALLOCSET_SMALL_MINSIZE,
|
||||||
|
ALLOCSET_SMALL_INITSIZE,
|
||||||
|
ALLOCSET_SMALL_MAXSIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create and fill the CachedPlanSource struct within the new context.
|
||||||
|
*/
|
||||||
|
oldcxt = MemoryContextSwitchTo(source_context);
|
||||||
|
plansource = (CachedPlanSource *) palloc(sizeof(CachedPlanSource));
|
||||||
|
plansource->raw_parse_tree = copyObject(raw_parse_tree);
|
||||||
|
plansource->query_string = query_string ? pstrdup(query_string) : NULL;
|
||||||
|
plansource->commandTag = commandTag; /* no copying needed */
|
||||||
|
if (num_params > 0)
|
||||||
|
{
|
||||||
|
plansource->param_types = (Oid *) palloc(num_params * sizeof(Oid));
|
||||||
|
memcpy(plansource->param_types, param_types, num_params * sizeof(Oid));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
plansource->param_types = NULL;
|
||||||
|
plansource->num_params = num_params;
|
||||||
|
plansource->fully_planned = fully_planned;
|
||||||
|
plansource->fixed_result = fixed_result;
|
||||||
|
plansource->generation = 0; /* StoreCachedPlan will increment */
|
||||||
|
plansource->resultDesc = ComputeResultDesc(stmt_list);
|
||||||
|
plansource->plan = NULL;
|
||||||
|
plansource->context = source_context;
|
||||||
|
plansource->orig_plan = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the current output plans into the plancache entry.
|
||||||
|
*/
|
||||||
|
StoreCachedPlan(plansource, stmt_list, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we can add the entry to the list of cached plans. The List nodes
|
||||||
|
* live in CacheMemoryContext.
|
||||||
|
*/
|
||||||
|
MemoryContextSwitchTo(CacheMemoryContext);
|
||||||
|
|
||||||
|
cached_plans_list = lappend(cached_plans_list, plansource);
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
|
||||||
|
return plansource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FastCreateCachedPlan: create a plan cache entry with minimal data copying.
|
||||||
|
*
|
||||||
|
* For plans that aren't expected to live very long, the copying overhead of
|
||||||
|
* CreateCachedPlan is annoying. We provide this variant entry point in which
|
||||||
|
* the caller has already placed all the data in a suitable memory context.
|
||||||
|
* The source data and completed plan are in the same context, since this
|
||||||
|
* avoids extra copy steps during plan construction. If the query ever does
|
||||||
|
* need replanning, we'll generate a separate new CachedPlan at that time, but
|
||||||
|
* the CachedPlanSource and the initial CachedPlan share the caller-provided
|
||||||
|
* context and go away together when neither is needed any longer. (Because
|
||||||
|
* the parser and planner generate extra cruft in addition to their real
|
||||||
|
* output, this approach means that the context probably contains a bunch of
|
||||||
|
* useless junk as well as the useful trees. Hence, this method is a
|
||||||
|
* space-for-time tradeoff, which is worth making for plans expected to be
|
||||||
|
* short-lived.)
|
||||||
|
*
|
||||||
|
* raw_parse_tree, query_string, param_types, and stmt_list must reside in the
|
||||||
|
* given context, which must have adequate lifespan (recommendation: make it a
|
||||||
|
* child of CacheMemoryContext). Otherwise the API is the same as
|
||||||
|
* CreateCachedPlan.
|
||||||
|
*/
|
||||||
|
CachedPlanSource *
|
||||||
|
FastCreateCachedPlan(Node *raw_parse_tree,
|
||||||
|
char *query_string,
|
||||||
|
const char *commandTag,
|
||||||
|
Oid *param_types,
|
||||||
|
int num_params,
|
||||||
|
List *stmt_list,
|
||||||
|
bool fully_planned,
|
||||||
|
bool fixed_result,
|
||||||
|
MemoryContext context)
|
||||||
|
{
|
||||||
|
CachedPlanSource *plansource;
|
||||||
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create and fill the CachedPlanSource struct within the given context.
|
||||||
|
*/
|
||||||
|
oldcxt = MemoryContextSwitchTo(context);
|
||||||
|
plansource = (CachedPlanSource *) palloc(sizeof(CachedPlanSource));
|
||||||
|
plansource->raw_parse_tree = raw_parse_tree;
|
||||||
|
plansource->query_string = query_string;
|
||||||
|
plansource->commandTag = commandTag; /* no copying needed */
|
||||||
|
plansource->param_types = param_types;
|
||||||
|
plansource->num_params = num_params;
|
||||||
|
plansource->fully_planned = fully_planned;
|
||||||
|
plansource->fixed_result = fixed_result;
|
||||||
|
plansource->generation = 0; /* StoreCachedPlan will increment */
|
||||||
|
plansource->resultDesc = ComputeResultDesc(stmt_list);
|
||||||
|
plansource->plan = NULL;
|
||||||
|
plansource->context = context;
|
||||||
|
plansource->orig_plan = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the current output plans into the plancache entry.
|
||||||
|
*/
|
||||||
|
StoreCachedPlan(plansource, stmt_list, context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since the context is owned by the CachedPlan, advance its refcount.
|
||||||
|
*/
|
||||||
|
plansource->orig_plan = plansource->plan;
|
||||||
|
plansource->orig_plan->refcount++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we can add the entry to the list of cached plans. The List nodes
|
||||||
|
* live in CacheMemoryContext.
|
||||||
|
*/
|
||||||
|
MemoryContextSwitchTo(CacheMemoryContext);
|
||||||
|
|
||||||
|
cached_plans_list = lappend(cached_plans_list, plansource);
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
|
||||||
|
return plansource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
|
||||||
|
*
|
||||||
|
* Common subroutine for CreateCachedPlan and RevalidateCachedPlan.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
StoreCachedPlan(CachedPlanSource *plansource,
|
||||||
|
List *stmt_list,
|
||||||
|
MemoryContext plan_context)
|
||||||
|
{
|
||||||
|
CachedPlan *plan;
|
||||||
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
|
if (plan_context == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Make a dedicated memory context for the CachedPlan and its
|
||||||
|
* subsidiary data.
|
||||||
|
*/
|
||||||
|
plan_context = AllocSetContextCreate(CacheMemoryContext,
|
||||||
|
"CachedPlan",
|
||||||
|
ALLOCSET_DEFAULT_MINSIZE,
|
||||||
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
|
ALLOCSET_DEFAULT_MAXSIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy supplied data into the new context.
|
||||||
|
*/
|
||||||
|
oldcxt = MemoryContextSwitchTo(plan_context);
|
||||||
|
|
||||||
|
stmt_list = (List *) copyObject(stmt_list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Assume subsidiary data is in the given context */
|
||||||
|
oldcxt = MemoryContextSwitchTo(plan_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create and fill the CachedPlan struct within the new context.
|
||||||
|
*/
|
||||||
|
plan = (CachedPlan *) palloc(sizeof(CachedPlan));
|
||||||
|
plan->stmt_list = stmt_list;
|
||||||
|
plan->fully_planned = plansource->fully_planned;
|
||||||
|
plan->dead = false;
|
||||||
|
plan->refcount = 1; /* for the parent's link */
|
||||||
|
plan->generation = ++(plansource->generation);
|
||||||
|
plan->context = plan_context;
|
||||||
|
|
||||||
|
Assert(plansource->plan == NULL);
|
||||||
|
plansource->plan = plan;
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DropCachedPlan: destroy a cached plan.
|
||||||
|
*
|
||||||
|
* Actually this only destroys the CachedPlanSource: the referenced CachedPlan
|
||||||
|
* is released, but not destroyed until its refcount goes to zero. That
|
||||||
|
* handles the situation where DropCachedPlan is called while the plan is
|
||||||
|
* still in use.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
DropCachedPlan(CachedPlanSource *plansource)
|
||||||
|
{
|
||||||
|
/* Validity check that we were given a CachedPlanSource */
|
||||||
|
Assert(list_member_ptr(cached_plans_list, plansource));
|
||||||
|
|
||||||
|
/* Remove it from the list */
|
||||||
|
cached_plans_list = list_delete_ptr(cached_plans_list, plansource);
|
||||||
|
|
||||||
|
/* Decrement child CachePlan's refcount and drop if no longer needed */
|
||||||
|
if (plansource->plan)
|
||||||
|
ReleaseCachedPlan(plansource->plan, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If CachedPlanSource has independent storage, just drop it. Otherwise
|
||||||
|
* decrement the refcount on the CachePlan that owns the storage.
|
||||||
|
*/
|
||||||
|
if (plansource->orig_plan == NULL)
|
||||||
|
{
|
||||||
|
/* Remove the CachedPlanSource and all subsidiary data */
|
||||||
|
MemoryContextDelete(plansource->context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert(plansource->context == plansource->orig_plan->context);
|
||||||
|
ReleaseCachedPlan(plansource->orig_plan, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RevalidateCachedPlan: prepare for re-use of a previously cached plan.
|
||||||
|
*
|
||||||
|
* What we do here is re-acquire locks and rebuild the plan if necessary.
|
||||||
|
* On return, the plan is valid and we have sufficient locks to begin
|
||||||
|
* execution (or planning, if not fully_planned).
|
||||||
|
*
|
||||||
|
* On return, the refcount of the plan has been incremented; a later
|
||||||
|
* ReleaseCachedPlan() call is expected. The refcount has been reported
|
||||||
|
* to the CurrentResourceOwner if useResOwner is true.
|
||||||
|
*
|
||||||
|
* Note: if any replanning activity is required, the caller's memory context
|
||||||
|
* is used for that work.
|
||||||
|
*/
|
||||||
|
CachedPlan *
|
||||||
|
RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
|
||||||
|
{
|
||||||
|
CachedPlan *plan;
|
||||||
|
|
||||||
|
/* Validity check that we were given a CachedPlanSource */
|
||||||
|
Assert(list_member_ptr(cached_plans_list, plansource));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the plan currently appears valid, acquire locks on the referenced
|
||||||
|
* objects; then check again. We need to do it this way to cover the
|
||||||
|
* race condition that an invalidation message arrives before we get
|
||||||
|
* the lock.
|
||||||
|
*/
|
||||||
|
plan = plansource->plan;
|
||||||
|
if (plan && !plan->dead)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Plan must have positive refcount because it is referenced by
|
||||||
|
* plansource; so no need to fear it disappears under us here.
|
||||||
|
*/
|
||||||
|
Assert(plan->refcount > 0);
|
||||||
|
|
||||||
|
if (plan->fully_planned)
|
||||||
|
AcquireExecutorLocks(plan->stmt_list, true);
|
||||||
|
else
|
||||||
|
AcquirePlannerLocks(plan->stmt_list, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By now, if any invalidation has happened, PlanCacheCallback
|
||||||
|
* will have marked the plan dead.
|
||||||
|
*/
|
||||||
|
if (plan->dead)
|
||||||
|
{
|
||||||
|
/* Ooops, the race case happened. Release useless locks. */
|
||||||
|
if (plan->fully_planned)
|
||||||
|
AcquireExecutorLocks(plan->stmt_list, false);
|
||||||
|
else
|
||||||
|
AcquirePlannerLocks(plan->stmt_list, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If plan has been invalidated, unlink it from the parent and release it.
|
||||||
|
*/
|
||||||
|
if (plan && plan->dead)
|
||||||
|
{
|
||||||
|
plansource->plan = NULL;
|
||||||
|
ReleaseCachedPlan(plan, false);
|
||||||
|
plan = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a new plan if needed.
|
||||||
|
*/
|
||||||
|
if (!plan)
|
||||||
|
{
|
||||||
|
List *slist;
|
||||||
|
TupleDesc resultDesc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run parse analysis and rule rewriting. The parser tends to
|
||||||
|
* scribble on its input, so we must copy the raw parse tree to
|
||||||
|
* prevent corruption of the cache. Note that we do not use
|
||||||
|
* parse_analyze_varparams(), assuming that the caller never wants the
|
||||||
|
* parameter types to change from the original values.
|
||||||
|
*/
|
||||||
|
slist = pg_analyze_and_rewrite(copyObject(plansource->raw_parse_tree),
|
||||||
|
plansource->query_string,
|
||||||
|
plansource->param_types,
|
||||||
|
plansource->num_params);
|
||||||
|
|
||||||
|
if (plansource->fully_planned)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Generate plans for queries. Assume snapshot is not set yet
|
||||||
|
* (XXX this may be wasteful, won't all callers have done that?)
|
||||||
|
*/
|
||||||
|
slist = pg_plan_queries(slist, NULL, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check or update the result tupdesc. XXX should we use a weaker
|
||||||
|
* condition than equalTupleDescs() here?
|
||||||
|
*/
|
||||||
|
resultDesc = ComputeResultDesc(slist);
|
||||||
|
if (resultDesc == NULL && plansource->resultDesc == NULL)
|
||||||
|
{
|
||||||
|
/* OK, doesn't return tuples */
|
||||||
|
}
|
||||||
|
else if (resultDesc == NULL || plansource->resultDesc == NULL ||
|
||||||
|
!equalTupleDescs(resultDesc, plansource->resultDesc))
|
||||||
|
{
|
||||||
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
|
/* can we give a better error message? */
|
||||||
|
if (plansource->fixed_result)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cached plan must not change result type")));
|
||||||
|
oldcxt = MemoryContextSwitchTo(plansource->context);
|
||||||
|
if (resultDesc)
|
||||||
|
resultDesc = CreateTupleDescCopy(resultDesc);
|
||||||
|
if (plansource->resultDesc)
|
||||||
|
FreeTupleDesc(plansource->resultDesc);
|
||||||
|
plansource->resultDesc = resultDesc;
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the plans into the plancache entry, advancing the generation
|
||||||
|
* count.
|
||||||
|
*/
|
||||||
|
StoreCachedPlan(plansource, slist, NULL);
|
||||||
|
|
||||||
|
plan = plansource->plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Last step: flag the plan as in use by caller.
|
||||||
|
*/
|
||||||
|
if (useResOwner)
|
||||||
|
ResourceOwnerEnlargePlanCacheRefs(CurrentResourceOwner);
|
||||||
|
plan->refcount++;
|
||||||
|
if (useResOwner)
|
||||||
|
ResourceOwnerRememberPlanCacheRef(CurrentResourceOwner, plan);
|
||||||
|
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReleaseCachedPlan: release active use of a cached plan.
|
||||||
|
*
|
||||||
|
* This decrements the reference count, and frees the plan if the count
|
||||||
|
* has thereby gone to zero. If useResOwner is true, it is assumed that
|
||||||
|
* the reference count is managed by the CurrentResourceOwner.
|
||||||
|
*
|
||||||
|
* Note: useResOwner = false is used for releasing references that are in
|
||||||
|
* persistent data structures, such as the parent CachedPlanSource or a
|
||||||
|
* Portal. Transient references should be protected by a resource owner.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ReleaseCachedPlan(CachedPlan *plan, bool useResOwner)
|
||||||
|
{
|
||||||
|
if (useResOwner)
|
||||||
|
ResourceOwnerForgetPlanCacheRef(CurrentResourceOwner, plan);
|
||||||
|
Assert(plan->refcount > 0);
|
||||||
|
plan->refcount--;
|
||||||
|
if (plan->refcount == 0)
|
||||||
|
MemoryContextDelete(plan->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AcquireExecutorLocks: acquire locks needed for execution of a fully-planned
|
||||||
|
* cached plan; or release them if acquire is false.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AcquireExecutorLocks(List *stmt_list, bool acquire)
|
||||||
|
{
|
||||||
|
ListCell *lc1;
|
||||||
|
|
||||||
|
foreach(lc1, stmt_list)
|
||||||
|
{
|
||||||
|
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc1);
|
||||||
|
int rt_index;
|
||||||
|
ListCell *lc2;
|
||||||
|
|
||||||
|
Assert(!IsA(plannedstmt, Query));
|
||||||
|
if (!IsA(plannedstmt, PlannedStmt))
|
||||||
|
continue; /* Ignore utility statements */
|
||||||
|
|
||||||
|
rt_index = 0;
|
||||||
|
foreach(lc2, plannedstmt->rtable)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
|
||||||
|
LOCKMODE lockmode;
|
||||||
|
|
||||||
|
rt_index++;
|
||||||
|
|
||||||
|
if (rte->rtekind != RTE_RELATION)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acquire the appropriate type of lock on each relation OID.
|
||||||
|
* Note that we don't actually try to open the rel, and hence
|
||||||
|
* will not fail if it's been dropped entirely --- we'll just
|
||||||
|
* transiently acquire a non-conflicting lock.
|
||||||
|
*/
|
||||||
|
if (list_member_int(plannedstmt->resultRelations, rt_index))
|
||||||
|
lockmode = RowExclusiveLock;
|
||||||
|
else if (rowmark_member(plannedstmt->rowMarks, rt_index))
|
||||||
|
lockmode = RowShareLock;
|
||||||
|
else
|
||||||
|
lockmode = AccessShareLock;
|
||||||
|
|
||||||
|
if (acquire)
|
||||||
|
LockRelationOid(rte->relid, lockmode);
|
||||||
|
else
|
||||||
|
UnlockRelationOid(rte->relid, lockmode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AcquirePlannerLocks: acquire locks needed for planning and execution of a
|
||||||
|
* not-fully-planned cached plan; or release them if acquire is false.
|
||||||
|
*
|
||||||
|
* Note that we don't actually try to open the relations, and hence will not
|
||||||
|
* fail if one has been dropped entirely --- we'll just transiently acquire
|
||||||
|
* a non-conflicting lock.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AcquirePlannerLocks(List *stmt_list, bool acquire)
|
||||||
|
{
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
foreach(lc, stmt_list)
|
||||||
|
{
|
||||||
|
Query *query = (Query *) lfirst(lc);
|
||||||
|
|
||||||
|
Assert(IsA(query, Query));
|
||||||
|
if (acquire)
|
||||||
|
ScanQueryForRelids(query, LockRelid, NULL);
|
||||||
|
else
|
||||||
|
ScanQueryForRelids(query, UnlockRelid, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ScanQueryForRelids callback functions for AcquirePlannerLocks
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
LockRelid(Oid relid, LOCKMODE lockmode, void *arg)
|
||||||
|
{
|
||||||
|
LockRelationOid(relid, lockmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
UnlockRelid(Oid relid, LOCKMODE lockmode, void *arg)
|
||||||
|
{
|
||||||
|
UnlockRelationOid(relid, lockmode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ScanQueryForRelids: recursively scan one Query and apply the callback
|
||||||
|
* function to each relation OID found therein. The callback function
|
||||||
|
* takes the arguments relation OID, lockmode, pointer arg.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ScanQueryForRelids(Query *parsetree,
|
||||||
|
void (*callback) (),
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
ListCell *lc;
|
||||||
|
int rt_index;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, process RTEs of the current query level.
|
||||||
|
*/
|
||||||
|
rt_index = 0;
|
||||||
|
foreach(lc, parsetree->rtable)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
||||||
|
LOCKMODE lockmode;
|
||||||
|
|
||||||
|
rt_index++;
|
||||||
|
switch (rte->rtekind)
|
||||||
|
{
|
||||||
|
case RTE_RELATION:
|
||||||
|
/*
|
||||||
|
* Determine the lock type required for this RTE.
|
||||||
|
*/
|
||||||
|
if (rt_index == parsetree->resultRelation)
|
||||||
|
lockmode = RowExclusiveLock;
|
||||||
|
else if (rowmark_member(parsetree->rowMarks, rt_index))
|
||||||
|
lockmode = RowShareLock;
|
||||||
|
else
|
||||||
|
lockmode = AccessShareLock;
|
||||||
|
|
||||||
|
(*callback) (rte->relid, lockmode, arg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RTE_SUBQUERY:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The subquery RTE itself is all right, but we have to
|
||||||
|
* recurse to process the represented subquery.
|
||||||
|
*/
|
||||||
|
ScanQueryForRelids(rte->subquery, callback, arg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* ignore other types of RTEs */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recurse into sublink subqueries, too. But we already did the ones in
|
||||||
|
* the rtable.
|
||||||
|
*/
|
||||||
|
if (parsetree->hasSubLinks)
|
||||||
|
{
|
||||||
|
ScanQueryWalkerContext context;
|
||||||
|
|
||||||
|
context.callback = callback;
|
||||||
|
context.arg = arg;
|
||||||
|
query_tree_walker(parsetree, ScanQueryWalker,
|
||||||
|
(void *) &context,
|
||||||
|
QTW_IGNORE_RT_SUBQUERIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walker to find sublink subqueries for ScanQueryForRelids
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ScanQueryWalker(Node *node, ScanQueryWalkerContext *context)
|
||||||
|
{
|
||||||
|
if (node == NULL)
|
||||||
|
return false;
|
||||||
|
if (IsA(node, SubLink))
|
||||||
|
{
|
||||||
|
SubLink *sub = (SubLink *) node;
|
||||||
|
|
||||||
|
/* Do what we came for */
|
||||||
|
ScanQueryForRelids((Query *) sub->subselect,
|
||||||
|
context->callback, context->arg);
|
||||||
|
/* Fall through to process lefthand args of SubLink */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do NOT recurse into Query nodes, because ScanQueryForRelids
|
||||||
|
* already processed subselects of subselects for us.
|
||||||
|
*/
|
||||||
|
return expression_tree_walker(node, ScanQueryWalker,
|
||||||
|
(void *) context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rowmark_member: check whether an RT index appears in a RowMarkClause list.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
rowmark_member(List *rowMarks, int rt_index)
|
||||||
|
{
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
foreach(l, rowMarks)
|
||||||
|
{
|
||||||
|
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
|
||||||
|
|
||||||
|
if (rc->rti == rt_index)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ComputeResultDesc: given a list of either fully-planned statements or
|
||||||
|
* Queries, 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.
|
||||||
|
*/
|
||||||
|
static TupleDesc
|
||||||
|
ComputeResultDesc(List *stmt_list)
|
||||||
|
{
|
||||||
|
Node *node;
|
||||||
|
Query *query;
|
||||||
|
PlannedStmt *pstmt;
|
||||||
|
|
||||||
|
switch (ChoosePortalStrategy(stmt_list))
|
||||||
|
{
|
||||||
|
case PORTAL_ONE_SELECT:
|
||||||
|
node = (Node *) linitial(stmt_list);
|
||||||
|
if (IsA(node, Query))
|
||||||
|
{
|
||||||
|
query = (Query *) node;
|
||||||
|
return ExecCleanTypeFromTL(query->targetList, false);
|
||||||
|
}
|
||||||
|
if (IsA(node, PlannedStmt))
|
||||||
|
{
|
||||||
|
pstmt = (PlannedStmt *) node;
|
||||||
|
return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
|
||||||
|
}
|
||||||
|
/* other cases shouldn't happen, but return NULL */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PORTAL_ONE_RETURNING:
|
||||||
|
node = PortalListGetPrimaryStmt(stmt_list);
|
||||||
|
if (IsA(node, Query))
|
||||||
|
{
|
||||||
|
query = (Query *) node;
|
||||||
|
Assert(query->returningList);
|
||||||
|
return ExecCleanTypeFromTL(query->returningList, false);
|
||||||
|
}
|
||||||
|
if (IsA(node, PlannedStmt))
|
||||||
|
{
|
||||||
|
pstmt = (PlannedStmt *) node;
|
||||||
|
Assert(pstmt->returningLists);
|
||||||
|
return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
|
||||||
|
}
|
||||||
|
/* other cases shouldn't happen, but return NULL */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PORTAL_UTIL_SELECT:
|
||||||
|
node = (Node *) linitial(stmt_list);
|
||||||
|
if (IsA(node, Query))
|
||||||
|
{
|
||||||
|
query = (Query *) node;
|
||||||
|
Assert(query->utilityStmt);
|
||||||
|
return UtilityTupleDescriptor(query->utilityStmt);
|
||||||
|
}
|
||||||
|
/* else it's a bare utility statement */
|
||||||
|
return UtilityTupleDescriptor(node);
|
||||||
|
|
||||||
|
case PORTAL_MULTI_QUERY:
|
||||||
|
/* will not return tuples */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PlanCacheCallback
|
||||||
|
* Relcache inval callback function
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
PlanCacheCallback(Datum arg, Oid relid)
|
||||||
|
{
|
||||||
|
ListCell *lc1;
|
||||||
|
ListCell *lc2;
|
||||||
|
|
||||||
|
foreach(lc1, cached_plans_list)
|
||||||
|
{
|
||||||
|
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
|
||||||
|
CachedPlan *plan = plansource->plan;
|
||||||
|
|
||||||
|
/* No work if it's already invalidated */
|
||||||
|
if (!plan || plan->dead)
|
||||||
|
continue;
|
||||||
|
if (plan->fully_planned)
|
||||||
|
{
|
||||||
|
foreach(lc2, plan->stmt_list)
|
||||||
|
{
|
||||||
|
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
|
||||||
|
ListCell *lc3;
|
||||||
|
|
||||||
|
Assert(!IsA(plannedstmt, Query));
|
||||||
|
if (!IsA(plannedstmt, PlannedStmt))
|
||||||
|
continue; /* Ignore utility statements */
|
||||||
|
foreach(lc3, plannedstmt->rtable)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc3);
|
||||||
|
|
||||||
|
if (rte->rtekind != RTE_RELATION)
|
||||||
|
continue;
|
||||||
|
if (relid == rte->relid)
|
||||||
|
{
|
||||||
|
/* Invalidate the plan! */
|
||||||
|
plan->dead = true;
|
||||||
|
break; /* out of rangetable scan */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (plan->dead)
|
||||||
|
break; /* out of stmt_list scan */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* For not-fully-planned entries we use ScanQueryForRelids,
|
||||||
|
* since a recursive traversal is needed. The callback API
|
||||||
|
* is a bit tedious but avoids duplication of coding.
|
||||||
|
*/
|
||||||
|
InvalRelidContext context;
|
||||||
|
|
||||||
|
context.inval_relid = relid;
|
||||||
|
context.plan = plan;
|
||||||
|
|
||||||
|
foreach(lc2, plan->stmt_list)
|
||||||
|
{
|
||||||
|
Query *query = (Query *) lfirst(lc2);
|
||||||
|
|
||||||
|
Assert(IsA(query, Query));
|
||||||
|
ScanQueryForRelids(query, InvalRelid, (void *) &context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ScanQueryForRelids callback function for PlanCacheCallback
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
InvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context)
|
||||||
|
{
|
||||||
|
if (relid == context->inval_relid)
|
||||||
|
context->plan->dead = true;
|
||||||
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.174 2007/02/15 23:23:23 alvherre Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.175 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
|
@ -28,6 +28,7 @@
|
||||||
#include "libpq/hba.h"
|
#include "libpq/hba.h"
|
||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "pgstat.h"
|
||||||
#include "postmaster/autovacuum.h"
|
#include "postmaster/autovacuum.h"
|
||||||
#include "postmaster/postmaster.h"
|
#include "postmaster/postmaster.h"
|
||||||
#include "storage/backendid.h"
|
#include "storage/backendid.h"
|
||||||
|
@ -40,10 +41,10 @@
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
#include "utils/flatfiles.h"
|
#include "utils/flatfiles.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
|
#include "utils/plancache.h"
|
||||||
#include "utils/portal.h"
|
#include "utils/portal.h"
|
||||||
#include "utils/relcache.h"
|
#include "utils/relcache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "pgstat.h"
|
|
||||||
|
|
||||||
|
|
||||||
static bool FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace);
|
static bool FindMyDatabase(const char *name, Oid *db_id, Oid *db_tablespace);
|
||||||
|
@ -429,6 +430,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
|
||||||
*/
|
*/
|
||||||
RelationCacheInitialize();
|
RelationCacheInitialize();
|
||||||
InitCatalogCache();
|
InitCatalogCache();
|
||||||
|
InitPlanCache();
|
||||||
|
|
||||||
/* Initialize portal manager */
|
/* Initialize portal manager */
|
||||||
EnablePortalManager();
|
EnablePortalManager();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
$PostgreSQL: pgsql/src/backend/utils/mmgr/README,v 1.9 2006/09/07 22:52:01 tgl Exp $
|
$PostgreSQL: pgsql/src/backend/utils/mmgr/README,v 1.10 2007/03/13 00:33:42 tgl Exp $
|
||||||
|
|
||||||
Notes about memory allocation redesign
|
Notes about memory allocation redesign
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
@ -201,15 +201,6 @@ have dangling pointers leading to a crash at top-level commit. An example of
|
||||||
data kept here is pending NOTIFY messages, which are sent at top-level commit,
|
data kept here is pending NOTIFY messages, which are sent at top-level commit,
|
||||||
but only if the generating subtransaction did not abort.
|
but only if the generating subtransaction did not abort.
|
||||||
|
|
||||||
QueryContext --- this is not actually a separate context, but a global
|
|
||||||
variable pointing to the context that holds the current command's parse tree.
|
|
||||||
(In simple-Query mode this points to MessageContext; when executing a
|
|
||||||
prepared statement it will point to the prepared statement's private context.
|
|
||||||
Note that the plan tree may or may not be in this same context.)
|
|
||||||
Generally it is not appropriate for any code to use QueryContext as an
|
|
||||||
allocation target --- from the point of view of any code that would be
|
|
||||||
referencing the QueryContext variable, it's a read-only context.
|
|
||||||
|
|
||||||
PortalContext --- this is not actually a separate context either, but a
|
PortalContext --- this is not actually a separate context either, but a
|
||||||
global variable pointing to the per-portal context of the currently active
|
global variable pointing to the per-portal context of the currently active
|
||||||
execution portal. This can be used if it's necessary to allocate storage
|
execution portal. This can be used if it's necessary to allocate storage
|
||||||
|
@ -229,9 +220,7 @@ Contexts for prepared statements and portals
|
||||||
A prepared-statement object has an associated private context, in which
|
A prepared-statement object has an associated private context, in which
|
||||||
the parse and plan trees for its query are stored. Because these trees
|
the parse and plan trees for its query are stored. Because these trees
|
||||||
are read-only to the executor, the prepared statement can be re-used many
|
are read-only to the executor, the prepared statement can be re-used many
|
||||||
times without further copying of these trees. QueryContext points at this
|
times without further copying of these trees.
|
||||||
private context while executing any portal built from the prepared
|
|
||||||
statement.
|
|
||||||
|
|
||||||
An execution-portal object has a private context that is referenced by
|
An execution-portal object has a private context that is referenced by
|
||||||
PortalContext when the portal is active. In the case of a portal created
|
PortalContext when the portal is active. In the case of a portal created
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/mmgr/mcxt.c,v 1.59 2007/01/05 22:19:47 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/mmgr/mcxt.c,v 1.60 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -46,8 +46,7 @@ MemoryContext MessageContext = NULL;
|
||||||
MemoryContext TopTransactionContext = NULL;
|
MemoryContext TopTransactionContext = NULL;
|
||||||
MemoryContext CurTransactionContext = NULL;
|
MemoryContext CurTransactionContext = NULL;
|
||||||
|
|
||||||
/* These two are transient links to contexts owned by other objects: */
|
/* This is a transient link to the active portal's memory context: */
|
||||||
MemoryContext QueryContext = NULL;
|
|
||||||
MemoryContext PortalContext = NULL;
|
MemoryContext PortalContext = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.99 2007/02/20 17:32:17 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.100 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -149,9 +149,9 @@ GetPortalByName(const char *name)
|
||||||
* cases should occur in present usages of this function.
|
* cases should occur in present usages of this function.
|
||||||
*
|
*
|
||||||
* Copes if given a list of Querys --- can't happen in a portal, but this
|
* Copes if given a list of Querys --- can't happen in a portal, but this
|
||||||
* code also supports prepared statements, which need both cases.
|
* code also supports plancache.c, which needs both cases.
|
||||||
*
|
*
|
||||||
* Note: the reason this is just handed a List is so that prepared statements
|
* Note: the reason this is just handed a List is so that plancache.c
|
||||||
* can share the code. For use with a portal, use PortalGetPrimaryStmt
|
* can share the code. For use with a portal, use PortalGetPrimaryStmt
|
||||||
* rather than calling this directly.
|
* rather than calling this directly.
|
||||||
*/
|
*/
|
||||||
|
@ -275,9 +275,17 @@ CreateNewPortal(void)
|
||||||
*
|
*
|
||||||
* Notes: commandTag shall be NULL if and only if the original query string
|
* Notes: commandTag shall be NULL if and only if the original query string
|
||||||
* (before rewriting) was an empty string. Also, the passed commandTag must
|
* (before rewriting) was an empty string. Also, the passed commandTag must
|
||||||
* be a pointer to a constant string, since it is not copied. The caller is
|
* be a pointer to a constant string, since it is not copied. However,
|
||||||
* responsible for ensuring that the passed prepStmtName (if any), sourceText
|
* prepStmtName and sourceText, if provided, are copied into the portal's
|
||||||
* (if any), and plan trees have adequate lifetime.
|
* heap context for safekeeping.
|
||||||
|
*
|
||||||
|
* If cplan is provided, then it is a cached plan containing the stmts,
|
||||||
|
* and the caller must have done RevalidateCachedPlan(), causing a refcount
|
||||||
|
* increment. The refcount will be released when the portal is destroyed.
|
||||||
|
*
|
||||||
|
* If cplan is NULL, then it is the caller's responsibility to ensure that
|
||||||
|
* the passed plan trees have adequate lifetime. Typically this is done by
|
||||||
|
* copying them into the portal's heap context.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
PortalDefineQuery(Portal portal,
|
PortalDefineQuery(Portal portal,
|
||||||
|
@ -285,18 +293,35 @@ PortalDefineQuery(Portal portal,
|
||||||
const char *sourceText,
|
const char *sourceText,
|
||||||
const char *commandTag,
|
const char *commandTag,
|
||||||
List *stmts,
|
List *stmts,
|
||||||
MemoryContext queryContext)
|
CachedPlan *cplan)
|
||||||
{
|
{
|
||||||
AssertArg(PortalIsValid(portal));
|
AssertArg(PortalIsValid(portal));
|
||||||
AssertState(portal->queryContext == NULL); /* else defined already */
|
AssertState(portal->status == PORTAL_NEW);
|
||||||
|
|
||||||
Assert(commandTag != NULL || stmts == NIL);
|
Assert(commandTag != NULL || stmts == NIL);
|
||||||
|
|
||||||
portal->prepStmtName = prepStmtName;
|
portal->prepStmtName = prepStmtName ?
|
||||||
portal->sourceText = sourceText;
|
MemoryContextStrdup(PortalGetHeapMemory(portal), prepStmtName) : NULL;
|
||||||
|
portal->sourceText = sourceText ?
|
||||||
|
MemoryContextStrdup(PortalGetHeapMemory(portal), sourceText) : NULL;
|
||||||
portal->commandTag = commandTag;
|
portal->commandTag = commandTag;
|
||||||
portal->stmts = stmts;
|
portal->stmts = stmts;
|
||||||
portal->queryContext = queryContext;
|
portal->cplan = cplan;
|
||||||
|
portal->status = PORTAL_DEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PortalReleaseCachedPlan
|
||||||
|
* Release a portal's reference to its cached plan, if any.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
PortalReleaseCachedPlan(Portal portal)
|
||||||
|
{
|
||||||
|
if (portal->cplan)
|
||||||
|
{
|
||||||
|
ReleaseCachedPlan(portal->cplan, false);
|
||||||
|
portal->cplan = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -356,6 +381,10 @@ PortalDrop(Portal portal, bool isTopCommit)
|
||||||
if (PointerIsValid(portal->cleanup))
|
if (PointerIsValid(portal->cleanup))
|
||||||
(*portal->cleanup) (portal);
|
(*portal->cleanup) (portal);
|
||||||
|
|
||||||
|
/* drop cached plan reference, if any */
|
||||||
|
if (portal->cplan)
|
||||||
|
PortalReleaseCachedPlan(portal);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Release any resources still attached to the portal. There are several
|
* Release any resources still attached to the portal. There are several
|
||||||
* cases being covered here:
|
* cases being covered here:
|
||||||
|
@ -423,29 +452,6 @@ PortalDrop(Portal portal, bool isTopCommit)
|
||||||
pfree(portal);
|
pfree(portal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* DropDependentPortals
|
|
||||||
* Drop any portals using the specified context as queryContext.
|
|
||||||
*
|
|
||||||
* This is normally used to make sure we can safely drop a prepared statement.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
DropDependentPortals(MemoryContext queryContext)
|
|
||||||
{
|
|
||||||
HASH_SEQ_STATUS status;
|
|
||||||
PortalHashEnt *hentry;
|
|
||||||
|
|
||||||
hash_seq_init(&status, PortalHashTable);
|
|
||||||
|
|
||||||
while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
|
|
||||||
{
|
|
||||||
Portal portal = hentry->portal;
|
|
||||||
|
|
||||||
if (portal->queryContext == queryContext)
|
|
||||||
PortalDrop(portal, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pre-commit processing for portals.
|
* Pre-commit processing for portals.
|
||||||
|
@ -485,6 +491,10 @@ CommitHoldablePortals(void)
|
||||||
PortalCreateHoldStore(portal);
|
PortalCreateHoldStore(portal);
|
||||||
PersistHoldablePortal(portal);
|
PersistHoldablePortal(portal);
|
||||||
|
|
||||||
|
/* drop cached plan reference, if any */
|
||||||
|
if (portal->cplan)
|
||||||
|
PortalReleaseCachedPlan(portal);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Any resources belonging to the portal will be released in the
|
* Any resources belonging to the portal will be released in the
|
||||||
* upcoming transaction-wide cleanup; the portal will no longer
|
* upcoming transaction-wide cleanup; the portal will no longer
|
||||||
|
@ -630,6 +640,10 @@ AtAbort_Portals(void)
|
||||||
portal->cleanup = NULL;
|
portal->cleanup = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* drop cached plan reference, if any */
|
||||||
|
if (portal->cplan)
|
||||||
|
PortalReleaseCachedPlan(portal);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Any resources belonging to the portal will be released in the
|
* Any resources belonging to the portal will be released in the
|
||||||
* upcoming transaction-wide cleanup; they will be gone before we run
|
* upcoming transaction-wide cleanup; they will be gone before we run
|
||||||
|
@ -769,6 +783,10 @@ AtSubAbort_Portals(SubTransactionId mySubid,
|
||||||
portal->cleanup = NULL;
|
portal->cleanup = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* drop cached plan reference, if any */
|
||||||
|
if (portal->cplan)
|
||||||
|
PortalReleaseCachedPlan(portal);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Any resources belonging to the portal will be released in the
|
* Any resources belonging to the portal will be released in the
|
||||||
* upcoming transaction-wide cleanup; they will be gone before we
|
* upcoming transaction-wide cleanup; they will be gone before we
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.4 2006/06/16 18:42:23 tgl Exp $
|
$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.5 2007/03/13 00:33:42 tgl Exp $
|
||||||
|
|
||||||
Notes about resource owners
|
Notes about resource owners
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -60,12 +60,13 @@ subtransaction or portal. Therefore, the "release" operation on a child
|
||||||
ResourceOwner transfers lock ownership to the parent instead of actually
|
ResourceOwner transfers lock ownership to the parent instead of actually
|
||||||
releasing the lock, if isCommit is true.
|
releasing the lock, if isCommit is true.
|
||||||
|
|
||||||
Currently, ResourceOwners contain direct support for recording ownership
|
Currently, ResourceOwners contain direct support for recording ownership of
|
||||||
of buffer pins, lmgr locks, and catcache, relcache, and tupdesc references.
|
buffer pins, lmgr locks, and catcache, relcache, plancache, and tupdesc
|
||||||
Other objects can be associated with a ResourceOwner by recording the address
|
references. Other objects can be associated with a ResourceOwner by recording
|
||||||
of the owning ResourceOwner in such an object. There is an API for other
|
the address of the owning ResourceOwner in such an object. There is an API
|
||||||
modules to get control during ResourceOwner release, so that they can scan
|
for other modules to get control during ResourceOwner release, so that they
|
||||||
their own data structures to find the objects that need to be deleted.
|
can scan their own data structures to find the objects that need to be
|
||||||
|
deleted.
|
||||||
|
|
||||||
Whenever we are inside a transaction, the global variable
|
Whenever we are inside a transaction, the global variable
|
||||||
CurrentResourceOwner shows which resource owner should be assigned
|
CurrentResourceOwner shows which resource owner should be assigned
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.23 2007/01/05 22:19:47 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.24 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -56,6 +56,11 @@ typedef struct ResourceOwnerData
|
||||||
Relation *relrefs; /* dynamically allocated array */
|
Relation *relrefs; /* dynamically allocated array */
|
||||||
int maxrelrefs; /* currently allocated array size */
|
int maxrelrefs; /* currently allocated array size */
|
||||||
|
|
||||||
|
/* We have built-in support for remembering plancache references */
|
||||||
|
int nplanrefs; /* number of owned plancache pins */
|
||||||
|
CachedPlan **planrefs; /* dynamically allocated array */
|
||||||
|
int maxplanrefs; /* currently allocated array size */
|
||||||
|
|
||||||
/* We have built-in support for remembering tupdesc references */
|
/* We have built-in support for remembering tupdesc references */
|
||||||
int ntupdescs; /* number of owned tupdesc references */
|
int ntupdescs; /* number of owned tupdesc references */
|
||||||
TupleDesc *tupdescs; /* dynamically allocated array */
|
TupleDesc *tupdescs; /* dynamically allocated array */
|
||||||
|
@ -90,6 +95,7 @@ static void ResourceOwnerReleaseInternal(ResourceOwner owner,
|
||||||
bool isCommit,
|
bool isCommit,
|
||||||
bool isTopLevel);
|
bool isTopLevel);
|
||||||
static void PrintRelCacheLeakWarning(Relation rel);
|
static void PrintRelCacheLeakWarning(Relation rel);
|
||||||
|
static void PrintPlanCacheLeakWarning(CachedPlan *plan);
|
||||||
static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
|
static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
|
||||||
|
|
||||||
|
|
||||||
|
@ -280,6 +286,13 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
|
||||||
PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
|
PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
|
||||||
ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
|
ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
|
||||||
}
|
}
|
||||||
|
/* Ditto for plancache references */
|
||||||
|
while (owner->nplanrefs > 0)
|
||||||
|
{
|
||||||
|
if (isCommit)
|
||||||
|
PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
|
||||||
|
ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
|
||||||
|
}
|
||||||
/* Ditto for tupdesc references */
|
/* Ditto for tupdesc references */
|
||||||
while (owner->ntupdescs > 0)
|
while (owner->ntupdescs > 0)
|
||||||
{
|
{
|
||||||
|
@ -316,6 +329,7 @@ ResourceOwnerDelete(ResourceOwner owner)
|
||||||
Assert(owner->ncatrefs == 0);
|
Assert(owner->ncatrefs == 0);
|
||||||
Assert(owner->ncatlistrefs == 0);
|
Assert(owner->ncatlistrefs == 0);
|
||||||
Assert(owner->nrelrefs == 0);
|
Assert(owner->nrelrefs == 0);
|
||||||
|
Assert(owner->nplanrefs == 0);
|
||||||
Assert(owner->ntupdescs == 0);
|
Assert(owner->ntupdescs == 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -341,6 +355,8 @@ ResourceOwnerDelete(ResourceOwner owner)
|
||||||
pfree(owner->catlistrefs);
|
pfree(owner->catlistrefs);
|
||||||
if (owner->relrefs)
|
if (owner->relrefs)
|
||||||
pfree(owner->relrefs);
|
pfree(owner->relrefs);
|
||||||
|
if (owner->planrefs)
|
||||||
|
pfree(owner->planrefs);
|
||||||
if (owner->tupdescs)
|
if (owner->tupdescs)
|
||||||
pfree(owner->tupdescs);
|
pfree(owner->tupdescs);
|
||||||
|
|
||||||
|
@ -758,6 +774,86 @@ PrintRelCacheLeakWarning(Relation rel)
|
||||||
RelationGetRelationName(rel));
|
RelationGetRelationName(rel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure there is room for at least one more entry in a ResourceOwner's
|
||||||
|
* plancache reference array.
|
||||||
|
*
|
||||||
|
* This is separate from actually inserting an entry because if we run out
|
||||||
|
* of memory, it's critical to do so *before* acquiring the resource.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
|
||||||
|
{
|
||||||
|
int newmax;
|
||||||
|
|
||||||
|
if (owner->nplanrefs < owner->maxplanrefs)
|
||||||
|
return; /* nothing to do */
|
||||||
|
|
||||||
|
if (owner->planrefs == NULL)
|
||||||
|
{
|
||||||
|
newmax = 16;
|
||||||
|
owner->planrefs = (CachedPlan **)
|
||||||
|
MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
|
||||||
|
owner->maxplanrefs = newmax;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newmax = owner->maxplanrefs * 2;
|
||||||
|
owner->planrefs = (CachedPlan **)
|
||||||
|
repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
|
||||||
|
owner->maxplanrefs = newmax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remember that a plancache reference is owned by a ResourceOwner
|
||||||
|
*
|
||||||
|
* Caller must have previously done ResourceOwnerEnlargePlanCacheRefs()
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
|
||||||
|
{
|
||||||
|
Assert(owner->nplanrefs < owner->maxplanrefs);
|
||||||
|
owner->planrefs[owner->nplanrefs] = plan;
|
||||||
|
owner->nplanrefs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forget that a plancache reference is owned by a ResourceOwner
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
|
||||||
|
{
|
||||||
|
CachedPlan **planrefs = owner->planrefs;
|
||||||
|
int np1 = owner->nplanrefs - 1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = np1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (planrefs[i] == plan)
|
||||||
|
{
|
||||||
|
while (i < np1)
|
||||||
|
{
|
||||||
|
planrefs[i] = planrefs[i + 1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
owner->nplanrefs = np1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elog(ERROR, "plancache reference %p is not owned by resource owner %s",
|
||||||
|
plan, owner->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debugging subroutine
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
PrintPlanCacheLeakWarning(CachedPlan *plan)
|
||||||
|
{
|
||||||
|
elog(WARNING, "plancache reference leak: plan %p not closed", plan);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure there is room for at least one more entry in a ResourceOwner's
|
* Make sure there is room for at least one more entry in a ResourceOwner's
|
||||||
* tupdesc reference array.
|
* tupdesc reference array.
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.84 2007/01/05 22:19:51 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.85 2007/03/13 00:33:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -164,9 +164,9 @@ extern bool IsTransactionBlock(void);
|
||||||
extern bool IsTransactionOrTransactionBlock(void);
|
extern bool IsTransactionOrTransactionBlock(void);
|
||||||
extern char TransactionBlockStatusCode(void);
|
extern char TransactionBlockStatusCode(void);
|
||||||
extern void AbortOutOfAnyTransaction(void);
|
extern void AbortOutOfAnyTransaction(void);
|
||||||
extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
|
extern void PreventTransactionChain(bool isTopLevel, const char *stmtType);
|
||||||
extern void RequireTransactionChain(void *stmtNode, const char *stmtType);
|
extern void RequireTransactionChain(bool isTopLevel, const char *stmtType);
|
||||||
extern bool IsInTransactionChain(void *stmtNode);
|
extern bool IsInTransactionChain(bool isTopLevel);
|
||||||
extern void RegisterXactCallback(XactCallback callback, void *arg);
|
extern void RegisterXactCallback(XactCallback callback, void *arg);
|
||||||
extern void UnregisterXactCallback(XactCallback callback, void *arg);
|
extern void UnregisterXactCallback(XactCallback callback, void *arg);
|
||||||
extern void RegisterSubXactCallback(SubXactCallback callback, void *arg);
|
extern void RegisterSubXactCallback(SubXactCallback callback, void *arg);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.31 2007/01/05 22:19:53 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.32 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
|
||||||
|
|
||||||
extern void cluster(ClusterStmt *stmt);
|
extern void cluster(ClusterStmt *stmt, bool isTopLevel);
|
||||||
|
|
||||||
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
|
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
|
||||||
bool recheck);
|
bool recheck);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.29 2007/01/05 22:19:53 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.30 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
#include "tcop/dest.h"
|
#include "tcop/dest.h"
|
||||||
|
|
||||||
|
|
||||||
extern uint64 DoCopy(const CopyStmt *stmt);
|
extern uint64 DoCopy(const CopyStmt *stmt, const char *queryString);
|
||||||
|
|
||||||
extern DestReceiver *CreateCopyDestReceiver(void);
|
extern DestReceiver *CreateCopyDestReceiver(void);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.80 2007/01/23 05:07:18 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.81 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -25,7 +25,6 @@ extern void DefineIndex(RangeVar *heapRelation,
|
||||||
char *tableSpaceName,
|
char *tableSpaceName,
|
||||||
List *attributeList,
|
List *attributeList,
|
||||||
Expr *predicate,
|
Expr *predicate,
|
||||||
List *rangetable,
|
|
||||||
List *options,
|
List *options,
|
||||||
bool unique,
|
bool unique,
|
||||||
bool primary,
|
bool primary,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/explain.h,v 1.29 2007/01/05 22:19:53 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/explain.h,v 1.30 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -16,11 +16,16 @@
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
|
|
||||||
|
|
||||||
extern void ExplainQuery(ExplainStmt *stmt, ParamListInfo params,
|
extern void ExplainQuery(ExplainStmt *stmt, const char *queryString,
|
||||||
DestReceiver *dest);
|
ParamListInfo params, DestReceiver *dest);
|
||||||
|
|
||||||
extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
|
extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
|
||||||
|
|
||||||
|
extern void ExplainOneUtility(Node *utilityStmt, ExplainStmt *stmt,
|
||||||
|
const char *queryString,
|
||||||
|
ParamListInfo params,
|
||||||
|
TupOutputState *tstate);
|
||||||
|
|
||||||
extern void ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
extern void ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
||||||
TupOutputState *tstate);
|
TupOutputState *tstate);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.21 2007/02/20 17:32:17 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/commands/portalcmds.h,v 1.22 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -18,7 +18,8 @@
|
||||||
#include "utils/portal.h"
|
#include "utils/portal.h"
|
||||||
|
|
||||||
|
|
||||||
extern void PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params);
|
extern void PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params,
|
||||||
|
const char *queryString, bool isTopLevel);
|
||||||
|
|
||||||
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
|
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
|
||||||
char *completionTag);
|
char *completionTag);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.24 2007/02/20 17:32:17 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.25 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -14,58 +14,49 @@
|
||||||
#define PREPARE_H
|
#define PREPARE_H
|
||||||
|
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
|
#include "utils/plancache.h"
|
||||||
#include "utils/timestamp.h"
|
#include "utils/timestamp.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The data structure representing a prepared statement
|
* The data structure representing a prepared statement. This is now just
|
||||||
|
* a thin veneer over a plancache entry --- the main addition is that of
|
||||||
|
* a name.
|
||||||
*
|
*
|
||||||
* A prepared statement might be fully planned, or only parsed-and-rewritten.
|
* Note: all subsidiary storage lives in the referenced plancache entry.
|
||||||
* If fully planned, stmt_list contains PlannedStmts and/or utility statements;
|
|
||||||
* if not, it contains Query nodes.
|
|
||||||
*
|
|
||||||
* Note: all subsidiary storage lives in the context denoted by the context
|
|
||||||
* field. However, the string referenced by commandTag is not subsidiary
|
|
||||||
* storage; it is assumed to be a compile-time-constant string. As with
|
|
||||||
* portals, commandTag shall be NULL if and only if the original query string
|
|
||||||
* (before rewriting) was an empty string.
|
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/* dynahash.c requires key to be first field */
|
/* dynahash.c requires key to be first field */
|
||||||
char stmt_name[NAMEDATALEN];
|
char stmt_name[NAMEDATALEN];
|
||||||
char *query_string; /* text of query, or NULL */
|
CachedPlanSource *plansource; /* the actual cached plan */
|
||||||
const char *commandTag; /* command tag (a constant!), or NULL */
|
|
||||||
List *stmt_list; /* list of statement or Query nodes */
|
|
||||||
List *argtype_list; /* list of parameter type OIDs */
|
|
||||||
bool fully_planned; /* what is in stmt_list, exactly? */
|
|
||||||
bool from_sql; /* prepared via SQL, not FE/BE protocol? */
|
bool from_sql; /* prepared via SQL, not FE/BE protocol? */
|
||||||
TimestampTz prepare_time; /* the time when the stmt was prepared */
|
TimestampTz prepare_time; /* the time when the stmt was prepared */
|
||||||
MemoryContext context; /* context containing this query */
|
|
||||||
} PreparedStatement;
|
} PreparedStatement;
|
||||||
|
|
||||||
|
|
||||||
/* Utility statements PREPARE, EXECUTE, DEALLOCATE, EXPLAIN EXECUTE */
|
/* Utility statements PREPARE, EXECUTE, DEALLOCATE, EXPLAIN EXECUTE */
|
||||||
extern void PrepareQuery(PrepareStmt *stmt);
|
extern void PrepareQuery(PrepareStmt *stmt, const char *queryString);
|
||||||
extern void ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
|
extern void ExecuteQuery(ExecuteStmt *stmt, const char *queryString,
|
||||||
|
ParamListInfo params,
|
||||||
DestReceiver *dest, char *completionTag);
|
DestReceiver *dest, char *completionTag);
|
||||||
extern void DeallocateQuery(DeallocateStmt *stmt);
|
extern void DeallocateQuery(DeallocateStmt *stmt);
|
||||||
extern void ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
|
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
|
||||||
TupOutputState *tstate);
|
const char *queryString,
|
||||||
|
ParamListInfo params, TupOutputState *tstate);
|
||||||
|
|
||||||
/* Low-level access to stored prepared statements */
|
/* Low-level access to stored prepared statements */
|
||||||
extern void StorePreparedStatement(const char *stmt_name,
|
extern void StorePreparedStatement(const char *stmt_name,
|
||||||
|
Node *raw_parse_tree,
|
||||||
const char *query_string,
|
const char *query_string,
|
||||||
const char *commandTag,
|
const char *commandTag,
|
||||||
|
Oid *param_types,
|
||||||
|
int num_params,
|
||||||
List *stmt_list,
|
List *stmt_list,
|
||||||
List *argtype_list,
|
|
||||||
bool fully_planned,
|
|
||||||
bool from_sql);
|
bool from_sql);
|
||||||
extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
|
extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
|
||||||
bool throwError);
|
bool throwError);
|
||||||
extern void DropPreparedStatement(const char *stmt_name, bool showError);
|
extern void DropPreparedStatement(const char *stmt_name, bool showError);
|
||||||
extern List *FetchPreparedStatementParams(const char *stmt_name);
|
|
||||||
extern TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt);
|
extern TupleDesc FetchPreparedStatementResultDesc(PreparedStatement *stmt);
|
||||||
extern bool PreparedStatementReturnsTuples(PreparedStatement *stmt);
|
|
||||||
extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
|
extern List *FetchPreparedStatementTargetList(PreparedStatement *stmt);
|
||||||
|
|
||||||
#endif /* PREPARE_H */
|
#endif /* PREPARE_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/schemacmds.h,v 1.15 2007/01/05 22:19:54 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/schemacmds.h,v 1.16 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,8 @@
|
||||||
|
|
||||||
#include "nodes/parsenodes.h"
|
#include "nodes/parsenodes.h"
|
||||||
|
|
||||||
extern void CreateSchemaCommand(CreateSchemaStmt *parsetree);
|
extern void CreateSchemaCommand(CreateSchemaStmt *parsetree,
|
||||||
|
const char *queryString);
|
||||||
|
|
||||||
extern void RemoveSchema(List *names, DropBehavior behavior, bool missing_ok);
|
extern void RemoveSchema(List *names, DropBehavior behavior, bool missing_ok);
|
||||||
extern void RemoveSchemaById(Oid schemaOid);
|
extern void RemoveSchemaById(Oid schemaOid);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.69 2007/01/05 22:19:54 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.70 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -110,7 +110,7 @@ extern int vacuum_freeze_min_age;
|
||||||
|
|
||||||
|
|
||||||
/* in commands/vacuum.c */
|
/* in commands/vacuum.c */
|
||||||
extern void vacuum(VacuumStmt *vacstmt, List *relids);
|
extern void vacuum(VacuumStmt *vacstmt, List *relids, bool isTopLevel);
|
||||||
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
|
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
|
||||||
int *nindexes, Relation **Irel);
|
int *nindexes, Relation **Irel);
|
||||||
extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);
|
extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/view.h,v 1.24 2007/01/05 22:19:54 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/view.h,v 1.25 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
#include "nodes/parsenodes.h"
|
#include "nodes/parsenodes.h"
|
||||||
|
|
||||||
extern void DefineView(RangeVar *view, Query *view_parse, bool replace);
|
extern void DefineView(ViewStmt *stmt, const char *queryString);
|
||||||
extern void RemoveView(const RangeVar *view, DropBehavior behavior);
|
extern void RemoveView(const RangeVar *view, DropBehavior behavior);
|
||||||
|
|
||||||
#endif /* VIEW_H */
|
#endif /* VIEW_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.34 2007/01/05 22:19:55 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.35 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -82,4 +82,7 @@ typedef struct ParamExecData
|
||||||
/* Functions found in src/backend/nodes/params.c */
|
/* Functions found in src/backend/nodes/params.c */
|
||||||
extern ParamListInfo copyParamList(ParamListInfo from);
|
extern ParamListInfo copyParamList(ParamListInfo from);
|
||||||
|
|
||||||
|
extern void getParamListTypes(ParamListInfo params,
|
||||||
|
Oid **param_types, int *num_params);
|
||||||
|
|
||||||
#endif /* PARAMS_H */
|
#endif /* PARAMS_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.341 2007/02/20 17:32:17 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.342 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1021,15 +1021,14 @@ typedef struct GrantRoleStmt
|
||||||
*
|
*
|
||||||
* We support "COPY relation FROM file", "COPY relation TO file", and
|
* We support "COPY relation FROM file", "COPY relation TO file", and
|
||||||
* "COPY (query) TO file". In any given CopyStmt, exactly one of "relation"
|
* "COPY (query) TO file". In any given CopyStmt, exactly one of "relation"
|
||||||
* and "query" must be non-NULL. Note: "query" is a SelectStmt before
|
* and "query" must be non-NULL.
|
||||||
* parse analysis, and a Query afterwards.
|
|
||||||
* ----------------------
|
* ----------------------
|
||||||
*/
|
*/
|
||||||
typedef struct CopyStmt
|
typedef struct CopyStmt
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
RangeVar *relation; /* the relation to copy */
|
RangeVar *relation; /* the relation to copy */
|
||||||
Query *query; /* the query to copy */
|
Node *query; /* the SELECT query to copy */
|
||||||
List *attlist; /* List of column names (as Strings), or NIL
|
List *attlist; /* List of column names (as Strings), or NIL
|
||||||
* for all columns */
|
* for all columns */
|
||||||
bool is_from; /* TO or FROM */
|
bool is_from; /* TO or FROM */
|
||||||
|
@ -1487,8 +1486,6 @@ typedef struct IndexStmt
|
||||||
List *indexParams; /* a list of IndexElem */
|
List *indexParams; /* a list of IndexElem */
|
||||||
List *options; /* options from WITH clause */
|
List *options; /* options from WITH clause */
|
||||||
Node *whereClause; /* qualification (partial-index predicate) */
|
Node *whereClause; /* qualification (partial-index predicate) */
|
||||||
List *rangetable; /* range table for qual and/or expressions,
|
|
||||||
* filled in by transformStmt() */
|
|
||||||
bool unique; /* is index unique? */
|
bool unique; /* is index unique? */
|
||||||
bool primary; /* is index on primary key? */
|
bool primary; /* is index on primary key? */
|
||||||
bool isconstraint; /* is it from a CONSTRAINT clause? */
|
bool isconstraint; /* is it from a CONSTRAINT clause? */
|
||||||
|
@ -1713,7 +1710,7 @@ typedef struct ViewStmt
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
RangeVar *view; /* the view to be created */
|
RangeVar *view; /* the view to be created */
|
||||||
List *aliases; /* target column names */
|
List *aliases; /* target column names */
|
||||||
Query *query; /* the SQL statement */
|
Node *query; /* the SELECT query */
|
||||||
bool replace; /* replace an existing view? */
|
bool replace; /* replace an existing view? */
|
||||||
} ViewStmt;
|
} ViewStmt;
|
||||||
|
|
||||||
|
@ -1805,7 +1802,7 @@ typedef struct VacuumStmt
|
||||||
typedef struct ExplainStmt
|
typedef struct ExplainStmt
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
Query *query; /* the query */
|
Node *query; /* the query (as a raw parse tree) */
|
||||||
bool verbose; /* print plan info */
|
bool verbose; /* print plan info */
|
||||||
bool analyze; /* get statistics by executing plan */
|
bool analyze; /* get statistics by executing plan */
|
||||||
} ExplainStmt;
|
} ExplainStmt;
|
||||||
|
@ -1940,9 +1937,8 @@ typedef struct PrepareStmt
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
char *name; /* Name of plan, arbitrary */
|
char *name; /* Name of plan, arbitrary */
|
||||||
List *argtypes; /* Types of parameters (TypeNames) */
|
List *argtypes; /* Types of parameters (List of TypeName) */
|
||||||
List *argtype_oids; /* Types of parameters (OIDs) */
|
Node *query; /* The query itself (as a raw parsetree) */
|
||||||
Query *query; /* The query itself */
|
|
||||||
} PrepareStmt;
|
} PrepareStmt;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.35 2007/01/05 22:19:56 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.36 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -21,6 +21,10 @@ extern List *parse_analyze(Node *parseTree, const char *sourceText,
|
||||||
extern List *parse_analyze_varparams(Node *parseTree, const char *sourceText,
|
extern List *parse_analyze_varparams(Node *parseTree, const char *sourceText,
|
||||||
Oid **paramTypes, int *numParams);
|
Oid **paramTypes, int *numParams);
|
||||||
extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
|
extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
|
||||||
|
|
||||||
|
extern IndexStmt *analyzeIndexStmt(IndexStmt *stmt, const char *queryString);
|
||||||
|
extern void analyzeRuleStmt(RuleStmt *stmt, const char *queryString,
|
||||||
|
List **actions, Node **whereClause);
|
||||||
extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
|
extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
|
||||||
extern void CheckSelectLocking(Query *qry);
|
extern void CheckSelectLocking(Query *qry);
|
||||||
extern void applyLockingClause(Query *qry, Index rtindex,
|
extern void applyLockingClause(Query *qry, Index rtindex,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/rewrite/rewriteDefine.h,v 1.23 2007/01/05 22:19:57 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/rewrite/rewriteDefine.h,v 1.24 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +16,15 @@
|
||||||
|
|
||||||
#include "nodes/parsenodes.h"
|
#include "nodes/parsenodes.h"
|
||||||
|
|
||||||
extern void DefineQueryRewrite(RuleStmt *args);
|
extern void DefineRule(RuleStmt *stmt, const char *queryString);
|
||||||
|
|
||||||
|
extern void DefineQueryRewrite(char *rulename,
|
||||||
|
RangeVar *event_obj,
|
||||||
|
Node *event_qual,
|
||||||
|
CmdType event_type,
|
||||||
|
bool is_instead,
|
||||||
|
bool replace,
|
||||||
|
List *action);
|
||||||
|
|
||||||
extern void RenameRewriteRule(Oid owningRel, const char *oldName,
|
extern void RenameRewriteRule(Oid owningRel, const char *oldName,
|
||||||
const char *newName);
|
const char *newName);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.41 2007/02/20 17:32:17 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.42 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -33,7 +33,7 @@ extern void PortalStart(Portal portal, ParamListInfo params,
|
||||||
extern void PortalSetResultFormat(Portal portal, int nFormats,
|
extern void PortalSetResultFormat(Portal portal, int nFormats,
|
||||||
int16 *formats);
|
int16 *formats);
|
||||||
|
|
||||||
extern bool PortalRun(Portal portal, long count,
|
extern bool PortalRun(Portal portal, long count, bool isTopLevel,
|
||||||
DestReceiver *dest, DestReceiver *altdest,
|
DestReceiver *dest, DestReceiver *altdest,
|
||||||
char *completionTag);
|
char *completionTag);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.31 2007/02/20 17:32:17 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.32 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -17,7 +17,8 @@
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
|
|
||||||
|
|
||||||
extern void ProcessUtility(Node *parsetree, ParamListInfo params,
|
extern void ProcessUtility(Node *parsetree, const char *queryString,
|
||||||
|
ParamListInfo params, bool isTopLevel,
|
||||||
DestReceiver *dest, char *completionTag);
|
DestReceiver *dest, char *completionTag);
|
||||||
|
|
||||||
extern bool UtilityReturnsTuples(Node *parsetree);
|
extern bool UtilityReturnsTuples(Node *parsetree);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/memutils.h,v 1.61 2007/01/05 22:19:59 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/utils/memutils.h,v 1.62 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -75,8 +75,7 @@ extern DLLIMPORT MemoryContext MessageContext;
|
||||||
extern DLLIMPORT MemoryContext TopTransactionContext;
|
extern DLLIMPORT MemoryContext TopTransactionContext;
|
||||||
extern DLLIMPORT MemoryContext CurTransactionContext;
|
extern DLLIMPORT MemoryContext CurTransactionContext;
|
||||||
|
|
||||||
/* These two are transient links to contexts owned by other objects: */
|
/* This is a transient link to the active portal's memory context: */
|
||||||
extern DLLIMPORT MemoryContext QueryContext;
|
|
||||||
extern DLLIMPORT MemoryContext PortalContext;
|
extern DLLIMPORT MemoryContext PortalContext;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* plancache.h
|
||||||
|
* Plan cache definitions.
|
||||||
|
*
|
||||||
|
* See plancache.c for comments.
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.1 2007/03/13 00:33:43 tgl Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef PLANCACHE_H
|
||||||
|
#define PLANCACHE_H
|
||||||
|
|
||||||
|
#include "access/tupdesc.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CachedPlanSource represents the portion of a cached plan that persists
|
||||||
|
* across invalidation/replan cycles. It stores a raw parse tree (required),
|
||||||
|
* the original source text (optional, but highly recommended to improve
|
||||||
|
* error reports), and adjunct data.
|
||||||
|
*
|
||||||
|
* Normally, both the struct itself and the subsidiary data live in the
|
||||||
|
* context denoted by the context field, while the linked-to CachedPlan, if
|
||||||
|
* any, has its own context. Thus an invalidated CachedPlan can be dropped
|
||||||
|
* when no longer needed, and conversely a CachedPlanSource can be dropped
|
||||||
|
* without worrying whether any portals depend on particular instances of
|
||||||
|
* its plan.
|
||||||
|
*
|
||||||
|
* But for entries created by FastCreateCachedPlan, the CachedPlanSource
|
||||||
|
* and the initial version of the CachedPlan share the same memory context.
|
||||||
|
* In this case, we treat the memory context as belonging to the CachedPlan.
|
||||||
|
* The CachedPlanSource has an extra reference-counted link (orig_plan)
|
||||||
|
* to the CachedPlan, and the memory context goes away when the CachedPlan's
|
||||||
|
* reference count goes to zero. This arrangement saves overhead for plans
|
||||||
|
* that aren't expected to live long enough to need replanning, while not
|
||||||
|
* losing any flexibility if a replan turns out to be necessary.
|
||||||
|
*
|
||||||
|
* Note: the string referenced by commandTag is not subsidiary storage;
|
||||||
|
* it is assumed to be a compile-time-constant string. As with portals,
|
||||||
|
* commandTag shall be NULL if and only if the original query string (before
|
||||||
|
* rewriting) was an empty string.
|
||||||
|
*/
|
||||||
|
typedef struct CachedPlanSource
|
||||||
|
{
|
||||||
|
Node *raw_parse_tree; /* output of raw_parser() */
|
||||||
|
char *query_string; /* text of query, or NULL */
|
||||||
|
const char *commandTag; /* command tag (a constant!), or NULL */
|
||||||
|
Oid *param_types; /* array of parameter type OIDs, or NULL */
|
||||||
|
int num_params; /* length of param_types array */
|
||||||
|
bool fully_planned; /* do we cache planner or rewriter output? */
|
||||||
|
bool fixed_result; /* disallow change in result tupdesc? */
|
||||||
|
int generation; /* counter, starting at 1, for replans */
|
||||||
|
TupleDesc resultDesc; /* result type; NULL = doesn't return tuples */
|
||||||
|
struct CachedPlan *plan; /* link to plan, or NULL if not valid */
|
||||||
|
MemoryContext context; /* context containing this CachedPlanSource */
|
||||||
|
struct CachedPlan *orig_plan; /* link to plan owning my context */
|
||||||
|
} CachedPlanSource;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CachedPlan represents the portion of a cached plan that is discarded when
|
||||||
|
* invalidation occurs. The reference count includes both the link(s) from the
|
||||||
|
* parent CachedPlanSource, and any active plan executions, so the plan can be
|
||||||
|
* discarded exactly when refcount goes to zero. Both the struct itself and
|
||||||
|
* the subsidiary data live in the context denoted by the context field.
|
||||||
|
* This makes it easy to free a no-longer-needed cached plan.
|
||||||
|
*/
|
||||||
|
typedef struct CachedPlan
|
||||||
|
{
|
||||||
|
List *stmt_list; /* list of statement or Query nodes */
|
||||||
|
bool fully_planned; /* do we cache planner or rewriter output? */
|
||||||
|
bool dead; /* if true, do not use */
|
||||||
|
int refcount; /* count of live references to this struct */
|
||||||
|
int generation; /* counter, starting at 1, for replans */
|
||||||
|
MemoryContext context; /* context containing this CachedPlan */
|
||||||
|
} CachedPlan;
|
||||||
|
|
||||||
|
|
||||||
|
extern void InitPlanCache(void);
|
||||||
|
extern CachedPlanSource *CreateCachedPlan(Node *raw_parse_tree,
|
||||||
|
const char *query_string,
|
||||||
|
const char *commandTag,
|
||||||
|
Oid *param_types,
|
||||||
|
int num_params,
|
||||||
|
List *stmt_list,
|
||||||
|
bool fully_planned,
|
||||||
|
bool fixed_result);
|
||||||
|
extern CachedPlanSource *FastCreateCachedPlan(Node *raw_parse_tree,
|
||||||
|
char *query_string,
|
||||||
|
const char *commandTag,
|
||||||
|
Oid *param_types,
|
||||||
|
int num_params,
|
||||||
|
List *stmt_list,
|
||||||
|
bool fully_planned,
|
||||||
|
bool fixed_result,
|
||||||
|
MemoryContext context);
|
||||||
|
extern void DropCachedPlan(CachedPlanSource *plansource);
|
||||||
|
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
|
||||||
|
bool useResOwner);
|
||||||
|
extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
|
||||||
|
|
||||||
|
#endif /* PLANCACHE_H */
|
|
@ -39,7 +39,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.73 2007/02/20 17:32:18 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.74 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -94,7 +94,8 @@ typedef enum PortalStrategy
|
||||||
*/
|
*/
|
||||||
typedef enum PortalStatus
|
typedef enum PortalStatus
|
||||||
{
|
{
|
||||||
PORTAL_NEW, /* in process of creation */
|
PORTAL_NEW, /* freshly created */
|
||||||
|
PORTAL_DEFINED, /* PortalDefineQuery done */
|
||||||
PORTAL_READY, /* PortalStart complete, can run it */
|
PORTAL_READY, /* PortalStart complete, can run it */
|
||||||
PORTAL_ACTIVE, /* portal is running (can't delete it) */
|
PORTAL_ACTIVE, /* portal is running (can't delete it) */
|
||||||
PORTAL_DONE, /* portal is finished (don't re-run it) */
|
PORTAL_DONE, /* portal is finished (don't re-run it) */
|
||||||
|
@ -125,15 +126,7 @@ typedef struct PortalData
|
||||||
const char *sourceText; /* text of query, if known (may be NULL) */
|
const char *sourceText; /* text of query, if known (may be NULL) */
|
||||||
const char *commandTag; /* command tag for original query */
|
const char *commandTag; /* command tag for original query */
|
||||||
List *stmts; /* PlannedStmts and/or utility statements */
|
List *stmts; /* PlannedStmts and/or utility statements */
|
||||||
MemoryContext queryContext; /* where the plan trees live */
|
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: queryContext effectively identifies which prepared statement the
|
|
||||||
* portal depends on, if any. The queryContext is *not* owned by the
|
|
||||||
* portal and is not to be deleted by portal destruction. (But for a
|
|
||||||
* cursor it is the same as "heap", and that context is deleted by portal
|
|
||||||
* destruction.) The plan trees may be in either queryContext or heap.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ParamListInfo portalParams; /* params to pass to query */
|
ParamListInfo portalParams; /* params to pass to query */
|
||||||
|
|
||||||
|
@ -210,14 +203,13 @@ extern void AtSubCleanup_Portals(SubTransactionId mySubid);
|
||||||
extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
|
extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
|
||||||
extern Portal CreateNewPortal(void);
|
extern Portal CreateNewPortal(void);
|
||||||
extern void PortalDrop(Portal portal, bool isTopCommit);
|
extern void PortalDrop(Portal portal, bool isTopCommit);
|
||||||
extern void DropDependentPortals(MemoryContext queryContext);
|
|
||||||
extern Portal GetPortalByName(const char *name);
|
extern Portal GetPortalByName(const char *name);
|
||||||
extern void PortalDefineQuery(Portal portal,
|
extern void PortalDefineQuery(Portal portal,
|
||||||
const char *prepStmtName,
|
const char *prepStmtName,
|
||||||
const char *sourceText,
|
const char *sourceText,
|
||||||
const char *commandTag,
|
const char *commandTag,
|
||||||
List *stmts,
|
List *stmts,
|
||||||
MemoryContext queryContext);
|
CachedPlan *cplan);
|
||||||
extern Node *PortalListGetPrimaryStmt(List *stmts);
|
extern Node *PortalListGetPrimaryStmt(List *stmts);
|
||||||
extern void PortalCreateHoldStore(Portal portal);
|
extern void PortalCreateHoldStore(Portal portal);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/resowner.h,v 1.10 2007/01/05 22:19:59 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/utils/resowner.h,v 1.11 2007/03/13 00:33:43 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "storage/buf.h"
|
#include "storage/buf.h"
|
||||||
#include "utils/catcache.h"
|
#include "utils/catcache.h"
|
||||||
|
#include "utils/plancache.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -106,6 +107,13 @@ extern void ResourceOwnerRememberRelationRef(ResourceOwner owner,
|
||||||
extern void ResourceOwnerForgetRelationRef(ResourceOwner owner,
|
extern void ResourceOwnerForgetRelationRef(ResourceOwner owner,
|
||||||
Relation rel);
|
Relation rel);
|
||||||
|
|
||||||
|
/* support for plancache refcount management */
|
||||||
|
extern void ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner);
|
||||||
|
extern void ResourceOwnerRememberPlanCacheRef(ResourceOwner owner,
|
||||||
|
CachedPlan *plan);
|
||||||
|
extern void ResourceOwnerForgetPlanCacheRef(ResourceOwner owner,
|
||||||
|
CachedPlan *plan);
|
||||||
|
|
||||||
/* support for tupledesc refcount management */
|
/* support for tupledesc refcount management */
|
||||||
extern void ResourceOwnerEnlargeTupleDescs(ResourceOwner owner);
|
extern void ResourceOwnerEnlargeTupleDescs(ResourceOwner owner);
|
||||||
extern void ResourceOwnerRememberTupleDesc(ResourceOwner owner,
|
extern void ResourceOwnerRememberTupleDesc(ResourceOwner owner,
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
--
|
||||||
|
-- Tests to exercise the plan caching/invalidation mechanism
|
||||||
|
--
|
||||||
|
CREATE TEMP TABLE foo AS SELECT * FROM int8_tbl;
|
||||||
|
-- create and use a cached plan
|
||||||
|
PREPARE prepstmt AS SELECT * FROM foo;
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
q1 | q2
|
||||||
|
------------------+-------------------
|
||||||
|
123 | 456
|
||||||
|
123 | 4567890123456789
|
||||||
|
4567890123456789 | 123
|
||||||
|
4567890123456789 | 4567890123456789
|
||||||
|
4567890123456789 | -4567890123456789
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
-- and one with parameters
|
||||||
|
PREPARE prepstmt2(bigint) AS SELECT * FROM foo WHERE q1 = $1;
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
q1 | q2
|
||||||
|
-----+------------------
|
||||||
|
123 | 456
|
||||||
|
123 | 4567890123456789
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- invalidate the plans and see what happens
|
||||||
|
DROP TABLE foo;
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
ERROR: relation "foo" does not exist
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
ERROR: relation "foo" does not exist
|
||||||
|
-- recreate the temp table (this demonstrates that the raw plan is
|
||||||
|
-- purely textual and doesn't depend on OIDs, for instance)
|
||||||
|
CREATE TEMP TABLE foo AS SELECT * FROM int8_tbl ORDER BY 2;
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
q1 | q2
|
||||||
|
------------------+-------------------
|
||||||
|
4567890123456789 | -4567890123456789
|
||||||
|
4567890123456789 | 123
|
||||||
|
123 | 456
|
||||||
|
123 | 4567890123456789
|
||||||
|
4567890123456789 | 4567890123456789
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
q1 | q2
|
||||||
|
-----+------------------
|
||||||
|
123 | 456
|
||||||
|
123 | 4567890123456789
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- prepared statements should prevent change in output tupdesc,
|
||||||
|
-- since clients probably aren't expecting that to change on the fly
|
||||||
|
ALTER TABLE foo ADD COLUMN q3 bigint;
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
ERROR: cached plan must not change result type
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
ERROR: cached plan must not change result type
|
||||||
|
-- but we're nice guys and will let you undo your mistake
|
||||||
|
ALTER TABLE foo DROP COLUMN q3;
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
q1 | q2
|
||||||
|
------------------+-------------------
|
||||||
|
4567890123456789 | -4567890123456789
|
||||||
|
4567890123456789 | 123
|
||||||
|
123 | 456
|
||||||
|
123 | 4567890123456789
|
||||||
|
4567890123456789 | 4567890123456789
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
q1 | q2
|
||||||
|
-----+------------------
|
||||||
|
123 | 456
|
||||||
|
123 | 4567890123456789
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Try it with a view, which isn't directly used in the resulting plan
|
||||||
|
-- but should trigger invalidation anyway
|
||||||
|
CREATE TEMP VIEW voo AS SELECT * FROM foo;
|
||||||
|
PREPARE vprep AS SELECT * FROM voo;
|
||||||
|
EXECUTE vprep;
|
||||||
|
q1 | q2
|
||||||
|
------------------+-------------------
|
||||||
|
4567890123456789 | -4567890123456789
|
||||||
|
4567890123456789 | 123
|
||||||
|
123 | 456
|
||||||
|
123 | 4567890123456789
|
||||||
|
4567890123456789 | 4567890123456789
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
CREATE OR REPLACE TEMP VIEW voo AS SELECT q1, q2/2 AS q2 FROM foo;
|
||||||
|
EXECUTE vprep;
|
||||||
|
q1 | q2
|
||||||
|
------------------+-------------------
|
||||||
|
4567890123456789 | -2283945061728394
|
||||||
|
4567890123456789 | 61
|
||||||
|
123 | 228
|
||||||
|
123 | 2283945061728394
|
||||||
|
4567890123456789 | 2283945061728394
|
||||||
|
(5 rows)
|
||||||
|
|
|
@ -1188,6 +1188,8 @@ drop rule foorule on foo;
|
||||||
create rule foorule as on insert to foo where f1 < 100
|
create rule foorule as on insert to foo where f1 < 100
|
||||||
do instead insert into foo2 values (f1);
|
do instead insert into foo2 values (f1);
|
||||||
ERROR: column "f1" does not exist
|
ERROR: column "f1" does not exist
|
||||||
|
LINE 2: do instead insert into foo2 values (f1);
|
||||||
|
^
|
||||||
-- this is the correct way:
|
-- this is the correct way:
|
||||||
create rule foorule as on insert to foo where f1 < 100
|
create rule foorule as on insert to foo where f1 < 100
|
||||||
do instead insert into foo2 values (new.f1);
|
do instead insert into foo2 values (new.f1);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# ----------
|
# ----------
|
||||||
# The first group of parallel test
|
# The first group of parallel test
|
||||||
# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.39 2007/02/09 03:35:35 tgl Exp $
|
# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.40 2007/03/13 00:33:44 tgl Exp $
|
||||||
# ----------
|
# ----------
|
||||||
test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid
|
test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ test: misc
|
||||||
# ----------
|
# ----------
|
||||||
# The fifth group of parallel test
|
# The fifth group of parallel test
|
||||||
# ----------
|
# ----------
|
||||||
test: select_views portals_p2 rules foreign_key cluster dependency guc combocid
|
test: select_views portals_p2 rules foreign_key cluster dependency guc combocid plancache
|
||||||
|
|
||||||
# ----------
|
# ----------
|
||||||
# The sixth group of parallel test
|
# The sixth group of parallel test
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.37 2007/02/09 03:35:35 tgl Exp $
|
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.38 2007/03/13 00:33:44 tgl Exp $
|
||||||
# This should probably be in an order similar to parallel_schedule.
|
# This should probably be in an order similar to parallel_schedule.
|
||||||
test: boolean
|
test: boolean
|
||||||
test: char
|
test: char
|
||||||
|
@ -89,6 +89,7 @@ test: cluster
|
||||||
test: dependency
|
test: dependency
|
||||||
test: guc
|
test: guc
|
||||||
test: combocid
|
test: combocid
|
||||||
|
test: plancache
|
||||||
test: limit
|
test: limit
|
||||||
test: plpgsql
|
test: plpgsql
|
||||||
test: copy2
|
test: copy2
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
--
|
||||||
|
-- Tests to exercise the plan caching/invalidation mechanism
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TEMP TABLE foo AS SELECT * FROM int8_tbl;
|
||||||
|
|
||||||
|
-- create and use a cached plan
|
||||||
|
PREPARE prepstmt AS SELECT * FROM foo;
|
||||||
|
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
|
||||||
|
-- and one with parameters
|
||||||
|
PREPARE prepstmt2(bigint) AS SELECT * FROM foo WHERE q1 = $1;
|
||||||
|
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
|
||||||
|
-- invalidate the plans and see what happens
|
||||||
|
DROP TABLE foo;
|
||||||
|
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
|
||||||
|
-- recreate the temp table (this demonstrates that the raw plan is
|
||||||
|
-- purely textual and doesn't depend on OIDs, for instance)
|
||||||
|
CREATE TEMP TABLE foo AS SELECT * FROM int8_tbl ORDER BY 2;
|
||||||
|
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
|
||||||
|
-- prepared statements should prevent change in output tupdesc,
|
||||||
|
-- since clients probably aren't expecting that to change on the fly
|
||||||
|
ALTER TABLE foo ADD COLUMN q3 bigint;
|
||||||
|
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
|
||||||
|
-- but we're nice guys and will let you undo your mistake
|
||||||
|
ALTER TABLE foo DROP COLUMN q3;
|
||||||
|
|
||||||
|
EXECUTE prepstmt;
|
||||||
|
EXECUTE prepstmt2(123);
|
||||||
|
|
||||||
|
-- Try it with a view, which isn't directly used in the resulting plan
|
||||||
|
-- but should trigger invalidation anyway
|
||||||
|
CREATE TEMP VIEW voo AS SELECT * FROM foo;
|
||||||
|
|
||||||
|
PREPARE vprep AS SELECT * FROM voo;
|
||||||
|
|
||||||
|
EXECUTE vprep;
|
||||||
|
|
||||||
|
CREATE OR REPLACE TEMP VIEW voo AS SELECT q1, q2/2 AS q2 FROM foo;
|
||||||
|
|
||||||
|
EXECUTE vprep;
|
Loading…
Reference in New Issue