Commit d5096af2 authored by Tom Lane's avatar Tom Lane

Make the world safe for passing whole rows of views to functions. This

already worked fine for whole rows of tables, but not so well for views...
parent e4c06b21
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.103 2001/04/01 22:37:19 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.104 2001/04/18 20:42:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -271,8 +271,12 @@ pull_up_subqueries(Query *parse, Node *jtnode) ...@@ -271,8 +271,12 @@ pull_up_subqueries(Query *parse, Node *jtnode)
/* /*
* Is this a subquery RTE, and if so, is the subquery simple * Is this a subquery RTE, and if so, is the subquery simple
* enough to pull up? (If not, do nothing at this node.) * enough to pull up? (If not, do nothing at this node.)
*
* Note: even if the subquery itself is simple enough, we can't
* pull it up if there is a reference to its whole tuple result.
*/ */
if (subquery && is_simple_subquery(subquery)) if (subquery && is_simple_subquery(subquery) &&
!contain_whole_tuple_var((Node *) parse, varno, 0))
{ {
int rtoffset; int rtoffset;
Node *subjointree; Node *subjointree;
......
...@@ -8,12 +8,10 @@ ...@@ -8,12 +8,10 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.30 2001/03/22 03:59:40 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.31 2001/04/18 20:42:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include <sys/types.h>
#include "postgres.h" #include "postgres.h"
#include "nodes/plannodes.h" #include "nodes/plannodes.h"
...@@ -27,6 +25,12 @@ typedef struct ...@@ -27,6 +25,12 @@ typedef struct
int sublevels_up; int sublevels_up;
} pull_varnos_context; } pull_varnos_context;
typedef struct
{
int varno;
int sublevels_up;
} contain_whole_tuple_var_context;
typedef struct typedef struct
{ {
List *varlist; List *varlist;
...@@ -35,6 +39,8 @@ typedef struct ...@@ -35,6 +39,8 @@ typedef struct
static bool pull_varnos_walker(Node *node, static bool pull_varnos_walker(Node *node,
pull_varnos_context *context); pull_varnos_context *context);
static bool contain_whole_tuple_var_walker(Node *node,
contain_whole_tuple_var_context *context);
static bool contain_var_clause_walker(Node *node, void *context); static bool contain_var_clause_walker(Node *node, void *context);
static bool pull_var_clause_walker(Node *node, static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context); pull_var_clause_context *context);
...@@ -46,11 +52,10 @@ static bool pull_var_clause_walker(Node *node, ...@@ -46,11 +52,10 @@ static bool pull_var_clause_walker(Node *node,
* Create a list of all the distinct varnos present in a parsetree. * Create a list of all the distinct varnos present in a parsetree.
* Only varnos that reference level-zero rtable entries are considered. * Only varnos that reference level-zero rtable entries are considered.
* *
* NOTE: unlike other routines in this file, pull_varnos() is used on * NOTE: this is used on not-yet-planned expressions. It may therefore find
* not-yet-planned expressions. It may therefore find bare SubLinks, * bare SubLinks, and if so it needs to recurse into them to look for uplevel
* and if so it needs to recurse into them to look for uplevel references * references to the desired rtable level! But when we find a completed
* to the desired rtable level! But when we find a completed SubPlan, * SubPlan, we only need to look at the parameters passed to the subplan.
* we only need to look at the parameters passed to the subplan.
*/ */
List * List *
pull_varnos(Node *node) pull_varnos(Node *node)
...@@ -122,17 +127,105 @@ pull_varnos_walker(Node *node, pull_varnos_context *context) ...@@ -122,17 +127,105 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
(void *) context); (void *) context);
} }
/*
* contain_whole_tuple_var
*
* Detect whether a parsetree contains any references to the whole
* tuple of a given rtable entry (ie, a Var with varattno = 0).
*
* NOTE: this is used on not-yet-planned expressions. It may therefore find
* bare SubLinks, and if so it needs to recurse into them to look for uplevel
* references to the desired rtable entry! But when we find a completed
* SubPlan, we only need to look at the parameters passed to the subplan.
*/
bool
contain_whole_tuple_var(Node *node, int varno, int levelsup)
{
contain_whole_tuple_var_context context;
context.varno = varno;
context.sublevels_up = levelsup;
/*
* Must be prepared to start with a Query or a bare expression tree;
* if it's a Query, go straight to query_tree_walker to make sure that
* sublevels_up doesn't get incremented prematurely.
*/
if (node && IsA(node, Query))
return query_tree_walker((Query *) node,
contain_whole_tuple_var_walker,
(void *) &context, true);
else
return contain_whole_tuple_var_walker(node, &context);
}
static bool
contain_whole_tuple_var_walker(Node *node,
contain_whole_tuple_var_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{
Var *var = (Var *) node;
if (var->varno == context->varno &&
var->varlevelsup == context->sublevels_up &&
var->varattno == InvalidAttrNumber)
return true;
return false;
}
if (is_subplan(node))
{
/*
* Already-planned subquery. Examine the args list (parameters to
* be passed to subquery), as well as the "oper" list which is
* executed by the outer query. But short-circuit recursion into
* the subquery itself, which would be a waste of effort.
*/
Expr *expr = (Expr *) node;
if (contain_whole_tuple_var_walker((Node *) ((SubPlan *) expr->oper)->sublink->oper,
context))
return true;
if (contain_whole_tuple_var_walker((Node *) expr->args,
context))
return true;
return false;
}
if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
bool result;
context->sublevels_up++;
result = query_tree_walker((Query *) node,
contain_whole_tuple_var_walker,
(void *) context, true);
context->sublevels_up--;
return result;
}
return expression_tree_walker(node, contain_whole_tuple_var_walker,
(void *) context);
}
/* /*
* contain_var_clause * contain_var_clause
* Recursively scan a clause to discover whether it contains any Var nodes * Recursively scan a clause to discover whether it contains any Var nodes
* (of the current query level). * (of the current query level).
* *
* Returns true if any varnode found. * Returns true if any varnode found.
*
* Does not examine subqueries, therefore must only be used after reduction
* of sublinks to subplans!
*/ */
bool bool
contain_var_clause(Node *clause) contain_var_clause(Node *node)
{ {
return contain_var_clause_walker(clause, NULL); return contain_var_clause_walker(node, NULL);
} }
static bool static bool
...@@ -150,6 +243,7 @@ contain_var_clause_walker(Node *node, void *context) ...@@ -150,6 +243,7 @@ contain_var_clause_walker(Node *node, void *context)
return expression_tree_walker(node, contain_var_clause_walker, context); return expression_tree_walker(node, contain_var_clause_walker, context);
} }
/* /*
* pull_var_clause * pull_var_clause
* Recursively pulls all var nodes from an expression clause. * Recursively pulls all var nodes from an expression clause.
...@@ -160,16 +254,19 @@ contain_var_clause_walker(Node *node, void *context) ...@@ -160,16 +254,19 @@ contain_var_clause_walker(Node *node, void *context)
* *
* Returns list of varnodes found. Note the varnodes themselves are not * Returns list of varnodes found. Note the varnodes themselves are not
* copied, only referenced. * copied, only referenced.
*
* Does not examine subqueries, therefore must only be used after reduction
* of sublinks to subplans!
*/ */
List * List *
pull_var_clause(Node *clause, bool includeUpperVars) pull_var_clause(Node *node, bool includeUpperVars)
{ {
pull_var_clause_context context; pull_var_clause_context context;
context.varlist = NIL; context.varlist = NIL;
context.includeUpperVars = includeUpperVars; context.includeUpperVars = includeUpperVars;
pull_var_clause_walker(clause, &context); pull_var_clause_walker(node, &context);
return context.varlist; return context.varlist;
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.56 2001/03/22 03:59:44 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.57 2001/04/18 20:42:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -765,8 +765,13 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) ...@@ -765,8 +765,13 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
if (this_varno == context->target_varno && if (this_varno == context->target_varno &&
this_varlevelsup == context->sublevels_up) this_varlevelsup == context->sublevels_up)
{ {
Node *n = FindMatchingNew(context->targetlist, Node *n;
var->varattno);
/* band-aid: don't do the wrong thing with a whole-tuple Var */
if (var->varattno == InvalidAttrNumber)
elog(ERROR, "ResolveNew: can't handle whole-tuple reference");
n = FindMatchingNew(context->targetlist, var->varattno);
if (n == NULL) if (n == NULL)
{ {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: var.h,v 1.12 2001/01/24 19:43:26 momjian Exp $ * $Id: var.h,v 1.13 2001/04/18 20:42:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,8 +16,9 @@ ...@@ -16,8 +16,9 @@
#include "nodes/primnodes.h" #include "nodes/primnodes.h"
extern List *pull_varnos(Node *me); extern List *pull_varnos(Node *node);
extern bool contain_var_clause(Node *clause); extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
extern List *pull_var_clause(Node *clause, bool includeUpperVars); extern bool contain_var_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars);
#endif /* VAR_H */ #endif /* VAR_H */
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.16 2001/02/19 19:49:53 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.17 2001/04/18 20:42:56 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -885,7 +885,7 @@ fori_lower : ...@@ -885,7 +885,7 @@ fori_lower :
} }
} }
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - 1); expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR; expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds)); expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL; expr->plan = NULL;
...@@ -1272,7 +1272,7 @@ read_sqlstmt (int until, char *s, char *sqlstart) ...@@ -1272,7 +1272,7 @@ read_sqlstmt (int until, char *s, char *sqlstart)
} }
} }
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - 1); expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR; expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds)); expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL; expr->plan = NULL;
...@@ -1310,7 +1310,7 @@ make_select_stmt() ...@@ -1310,7 +1310,7 @@ make_select_stmt()
{ {
PLpgSQL_stmt_execsql *execsql; PLpgSQL_stmt_execsql *execsql;
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - 1); expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR; expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds)); expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL; expr->plan = NULL;
...@@ -1449,14 +1449,13 @@ make_select_stmt() ...@@ -1449,14 +1449,13 @@ make_select_stmt()
{ {
PLpgSQL_stmt_execsql *execsql; PLpgSQL_stmt_execsql *execsql;
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - 1); expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR; expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds)); expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL; expr->plan = NULL;
expr->nparams = nparams; expr->nparams = nparams;
while(nparams-- > 0) { while (nparams-- > 0)
expr->params[nparams] = params[nparams]; expr->params[nparams] = params[nparams];
}
plpgsql_dstring_free(&ds); plpgsql_dstring_free(&ds);
execsql = malloc(sizeof(PLpgSQL_stmt_execsql)); execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
...@@ -1549,7 +1548,7 @@ make_select_stmt() ...@@ -1549,7 +1548,7 @@ make_select_stmt()
} }
} }
expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * (nparams - 1)); expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR; expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = strdup(plpgsql_dstring_get(&ds)); expr->query = strdup(plpgsql_dstring_get(&ds));
expr->plan = NULL; expr->plan = NULL;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.29 2001/04/06 02:06:48 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.30 2001/04/18 20:42:56 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -552,7 +552,7 @@ plpgsql_parse_word(char *word) ...@@ -552,7 +552,7 @@ plpgsql_parse_word(char *word)
*/ */
if (plpgsql_curr_compile->fn_functype == T_TRIGGER) if (plpgsql_curr_compile->fn_functype == T_TRIGGER)
{ {
if (!strcmp(cp, "tg_argv")) if (strcmp(cp, "tg_argv") == 0)
{ {
int save_spacescanned = plpgsql_SpaceScanned; int save_spacescanned = plpgsql_SpaceScanned;
PLpgSQL_trigarg *trigarg; PLpgSQL_trigarg *trigarg;
...@@ -751,7 +751,7 @@ plpgsql_parse_dblword(char *string) ...@@ -751,7 +751,7 @@ plpgsql_parse_dblword(char *string)
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++) for (i = 0; i < row->nfields; i++)
{ {
if (!strcmp(row->fieldnames[i], word2)) if (strcmp(row->fieldnames[i], word2) == 0)
{ {
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]); plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
pfree(word1); pfree(word1);
...@@ -855,7 +855,7 @@ plpgsql_parse_tripword(char *string) ...@@ -855,7 +855,7 @@ plpgsql_parse_tripword(char *string)
row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]); row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
for (i = 0; i < row->nfields; i++) for (i = 0; i < row->nfields; i++)
{ {
if (!strcmp(row->fieldnames[i], word3)) if (strcmp(row->fieldnames[i], word3) == 0)
{ {
plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]); plpgsql_yylval.var = (PLpgSQL_var *) (plpgsql_Datums[row->varnos[i]]);
pfree(word1); pfree(word1);
...@@ -1139,14 +1139,17 @@ plpgsql_parse_wordrowtype(char *string) ...@@ -1139,14 +1139,17 @@ plpgsql_parse_wordrowtype(char *string)
elog(ERROR, "%s: no such class", word1); elog(ERROR, "%s: no such class", word1);
} }
classStruct = (Form_pg_class) GETSTRUCT(classtup); classStruct = (Form_pg_class) GETSTRUCT(classtup);
if (classStruct->relkind != 'r' && classStruct->relkind != 's') /* accept relation, sequence, or view pg_class entries */
if (classStruct->relkind != 'r' &&
classStruct->relkind != 's' &&
classStruct->relkind != 'v')
{ {
plpgsql_comperrinfo(); plpgsql_comperrinfo();
elog(ERROR, "%s isn't a table", word1); elog(ERROR, "%s isn't a table", word1);
} }
/* /*
* Fetch the tables pg_type tuple too * Fetch the table's pg_type tuple too
*/ */
typetup = SearchSysCache(TYPENAME, typetup = SearchSysCache(TYPENAME,
PointerGetDatum(word1), PointerGetDatum(word1),
...@@ -1205,15 +1208,17 @@ plpgsql_parse_wordrowtype(char *string) ...@@ -1205,15 +1208,17 @@ plpgsql_parse_wordrowtype(char *string)
typeStruct = (Form_pg_type) GETSTRUCT(typetup); typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/* /*
* Create the internal variable We know if the table definitions * Create the internal variable
* contain a default value or if the field is declared in the *
* table as NOT NULL. But it's possible to create a table field as * We know if the table definitions contain a default value or if the
* NOT NULL without a default value and that would lead to * field is declared in the table as NOT NULL. But it's possible to
* problems later when initializing the variables due to entering * create a table field as NOT NULL without a default value and that
* a block at execution time. Thus we ignore this information for * would lead to problems later when initializing the variables due to
* now. * entering a block at execution time. Thus we ignore this information
* for now.
*/ */
var = malloc(sizeof(PLpgSQL_var)); var = malloc(sizeof(PLpgSQL_var));
memset(var, 0, sizeof(PLpgSQL_var));
var->dtype = PLPGSQL_DTYPE_VAR; var->dtype = PLPGSQL_DTYPE_VAR;
var->refname = malloc(strlen(word1) + strlen(cp) + 2); var->refname = malloc(strlen(word1) + strlen(cp) + 2);
strcpy(var->refname, word1); strcpy(var->refname, word1);
...@@ -1241,7 +1246,7 @@ plpgsql_parse_wordrowtype(char *string) ...@@ -1241,7 +1246,7 @@ plpgsql_parse_wordrowtype(char *string)
/* /*
* Add the variable to the row. * Add the variable to the row.
*/ */
row->fieldnames[i] = cp; row->fieldnames[i] = strdup(cp);
row->varnos[i] = var->varno; row->varnos[i] = var->varno;
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.9 2001/02/19 19:49:53 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.10 2001/04/18 20:42:56 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -145,6 +145,12 @@ dump { return O_DUMP; } ...@@ -145,6 +145,12 @@ dump { return O_DUMP; }
{WS}{WC}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); } {WS}{WC}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
\$[0-9]+ { return plpgsql_parse_word(yytext); } \$[0-9]+ { return plpgsql_parse_word(yytext); }
\$[0-9]+\.{WS}{WC}* { return plpgsql_parse_dblword(yytext); }
\$[0-9]+\.{WS}{WC}*\.{WS}{WC}* { return plpgsql_parse_tripword(yytext); }
\$[0-9]+%TYPE { return plpgsql_parse_wordtype(yytext); }
\$[0-9]+\.{WS}{WC}*%TYPE { return plpgsql_parse_dblwordtype(yytext); }
\$[0-9]+%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); }
[0-9]+ { return T_NUMBER; } [0-9]+ { return T_NUMBER; }
/* ---------- /* ----------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment