]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Make the world safe for passing whole rows of views to functions. This
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 18 Apr 2001 20:42:56 +0000 (20:42 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 18 Apr 2001 20:42:56 +0000 (20:42 +0000)
already worked fine for whole rows of tables, but not so well for views...

src/backend/optimizer/plan/planner.c
src/backend/optimizer/util/var.c
src/backend/rewrite/rewriteManip.c
src/include/optimizer/var.h
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/scan.l

index 199d27973bcf241a3a655dc45f04cf8a92ea2fd4..3026fdf0581f0e24b981b76e5fcfc72211dbf2e3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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)
                /*
                 * Is this a subquery RTE, and if so, is the subquery simple
                 * 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;
                        Node       *subjointree;
index cac0eee8276a9ff8131e6995cfa3e35f39e490a1..30f02de5c72e2c1dfb6e7b88a7dcc8d98f5e19db 100644 (file)
@@ -8,12 +8,10 @@
  *
  *
  * 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 "nodes/plannodes.h"
@@ -27,6 +25,12 @@ typedef struct
        int                     sublevels_up;
 } pull_varnos_context;
 
+typedef struct
+{
+       int                     varno;
+       int                     sublevels_up;
+} contain_whole_tuple_var_context;
+
 typedef struct
 {
        List       *varlist;
@@ -35,6 +39,8 @@ typedef struct
 
 static bool pull_varnos_walker(Node *node,
                                   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 pull_var_clause_walker(Node *node,
                                           pull_var_clause_context *context);
@@ -46,11 +52,10 @@ static bool pull_var_clause_walker(Node *node,
  *             Create a list of all the distinct varnos present in a parsetree.
  *             Only varnos that reference level-zero rtable entries are considered.
  *
- * NOTE: unlike other routines in this file, pull_varnos() 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 level!  But when we find a completed SubPlan,
- * we only need to look at the parameters passed to the subplan.
+ * 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 level!  But when we find a completed
+ * SubPlan, we only need to look at the parameters passed to the subplan.
  */
 List *
 pull_varnos(Node *node)
@@ -122,17 +127,105 @@ pull_varnos_walker(Node *node, pull_varnos_context *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
  *       Recursively scan a clause to discover whether it contains any Var nodes
  *       (of the current query level).
  *
  *       Returns true if any varnode found.
+ *
+ * Does not examine subqueries, therefore must only be used after reduction
+ * of sublinks to subplans!
  */
 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
@@ -150,6 +243,7 @@ contain_var_clause_walker(Node *node, void *context)
        return expression_tree_walker(node, contain_var_clause_walker, context);
 }
 
+
 /*
  * pull_var_clause
  *       Recursively pulls all var nodes from an expression clause.
@@ -160,16 +254,19 @@ contain_var_clause_walker(Node *node, void *context)
  *
  *       Returns list of varnodes found.  Note the varnodes themselves are not
  *       copied, only referenced.
+ *
+ * Does not examine subqueries, therefore must only be used after reduction
+ * of sublinks to subplans!
  */
 List *
-pull_var_clause(Node *clause, bool includeUpperVars)
+pull_var_clause(Node *node, bool includeUpperVars)
 {
        pull_var_clause_context context;
 
        context.varlist = NIL;
        context.includeUpperVars = includeUpperVars;
 
-       pull_var_clause_walker(clause, &context);
+       pull_var_clause_walker(node, &context);
        return context.varlist;
 }
 
