Re: Assertion failure with assignment to array elem - Mailing list pgsql-bugs

From Tom Lane
Subject Re: Assertion failure with assignment to array elem
Date
Msg-id 2034.1281371524@sss.pgh.pa.us
Whole thread Raw
In response to Re: Assertion failure with assignment to array elem  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: Assertion failure with assignment to array elem
List pgsql-bugs
I wrote:
> Yeah, I don't like that either.  What we need to do instead is fix
> exec_assign_value so that it can cope with the case of the caller having
> an open expression evaluation.  We can easily have it save/restore
> eval_tuptable.  Not resetting eval_econtext is a bit harder, but maybe
> we could have a use-count variable ... or even easier, just decree that
> the caller has to do exec_eval_cleanup after calling exec_assign_value,
> whether or not it had an open expression eval.

After a bit of experimentation, I propose the attached patch.  I don't
think any additional resets of eval_econtext are necessary.  There are
some callers of exec_assign_value that don't immediately do an
exec_eval_cleanup afterwards, but one will happen soon enough.

            regards, tom lane

Index: pl_exec.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.262
diff -c -r1.262 pl_exec.c
*** pl_exec.c    9 Aug 2010 02:25:05 -0000    1.262
--- pl_exec.c    9 Aug 2010 16:27:56 -0000
***************
*** 2701,2706 ****
--- 2701,2709 ----
   *
   * NB: the result of the evaluation is no longer valid after this is done,
   * unless it is a pass-by-value datatype.
+  *
+  * NB: if you change this code, see also the hacks in exec_assign_value's
+  * PLPGSQL_DTYPE_ARRAYELEM case.
   * ----------
   */
  static void
***************
*** 3464,3469 ****
--- 3467,3476 ----

  /* ----------
   * exec_assign_value            Put a value into a target field
+  *
+  * Note: in some code paths, this may leak memory in the eval_econtext;
+  * we assume that will be cleaned up later by exec_eval_cleanup.  We cannot
+  * call exec_eval_cleanup here for fear of destroying the input Datum value.
   * ----------
   */
  static void
***************
*** 3714,3719 ****
--- 3721,3729 ----

          case PLPGSQL_DTYPE_ARRAYELEM:
              {
+                 /*
+                  * Target is an element of an array
+                  */
                  int            nsubscripts;
                  int            i;
                  PLpgSQL_expr *subscripts[MAXDIM];
***************
*** 3729,3738 ****
                              coerced_value;
                  ArrayType  *oldarrayval;
                  ArrayType  *newarrayval;

                  /*
-                  * Target is an element of an array
-                  *
                   * To handle constructs like x[1][2] := something, we have to
                   * be prepared to deal with a chain of arrayelem datums. Chase
                   * back to find the base array datum, and save the subscript
--- 3739,3757 ----
                              coerced_value;
                  ArrayType  *oldarrayval;
                  ArrayType  *newarrayval;
+                 SPITupleTable *save_eval_tuptable;
+
+                 /*
+                  * We need to do subscript evaluation, which might require
+                  * evaluating general expressions; and the caller might have
+                  * done that too in order to prepare the input Datum.  We
+                  * have to save and restore the caller's SPI_execute result,
+                  * if any.
+                  */
+                 save_eval_tuptable = estate->eval_tuptable;
+                 estate->eval_tuptable = NULL;

                  /*
                   * To handle constructs like x[1][2] := something, we have to
                   * be prepared to deal with a chain of arrayelem datums. Chase
                   * back to find the base array datum, and save the subscript
***************
*** 3786,3793 ****
--- 3805,3827 ----
                          ereport(ERROR,
                                  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                                   errmsg("array subscript in assignment must not be null")));
+
+                     /*
+                      * Clean up in case the subscript expression wasn't simple.
+                      * We can't do exec_eval_cleanup, but we can do this much
+                      * (which is safe because the integer subscript value is
+                      * surely pass-by-value), and we must do it in case the
+                      * next subscript expression isn't simple either.
+                      */
+                     if (estate->eval_tuptable != NULL)
+                         SPI_freetuptable(estate->eval_tuptable);
+                     estate->eval_tuptable = NULL;
                  }

+                 /* Now we can restore caller's SPI_execute result if any. */
+                 Assert(estate->eval_tuptable == NULL);
+                 estate->eval_tuptable = save_eval_tuptable;
+
                  /* Coerce source value to match array element type. */
                  coerced_value = exec_simple_cast_value(value,
                                                         valtype,

pgsql-bugs by date:

Previous
From: Tom Lane
Date:
Subject: Re: Assertion failure with assignment to array elem
Next
From: Heikki Linnakangas
Date:
Subject: Re: Assertion failure with assignment to array elem