index 663b67708ee31ee151e98dc3804dc57b2e97457a..45115b8d045e3cc26803fa5f2d5993e7f0703b7c 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * 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)
                if (this_varno == context->target_varno &&
                        this_varlevelsup == context->sublevels_up)
                {
-                       Node       *n = FindMatchingNew(context->targetlist,
-                                                                                       var->varattno);
+                       Node       *n;
+
+                       /* 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)
                        {
index d0840596f1bf98cc3fd94e1efaba2db39736931f..45048133eb03002bb378fa3c90f6b7b8a5e7f0e8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * 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 @@
 
 #include "nodes/primnodes.h"
 
-extern List *pull_varnos(Node *me);
-extern bool contain_var_clause(Node *clause);
-extern List *pull_var_clause(Node *clause, bool includeUpperVars);
+extern List *pull_varnos(Node *node);
+extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
+extern bool contain_var_clause(Node *node);
+extern List *pull_var_clause(Node *node, bool includeUpperVars);
 
 #endif  /* VAR_H */
index 29e2dd24bbd2ed1a312e24bb15458bc24c574304..1bfce18473f377cda969d259853d1dbd32bf32fd 100644 (file)
@@ -4,7 +4,7 @@
  *                                               procedural language
  *
  * 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.
  *
@@ -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->query                             = strdup(plpgsql_dstring_get(&ds));
                                                expr->plan                              = NULL;
@@ -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->query                     = strdup(plpgsql_dstring_get(&ds));
        expr->plan                      = NULL;
@@ -1310,7 +1310,7 @@ make_select_stmt()
                {
                        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->query                     = strdup(plpgsql_dstring_get(&ds));
                        expr->plan                      = NULL;
@@ -1449,14 +1449,13 @@ make_select_stmt()
                                        {
                                                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->query                             = strdup(plpgsql_dstring_get(&ds));
                                                expr->plan                              = NULL;
                                                expr->nparams   = nparams;
-                                               while(nparams-- > 0) {
+                                               while (nparams-- > 0)
                                                        expr->params[nparams] = params[nparams];
-                                               }
                                                plpgsql_dstring_free(&ds);
 
                                                execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
@@ -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->query                     = strdup(plpgsql_dstring_get(&ds));
        expr->plan                      = NULL;
index 22bb9e03a8f6312fc34f99dcb05d2b3a4538a787..9cfa748241330c4016d3deadc502c29e5f47e0bd 100644 (file)
@@ -3,7 +3,7 @@
  *                       procedural language
  *
  * 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.
  *
@@ -552,7 +552,7 @@ plpgsql_parse_word(char *word)
         */
        if (plpgsql_curr_compile->fn_functype == T_TRIGGER)
        {
-               if (!strcmp(cp, "tg_argv"))
+               if (strcmp(cp, "tg_argv") == 0)
                {
                        int                     save_spacescanned = plpgsql_SpaceScanned;
                        PLpgSQL_trigarg *trigarg;
@@ -751,7 +751,7 @@ plpgsql_parse_dblword(char *string)
                                row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
                                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]]);
                                                pfree(word1);
@@ -855,7 +855,7 @@ plpgsql_parse_tripword(char *string)
                                row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
                                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]]);
                                                pfree(word1);
@@ -1139,14 +1139,17 @@ plpgsql_parse_wordrowtype(char *string)
                elog(ERROR, "%s: no such class", word1);
        }
        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();
                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,
                                                         PointerGetDatum(word1),
@@ -1205,15 +1208,17 @@ plpgsql_parse_wordrowtype(char *string)
                typeStruct = (Form_pg_type) GETSTRUCT(typetup);
 
                /*
-                * Create the internal variable We know if the table definitions
-                * 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
-                * NOT NULL without a default value and that would lead to
-                * problems later when initializing the variables due to entering
-                * a block at execution time. Thus we ignore this information for
-                * now.
+                * Create the internal variable
+                *
+                * We know if the table definitions 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 NOT NULL without a default value and that
+                * would lead to problems later when initializing the variables due to
+                * entering a block at execution time. Thus we ignore this information
+                * for now.
                 */
                var = malloc(sizeof(PLpgSQL_var));
+               memset(var, 0, sizeof(PLpgSQL_var));
                var->dtype = PLPGSQL_DTYPE_VAR;
                var->refname = malloc(strlen(word1) + strlen(cp) + 2);
                strcpy(var->refname, word1);
@@ -1241,7 +1246,7 @@ plpgsql_parse_wordrowtype(char *string)
                /*
                 * Add the variable to the row.
                 */
-               row->fieldnames[i] = cp;
+               row->fieldnames[i] = strdup(cp);
                row->varnos[i] = var->varno;
        }
 
index 5872a4eddb82f593705aa7b54054e7d601ff0cf5..ecde59df0c018aa993fad9099ae9632f0beb53c9 100644 (file)
@@ -4,7 +4,7 @@
  *                       procedural language
  *
  * 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.
  *
@@ -145,6 +145,12 @@ dump                       { return O_DUMP;                        }
 {WS}{WC}*%ROWTYPE      { return plpgsql_parse_wordrowtype(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;                      }
 
     /* ----------