Rationalizing the API for array_ref, array_set, and friends - Mailing list pgsql-hackers

From Tom Lane
Subject Rationalizing the API for array_ref, array_set, and friends
Date
Msg-id 26511.1424050758@sss.pgh.pa.us
Whole thread Raw
List pgsql-hackers
The four functions array_ref, array_set, array_get_slice, array_set_slice
have traditionally declared their array inputs and results as being of
type "ArrayType *".  This is a lie, and has been since Berkeley days,
because they actually also support "fixed-length array" types such as
"name" and "point".  If anyone tried to reference such arguments/results
as an ArrayType struct they'd be in for a surprise.  We made it worse
when we invented toasting, because the arguments (though not the results,
at the moment) might also be toast pointers or short-header varlenas.

Aside from being confusing, ISTM this notational abuse runs some risk of
bad code generation, because a compiler might think the declaration of
these arguments entitles it to assume the pointers are 4-byte-aligned.
I'm not aware that we've had any actual bug reports like that, but as
the compiler boys get ever tenser, who knows what will happen?

Anyway, I'd been quietly averting my eyes from this issue for some time,
but preserving this mess is helping to make a mess of my expanded-array
patch, so I think it's time to do something about it.  Does anyone have
an objection to my pushing the attached cleanup patch?  It simply fixes
the function declarations and adds/subtracts DatumGet/GetDatum calls
as necessary.

            regards, tom lane

diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 3b95552..44d3b3e 100644
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
*************** pg_extension_config_dump(PG_FUNCTION_ARG
*** 2158,2170 ****
              }
          }

!         a = array_set(a, 1, &arrayIndex,
!                       elementDatum,
!                       false,
!                       -1 /* varlena array */ ,
!                       sizeof(Oid) /* OID's typlen */ ,
!                       true /* OID's typbyval */ ,
!                       'i' /* OID's typalign */ );
      }
      repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
      repl_repl[Anum_pg_extension_extconfig - 1] = true;
--- 2158,2170 ----
              }
          }

!         a = DatumGetArrayTypeP(array_set(PointerGetDatum(a), 1, &arrayIndex,
!                                          elementDatum,
!                                          false,
!                                          -1 /* varlena array */ ,
!                                          sizeof(Oid) /* OID's typlen */ ,
!                                          true /* OID's typbyval */ ,
!                                          'i' /* OID's typalign */ ));
      }
      repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a);
      repl_repl[Anum_pg_extension_extconfig - 1] = true;
*************** pg_extension_config_dump(PG_FUNCTION_ARG
*** 2196,2208 ****
              elog(ERROR, "extconfig and extcondition arrays do not match");

          /* Add or replace at same index as in extconfig */
!         a = array_set(a, 1, &arrayIndex,
!                       elementDatum,
!                       false,
!                       -1 /* varlena array */ ,
!                       -1 /* TEXT's typlen */ ,
!                       false /* TEXT's typbyval */ ,
!                       'i' /* TEXT's typalign */ );
      }
      repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
      repl_repl[Anum_pg_extension_extcondition - 1] = true;
--- 2196,2208 ----
              elog(ERROR, "extconfig and extcondition arrays do not match");

          /* Add or replace at same index as in extconfig */
!         a = DatumGetArrayTypeP(array_set(PointerGetDatum(a), 1, &arrayIndex,
!                                          elementDatum,
!                                          false,
!                                          -1 /* varlena array */ ,
!                                          -1 /* TEXT's typlen */ ,
!                                          false /* TEXT's typbyval */ ,
!                                          'i' /* TEXT's typalign */ ));
      }
      repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a);
      repl_repl[Anum_pg_extension_extcondition - 1] = true;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 0e7400f..e66d7b5 100644
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
*************** static Datum ExecEvalCurrentOfExpr(ExprS
*** 252,263 ****
   *
   * NOTE: if we get a NULL result from a subscript expression, we return NULL
   * when it's an array reference, or raise an error when it's an assignment.
-  *
-  * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here,
-  * even though that might seem natural, because this code needs to support
-  * both varlena arrays and fixed-length array types.  DatumGetArrayTypeP()
-  * only works for the varlena kind.  The routines we call in arrayfuncs.c
-  * have to know the difference (that's what they need refattrlength for).
   *----------
   */
  static Datum
--- 252,257 ----
*************** ExecEvalArrayRef(ArrayRefExprState *asta
*** 267,274 ****
                   ExprDoneCond *isDone)
  {
      ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
!     ArrayType  *array_source;
!     ArrayType  *resultArray;
      bool        isAssignment = (arrayRef->refassgnexpr != NULL);
      bool        eisnull;
      ListCell   *l;
--- 261,267 ----
                   ExprDoneCond *isDone)
  {
      ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
!     Datum        array_source;
      bool        isAssignment = (arrayRef->refassgnexpr != NULL);
      bool        eisnull;
      ListCell   *l;
*************** ExecEvalArrayRef(ArrayRefExprState *asta
*** 278,288 ****
                  lower;
      int           *lIndex;

!     array_source = (ArrayType *)
!         DatumGetPointer(ExecEvalExpr(astate->refexpr,
!                                      econtext,
!                                      isNull,
!                                      isDone));

      /*
       * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
--- 271,280 ----
                  lower;
      int           *lIndex;

!     array_source = ExecEvalExpr(astate->refexpr,
!                                 econtext,
!                                 isNull,
!                                 isDone);

      /*
       * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
*************** ExecEvalArrayRef(ArrayRefExprState *asta
*** 400,412 ****
              }
              else
              {
!                 resultArray = array_get_slice(array_source, i,
!                                               upper.indx, lower.indx,
!                                               astate->refattrlength,
!                                               astate->refelemlength,
!                                               astate->refelembyval,
!                                               astate->refelemalign);
!                 econtext->caseValue_datum = PointerGetDatum(resultArray);
                  econtext->caseValue_isNull = false;
              }
          }
--- 392,404 ----
              }
              else
              {
!                 econtext->caseValue_datum =
!                     array_get_slice(array_source, i,
!                                     upper.indx, lower.indx,
!                                     astate->refattrlength,
!                                     astate->refelemlength,
!                                     astate->refelembyval,
!                                     astate->refelemalign);
                  econtext->caseValue_isNull = false;
              }
          }
*************** ExecEvalArrayRef(ArrayRefExprState *asta
*** 435,441 ****
           */
          if (astate->refattrlength > 0)    /* fixed-length array? */
              if (eisnull || *isNull)
!                 return PointerGetDatum(array_source);

          /*
           * For assignment to varlena arrays, we handle a NULL original array
--- 427,433 ----
           */
          if (astate->refattrlength > 0)    /* fixed-length array? */
              if (eisnull || *isNull)
!                 return array_source;

          /*
           * For assignment to varlena arrays, we handle a NULL original array
*************** ExecEvalArrayRef(ArrayRefExprState *asta
*** 445,473 ****
           */
          if (*isNull)
          {
!             array_source = construct_empty_array(arrayRef->refelemtype);
              *isNull = false;
          }

          if (lIndex == NULL)
!             resultArray = array_set(array_source, i,
!                                     upper.indx,
!                                     sourceData,
!                                     eisnull,
!                                     astate->refattrlength,
!                                     astate->refelemlength,
!                                     astate->refelembyval,
!                                     astate->refelemalign);
          else
!             resultArray = array_set_slice(array_source, i,
!                                           upper.indx, lower.indx,
!                                    (ArrayType *) DatumGetPointer(sourceData),
!                                           eisnull,
!                                           astate->refattrlength,
!                                           astate->refelemlength,
!                                           astate->refelembyval,
!                                           astate->refelemalign);
!         return PointerGetDatum(resultArray);
      }

      if (lIndex == NULL)
--- 437,464 ----
           */
          if (*isNull)
          {
!             array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
              *isNull = false;
          }

          if (lIndex == NULL)
!             return array_set(array_source, i,
!                              upper.indx,
!                              sourceData,
!                              eisnull,
!                              astate->refattrlength,
!                              astate->refelemlength,
!                              astate->refelembyval,
!                              astate->refelemalign);
          else
!             return array_set_slice(array_source, i,
!                                    upper.indx, lower.indx,
!                                    sourceData,
!                                    eisnull,
!                                    astate->refattrlength,
!                                    astate->refelemlength,
!                                    astate->refelembyval,
!                                    astate->refelemalign);
      }

      if (lIndex == NULL)
*************** ExecEvalArrayRef(ArrayRefExprState *asta
*** 478,492 ****
                           astate->refelemalign,
                           isNull);
      else
!     {
!         resultArray = array_get_slice(array_source, i,
!                                       upper.indx, lower.indx,
!                                       astate->refattrlength,
!                                       astate->refelemlength,
!                                       astate->refelembyval,
!                                       astate->refelemalign);
!         return PointerGetDatum(resultArray);
!     }
  }

  /*
--- 469,480 ----
                           astate->refelemalign,
                           isNull);
      else
!         return array_get_slice(array_source, i,
!                                upper.indx, lower.indx,
!                                astate->refattrlength,
!                                astate->refelemlength,
!                                astate->refelembyval,
!                                astate->refelemalign);
  }

  /*
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 600646e..72285ef 100644
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
*************** array_push(PG_FUNCTION_ARGS)
*** 146,153 ****
      typbyval = my_extra->typbyval;
      typalign = my_extra->typalign;

!     result = array_set(v, 1, &indx, newelem, isNull,
!                        -1, typlen, typbyval, typalign);

      /*
       * Readjust result's LB to match the input's.  This does nothing in the
--- 146,154 ----
      typbyval = my_extra->typbyval;
      typalign = my_extra->typalign;

!     result = DatumGetArrayTypeP(array_set(PointerGetDatum(v), 1, &indx,
!                                           newelem, isNull,
!                                           -1, typlen, typbyval, typalign));

      /*
       * Readjust result's LB to match the input's.  This does nothing in the
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 5591b46..aa17946 100644
*** a/src/backend/utils/adt/arrayfuncs.c
--- b/src/backend/utils/adt/arrayfuncs.c
*************** array_cardinality(PG_FUNCTION_ARGS)
*** 1796,1809 ****

  /*
   * array_ref :
!  *      This routine takes an array pointer and a subscript array and returns
   *      the referenced item as a Datum.  Note that for a pass-by-reference
   *      datatype, the returned Datum is a pointer into the array object.
   *
   * This handles both ordinary varlena arrays and fixed-length arrays.
   *
   * Inputs:
!  *    array: the array object (mustn't be NULL)
   *    nSubscripts: number of subscripts supplied
   *    indx[]: the subscript values
   *    arraytyplen: pg_type.typlen for the array type
--- 1796,1809 ----

  /*
   * array_ref :
!  *      This routine takes an array datum and a subscript array and returns
   *      the referenced item as a Datum.  Note that for a pass-by-reference
   *      datatype, the returned Datum is a pointer into the array object.
   *
   * This handles both ordinary varlena arrays and fixed-length arrays.
   *
   * Inputs:
!  *    arraydatum: the array object (mustn't be NULL)
   *    nSubscripts: number of subscripts supplied
   *    indx[]: the subscript values
   *    arraytyplen: pg_type.typlen for the array type
*************** array_cardinality(PG_FUNCTION_ARGS)
*** 1816,1822 ****
   *    *isNull is set to indicate whether the element is NULL.
   */
  Datum
! array_ref(ArrayType *array,
            int nSubscripts,
            int *indx,
            int arraytyplen,
--- 1816,1822 ----
   *    *isNull is set to indicate whether the element is NULL.
   */
  Datum
! array_ref(Datum arraydatum,
            int nSubscripts,
            int *indx,
            int arraytyplen,
*************** array_ref(ArrayType *array,
*** 1825,1830 ****
--- 1825,1831 ----
            char elmalign,
            bool *isNull)
  {
+     ArrayType  *array;
      int            i,
                  ndim,
                 *dim,
*************** array_ref(ArrayType *array,
*** 1846,1858 ****
          fixedLb[0] = 0;
          dim = fixedDim;
          lb = fixedLb;
!         arraydataptr = (char *) array;
          arraynullsptr = NULL;
      }
      else
      {
          /* detoast input array if necessary */
!         array = DatumGetArrayTypeP(PointerGetDatum(array));

          ndim = ARR_NDIM(array);
          dim = ARR_DIMS(array);
--- 1847,1859 ----
          fixedLb[0] = 0;
          dim = fixedDim;
          lb = fixedLb;
!         arraydataptr = (char *) DatumGetPointer(arraydatum);
          arraynullsptr = NULL;
      }
      else
      {
          /* detoast input array if necessary */
!         array = DatumGetArrayTypeP(arraydatum);

          ndim = ARR_NDIM(array);
          dim = ARR_DIMS(array);
*************** array_ref(ArrayType *array,
*** 1910,1916 ****
   * This handles both ordinary varlena arrays and fixed-length arrays.
   *
   * Inputs:
!  *    array: the array object (mustn't be NULL)
   *    nSubscripts: number of subscripts supplied (must be same for upper/lower)
   *    upperIndx[]: the upper subscript values
   *    lowerIndx[]: the lower subscript values
--- 1911,1917 ----
   * This handles both ordinary varlena arrays and fixed-length arrays.
   *
   * Inputs:
!  *    arraydatum: the array object (mustn't be NULL)
   *    nSubscripts: number of subscripts supplied (must be same for upper/lower)
   *    upperIndx[]: the upper subscript values
   *    lowerIndx[]: the lower subscript values
*************** array_ref(ArrayType *array,
*** 1925,1932 ****
   * NOTE: we assume it is OK to scribble on the provided subscript arrays
   * lowerIndx[] and upperIndx[].  These are generally just temporaries.
   */
! ArrayType *
! array_get_slice(ArrayType *array,
                  int nSubscripts,
                  int *upperIndx,
                  int *lowerIndx,
--- 1926,1933 ----
   * NOTE: we assume it is OK to scribble on the provided subscript arrays
   * lowerIndx[] and upperIndx[].  These are generally just temporaries.
   */
! Datum
! array_get_slice(Datum arraydatum,
                  int nSubscripts,
                  int *upperIndx,
                  int *lowerIndx,
*************** array_get_slice(ArrayType *array,
*** 1935,1940 ****
--- 1936,1942 ----
                  bool elmbyval,
                  char elmalign)
  {
+     ArrayType  *array;
      ArrayType  *newarray;
      int            i,
                  ndim,
*************** array_get_slice(ArrayType *array,
*** 1973,1985 ****
          dim = fixedDim;
          lb = fixedLb;
          elemtype = InvalidOid;    /* XXX */
!         arraydataptr = (char *) array;
          arraynullsptr = NULL;
      }
      else
      {
          /* detoast input array if necessary */
!         array = DatumGetArrayTypeP(PointerGetDatum(array));

          ndim = ARR_NDIM(array);
          dim = ARR_DIMS(array);
--- 1975,1987 ----
          dim = fixedDim;
          lb = fixedLb;
          elemtype = InvalidOid;    /* XXX */
!         arraydataptr = (char *) DatumGetPointer(arraydatum);
          arraynullsptr = NULL;
      }
      else
      {
          /* detoast input array if necessary */
!         array = DatumGetArrayTypeP(arraydatum);

          ndim = ARR_NDIM(array);
          dim = ARR_DIMS(array);
*************** array_get_slice(ArrayType *array,
*** 1995,2001 ****
       * slice, return an empty array.
       */
      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
!         return construct_empty_array(elemtype);

      for (i = 0; i < nSubscripts; i++)
      {
--- 1997,2003 ----
       * slice, return an empty array.
       */
      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
!         return PointerGetDatum(construct_empty_array(elemtype));

      for (i = 0; i < nSubscripts; i++)
      {
*************** array_get_slice(ArrayType *array,
*** 2004,2010 ****
          if (upperIndx[i] >= (dim[i] + lb[i]))
              upperIndx[i] = dim[i] + lb[i] - 1;
          if (lowerIndx[i] > upperIndx[i])
!             return construct_empty_array(elemtype);
      }
      /* fill any missing subscript positions with full array range */
      for (; i < ndim; i++)
--- 2006,2012 ----
          if (upperIndx[i] >= (dim[i] + lb[i]))
              upperIndx[i] = dim[i] + lb[i] - 1;
          if (lowerIndx[i] > upperIndx[i])
!             return PointerGetDatum(construct_empty_array(elemtype));
      }
      /* fill any missing subscript positions with full array range */
      for (; i < ndim; i++)
*************** array_get_slice(ArrayType *array,
*** 2012,2018 ****
          lowerIndx[i] = lb[i];
          upperIndx[i] = dim[i] + lb[i] - 1;
          if (lowerIndx[i] > upperIndx[i])
!             return construct_empty_array(elemtype);
      }

      mda_get_range(ndim, span, lowerIndx, upperIndx);
--- 2014,2020 ----
          lowerIndx[i] = lb[i];
          upperIndx[i] = dim[i] + lb[i] - 1;
          if (lowerIndx[i] > upperIndx[i])
!             return PointerGetDatum(construct_empty_array(elemtype));
      }

      mda_get_range(ndim, span, lowerIndx, upperIndx);
*************** array_get_slice(ArrayType *array,
*** 2058,2064 ****
                          lowerIndx, upperIndx,
                          elmlen, elmbyval, elmalign);

!     return newarray;
  }

  /*
--- 2060,2066 ----
                          lowerIndx, upperIndx,
                          elmlen, elmbyval, elmalign);

!     return PointerGetDatum(newarray);
  }

  /*
*************** array_get_slice(ArrayType *array,
*** 2069,2075 ****
   * This handles both ordinary varlena arrays and fixed-length arrays.
   *
   * Inputs:
!  *    array: the initial array object (mustn't be NULL)
   *    nSubscripts: number of subscripts supplied
   *    indx[]: the subscript values
   *    dataValue: the datum to be inserted at the given position
--- 2071,2077 ----
   * This handles both ordinary varlena arrays and fixed-length arrays.
   *
   * Inputs:
!  *    arraydatum: the initial array object (mustn't be NULL)
   *    nSubscripts: number of subscripts supplied
   *    indx[]: the subscript values
   *    dataValue: the datum to be inserted at the given position
*************** array_get_slice(ArrayType *array,
*** 2091,2098 ****
   * NOTE: For assignments, we throw an error for invalid subscripts etc,
   * rather than returning a NULL as the fetch operations do.
   */
! ArrayType *
! array_set(ArrayType *array,
            int nSubscripts,
            int *indx,
            Datum dataValue,
--- 2093,2100 ----
   * NOTE: For assignments, we throw an error for invalid subscripts etc,
   * rather than returning a NULL as the fetch operations do.
   */
! Datum
! array_set(Datum arraydatum,
            int nSubscripts,
            int *indx,
            Datum dataValue,
*************** array_set(ArrayType *array,
*** 2102,2107 ****
--- 2104,2110 ----
            bool elmbyval,
            char elmalign)
  {
+     ArrayType  *array;
      ArrayType  *newarray;
      int            i,
                  ndim,
*************** array_set(ArrayType *array,
*** 2130,2135 ****
--- 2133,2140 ----
           * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
           * cannot extend them, either.
           */
+         char       *resultarray;
+
          if (nSubscripts != 1)
              ereport(ERROR,
                      (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
*************** array_set(ArrayType *array,
*** 2145,2155 ****
                      (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                       errmsg("cannot assign null value to an element of a fixed-length array")));

!         newarray = (ArrayType *) palloc(arraytyplen);
!         memcpy(newarray, array, arraytyplen);
!         elt_ptr = (char *) newarray + indx[0] * elmlen;
          ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
!         return newarray;
      }

      if (nSubscripts <= 0 || nSubscripts > MAXDIM)
--- 2150,2160 ----
                      (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                       errmsg("cannot assign null value to an element of a fixed-length array")));

!         resultarray = (char *) palloc(arraytyplen);
!         memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
!         elt_ptr = (char *) resultarray + indx[0] * elmlen;
          ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
!         return PointerGetDatum(resultarray);
      }

      if (nSubscripts <= 0 || nSubscripts > MAXDIM)
*************** array_set(ArrayType *array,
*** 2162,2168 ****
          dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));

      /* detoast input array if necessary */
!     array = DatumGetArrayTypeP(PointerGetDatum(array));

      ndim = ARR_NDIM(array);

--- 2167,2173 ----
          dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));

      /* detoast input array if necessary */
!     array = DatumGetArrayTypeP(arraydatum);

      ndim = ARR_NDIM(array);

*************** array_set(ArrayType *array,
*** 2181,2189 ****
              lb[i] = indx[i];
          }

!         return construct_md_array(&dataValue, &isNull, nSubscripts,
!                                   dim, lb, elmtype,
!                                   elmlen, elmbyval, elmalign);
      }

      if (ndim != nSubscripts)
--- 2186,2195 ----
              lb[i] = indx[i];
          }

!         return PointerGetDatum(construct_md_array(&dataValue, &isNull,
!                                                   nSubscripts, dim, lb,
!                                                   elmtype,
!                                                 elmlen, elmbyval, elmalign));
      }

      if (ndim != nSubscripts)
*************** array_set(ArrayType *array,
*** 2345,2351 ****
          }
      }

!     return newarray;
  }

  /*
--- 2351,2357 ----
          }
      }

!     return PointerGetDatum(newarray);
  }

  /*
*************** array_set(ArrayType *array,
*** 2357,2368 ****
   * This handles both ordinary varlena arrays and fixed-length arrays.
   *
   * Inputs:
!  *    array: the initial array object (mustn't be NULL)
   *    nSubscripts: number of subscripts supplied (must be same for upper/lower)
   *    upperIndx[]: the upper subscript values
   *    lowerIndx[]: the lower subscript values
!  *    srcArray: the source for the inserted values
!  *    isNull: indicates whether srcArray is NULL
   *    arraytyplen: pg_type.typlen for the array type
   *    elmlen: pg_type.typlen for the array's element type
   *    elmbyval: pg_type.typbyval for the array's element type
--- 2363,2374 ----
   * This handles both ordinary varlena arrays and fixed-length arrays.
   *
   * Inputs:
!  *    arraydatum: the initial array object (mustn't be NULL)
   *    nSubscripts: number of subscripts supplied (must be same for upper/lower)
   *    upperIndx[]: the upper subscript values
   *    lowerIndx[]: the lower subscript values
!  *    srcArrayDatum: the source for the inserted values
!  *    isNull: indicates whether srcArrayDatum is NULL
   *    arraytyplen: pg_type.typlen for the array type
   *    elmlen: pg_type.typlen for the array's element type
   *    elmbyval: pg_type.typbyval for the array's element type
*************** array_set(ArrayType *array,
*** 2383,2400 ****
   * NOTE: For assignments, we throw an error for silly subscripts etc,
   * rather than returning a NULL or empty array as the fetch operations do.
   */
! ArrayType *
! array_set_slice(ArrayType *array,
                  int nSubscripts,
                  int *upperIndx,
                  int *lowerIndx,
!                 ArrayType *srcArray,
                  bool isNull,
                  int arraytyplen,
                  int elmlen,
                  bool elmbyval,
                  char elmalign)
  {
      ArrayType  *newarray;
      int            i,
                  ndim,
--- 2389,2408 ----
   * NOTE: For assignments, we throw an error for silly subscripts etc,
   * rather than returning a NULL or empty array as the fetch operations do.
   */
! Datum
! array_set_slice(Datum arraydatum,
                  int nSubscripts,
                  int *upperIndx,
                  int *lowerIndx,
!                 Datum srcArrayDatum,
                  bool isNull,
                  int arraytyplen,
                  int elmlen,
                  bool elmbyval,
                  char elmalign)
  {
+     ArrayType  *array;
+     ArrayType  *srcArray;
      ArrayType  *newarray;
      int            i,
                  ndim,
*************** array_set_slice(ArrayType *array,
*** 2420,2426 ****

      /* Currently, assignment from a NULL source array is a no-op */
      if (isNull)
!         return array;

      if (arraytyplen > 0)
      {
--- 2428,2434 ----

      /* Currently, assignment from a NULL source array is a no-op */
      if (isNull)
!         return arraydatum;

      if (arraytyplen > 0)
      {
*************** array_set_slice(ArrayType *array,
*** 2433,2440 ****
      }

      /* detoast arrays if necessary */
!     array = DatumGetArrayTypeP(PointerGetDatum(array));
!     srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));

      /* note: we assume srcArray contains no toasted elements */

--- 2441,2448 ----
      }

      /* detoast arrays if necessary */
!     array = DatumGetArrayTypeP(arraydatum);
!     srcArray = DatumGetArrayTypeP(srcArrayDatum);

      /* note: we assume srcArray contains no toasted elements */

*************** array_set_slice(ArrayType *array,
*** 2467,2475 ****
                      (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                       errmsg("source array too small")));

!         return construct_md_array(dvalues, dnulls, nSubscripts,
!                                   dim, lb, elmtype,
!                                   elmlen, elmbyval, elmalign);
      }

      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
--- 2475,2483 ----
                      (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                       errmsg("source array too small")));

!         return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
!                                                   dim, lb, elmtype,
!                                                 elmlen, elmbyval, elmalign));
      }

      if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
*************** array_set_slice(ArrayType *array,
*** 2671,2677 ****
          }
      }

!     return newarray;
  }

  /*
--- 2679,2685 ----
          }
      }

!     return PointerGetDatum(newarray);
  }

  /*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c1d860c..67cccfd 100644
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** pg_get_functiondef(PG_FUNCTION_ARGS)
*** 2006,2012 ****
          {
              Datum        d;

!             d = array_ref(a, 1, &i,
                            -1 /* varlenarray */ ,
                            -1 /* TEXT's typlen */ ,
                            false /* TEXT's typbyval */ ,
--- 2006,2012 ----
          {
              Datum        d;

!             d = array_ref(PointerGetDatum(a), 1, &i,
                            -1 /* varlenarray */ ,
                            -1 /* TEXT's typlen */ ,
                            false /* TEXT's typbyval */ ,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9572777..648dac1 100644
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
*************** ProcessGUCArray(ArrayType *array,
*** 8916,8922 ****
          char       *name;
          char       *value;

!         d = array_ref(array, 1, &i,
                        -1 /* varlenarray */ ,
                        -1 /* TEXT's typlen */ ,
                        false /* TEXT's typbyval */ ,
--- 8916,8922 ----
          char       *name;
          char       *value;

!         d = array_ref(PointerGetDatum(array), 1, &i,
                        -1 /* varlenarray */ ,
                        -1 /* TEXT's typlen */ ,
                        false /* TEXT's typbyval */ ,
*************** GUCArrayAdd(ArrayType *array, const char
*** 8995,9001 ****
              Datum        d;
              char       *current;

!             d = array_ref(array, 1, &i,
                            -1 /* varlenarray */ ,
                            -1 /* TEXT's typlen */ ,
                            false /* TEXT's typbyval */ ,
--- 8995,9001 ----
              Datum        d;
              char       *current;

!             d = array_ref(PointerGetDatum(array), 1, &i,
                            -1 /* varlenarray */ ,
                            -1 /* TEXT's typlen */ ,
                            false /* TEXT's typbyval */ ,
*************** GUCArrayAdd(ArrayType *array, const char
*** 9013,9025 ****
              }
          }

!         a = array_set(array, 1, &index,
!                       datum,
!                       false,
!                       -1 /* varlena array */ ,
!                       -1 /* TEXT's typlen */ ,
!                       false /* TEXT's typbyval */ ,
!                       'i' /* TEXT's typalign */ );
      }
      else
          a = construct_array(&datum, 1,
--- 9013,9025 ----
              }
          }

!         a = DatumGetArrayTypeP(array_set(PointerGetDatum(array), 1, &index,
!                                          datum,
!                                          false,
!                                          -1 /* varlena array */ ,
!                                          -1 /* TEXT's typlen */ ,
!                                          false /* TEXT's typbyval */ ,
!                                          'i' /* TEXT's typalign */ ));
      }
      else
          a = construct_array(&datum, 1,
*************** GUCArrayDelete(ArrayType *array, const c
*** 9066,9072 ****
          char       *val;
          bool        isnull;

!         d = array_ref(array, 1, &i,
                        -1 /* varlenarray */ ,
                        -1 /* TEXT's typlen */ ,
                        false /* TEXT's typbyval */ ,
--- 9066,9072 ----
          char       *val;
          bool        isnull;

!         d = array_ref(PointerGetDatum(array), 1, &i,
                        -1 /* varlenarray */ ,
                        -1 /* TEXT's typlen */ ,
                        false /* TEXT's typbyval */ ,
*************** GUCArrayDelete(ArrayType *array, const c
*** 9083,9095 ****

          /* else add it to the output array */
          if (newarray)
!             newarray = array_set(newarray, 1, &index,
!                                  d,
!                                  false,
!                                  -1 /* varlenarray */ ,
!                                  -1 /* TEXT's typlen */ ,
!                                  false /* TEXT's typbyval */ ,
!                                  'i' /* TEXT's typalign */ );
          else
              newarray = construct_array(&d, 1,
                                         TEXTOID,
--- 9083,9097 ----

          /* else add it to the output array */
          if (newarray)
!             newarray =
!                 DatumGetArrayTypeP(array_set(PointerGetDatum(newarray),
!                                              1, &index,
!                                              d,
!                                              false,
!                                              -1 /* varlenarray */ ,
!                                              -1 /* TEXT's typlen */ ,
!                                              false /* TEXT's typbyval */ ,
!                                              'i' /* TEXT's typalign */ ));
          else
              newarray = construct_array(&d, 1,
                                         TEXTOID,
*************** GUCArrayReset(ArrayType *array)
*** 9132,9138 ****
          char       *eqsgn;
          bool        isnull;

!         d = array_ref(array, 1, &i,
                        -1 /* varlenarray */ ,
                        -1 /* TEXT's typlen */ ,
                        false /* TEXT's typbyval */ ,
--- 9134,9140 ----
          char       *eqsgn;
          bool        isnull;

!         d = array_ref(PointerGetDatum(array), 1, &i,
                        -1 /* varlenarray */ ,
                        -1 /* TEXT's typlen */ ,
                        false /* TEXT's typbyval */ ,
*************** GUCArrayReset(ArrayType *array)
*** 9151,9163 ****

          /* else add it to the output array */
          if (newarray)
!             newarray = array_set(newarray, 1, &index,
!                                  d,
!                                  false,
!                                  -1 /* varlenarray */ ,
!                                  -1 /* TEXT's typlen */ ,
!                                  false /* TEXT's typbyval */ ,
!                                  'i' /* TEXT's typalign */ );
          else
              newarray = construct_array(&d, 1,
                                         TEXTOID,
--- 9153,9167 ----

          /* else add it to the output array */
          if (newarray)
!             newarray =
!                 DatumGetArrayTypeP(array_set(PointerGetDatum(newarray),
!                                              1, &index,
!                                              d,
!                                              false,
!                                              -1 /* varlenarray */ ,
!                                              -1 /* TEXT's typlen */ ,
!                                              false /* TEXT's typbyval */ ,
!                                              'i' /* TEXT's typalign */ ));
          else
              newarray = construct_array(&d, 1,
                                         TEXTOID,
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 694bce7..b2d3fde 100644
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
*************** extern Datum array_remove(PG_FUNCTION_AR
*** 248,265 ****
  extern Datum array_replace(PG_FUNCTION_ARGS);
  extern Datum width_bucket_array(PG_FUNCTION_ARGS);

! extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
            int arraytyplen, int elmlen, bool elmbyval, char elmalign,
            bool *isNull);
! extern ArrayType *array_set(ArrayType *array, int nSubscripts, int *indx,
            Datum dataValue, bool isNull,
            int arraytyplen, int elmlen, bool elmbyval, char elmalign);
! extern ArrayType *array_get_slice(ArrayType *array, int nSubscripts,
                  int *upperIndx, int *lowerIndx,
                  int arraytyplen, int elmlen, bool elmbyval, char elmalign);
! extern ArrayType *array_set_slice(ArrayType *array, int nSubscripts,
                  int *upperIndx, int *lowerIndx,
!                 ArrayType *srcArray, bool isNull,
                  int arraytyplen, int elmlen, bool elmbyval, char elmalign);

  extern Datum array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
--- 248,265 ----
  extern Datum array_replace(PG_FUNCTION_ARGS);
  extern Datum width_bucket_array(PG_FUNCTION_ARGS);

! extern Datum array_ref(Datum arraydatum, int nSubscripts, int *indx,
            int arraytyplen, int elmlen, bool elmbyval, char elmalign,
            bool *isNull);
! extern Datum array_set(Datum arraydatum, int nSubscripts, int *indx,
            Datum dataValue, bool isNull,
            int arraytyplen, int elmlen, bool elmbyval, char elmalign);
! extern Datum array_get_slice(Datum arraydatum, int nSubscripts,
                  int *upperIndx, int *lowerIndx,
                  int arraytyplen, int elmlen, bool elmbyval, char elmalign);
! extern Datum array_set_slice(Datum arraydatum, int nSubscripts,
                  int *upperIndx, int *lowerIndx,
!                 Datum srcArrayDatum, bool isNull,
                  int arraytyplen, int elmlen, bool elmbyval, char elmalign);

  extern Datum array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index ae5421f..a754727 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** exec_assign_value(PLpgSQL_execstate *est
*** 4233,4244 ****
                  PLpgSQL_expr *subscripts[MAXDIM];
                  int            subscriptvals[MAXDIM];
                  Datum        oldarraydatum,
                              coerced_value;
                  bool        oldarrayisnull;
                  Oid            parenttypoid;
                  int32        parenttypmod;
-                 ArrayType  *oldarrayval;
-                 ArrayType  *newarrayval;
                  SPITupleTable *save_eval_tuptable;
                  MemoryContext oldcontext;

--- 4233,4243 ----
                  PLpgSQL_expr *subscripts[MAXDIM];
                  int            subscriptvals[MAXDIM];
                  Datum        oldarraydatum,
+                             newarraydatum,
                              coerced_value;
                  bool        oldarrayisnull;
                  Oid            parenttypoid;
                  int32        parenttypmod;
                  SPITupleTable *save_eval_tuptable;
                  MemoryContext oldcontext;

*************** exec_assign_value(PLpgSQL_execstate *est
*** 4378,4403 ****
                      (oldarrayisnull || *isNull))
                      return;

!                 /* oldarrayval and newarrayval should be short-lived */
                  oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);

                  if (oldarrayisnull)
!                     oldarrayval = construct_empty_array(arrayelem->elemtypoid);
!                 else
!                     oldarrayval = (ArrayType *) DatumGetPointer(oldarraydatum);

                  /*
                   * Build the modified array value.
                   */
!                 newarrayval = array_set(oldarrayval,
!                                         nsubscripts,
!                                         subscriptvals,
!                                         coerced_value,
!                                         *isNull,
!                                         arrayelem->arraytyplen,
!                                         arrayelem->elemtyplen,
!                                         arrayelem->elemtypbyval,
!                                         arrayelem->elemtypalign);

                  MemoryContextSwitchTo(oldcontext);

--- 4377,4400 ----
                      (oldarrayisnull || *isNull))
                      return;

!                 /* empty array, if any, and newarraydatum are short-lived */
                  oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);

                  if (oldarrayisnull)
!                     oldarraydatum = PointerGetDatum(construct_empty_array(arrayelem->elemtypoid));

                  /*
                   * Build the modified array value.
                   */
!                 newarraydatum = array_set(oldarraydatum,
!                                           nsubscripts,
!                                           subscriptvals,
!                                           coerced_value,
!                                           *isNull,
!                                           arrayelem->arraytyplen,
!                                           arrayelem->elemtyplen,
!                                           arrayelem->elemtypbyval,
!                                           arrayelem->elemtypalign);

                  MemoryContextSwitchTo(oldcontext);

*************** exec_assign_value(PLpgSQL_execstate *est
*** 4409,4415 ****
                   */
                  *isNull = false;
                  exec_assign_value(estate, target,
!                                   PointerGetDatum(newarrayval),
                                    arrayelem->arraytypoid, isNull);
                  break;
              }
--- 4406,4412 ----
                   */
                  *isNull = false;
                  exec_assign_value(estate, target,
!                                   newarraydatum,
                                    arrayelem->arraytypoid, isNull);
                  break;
              }
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 524534e..4c184c5 100644
*** a/src/pl/plpython/plpy_typeio.c
--- b/src/pl/plpython/plpy_typeio.c
*************** PLyList_FromArray(PLyDatumToOb *arg, Dat
*** 628,634 ****
          int            offset;

          offset = lbound + i;
!         elem = array_ref(array, 1, &offset, arg->typlen,
                           elm->typlen, elm->typbyval, elm->typalign,
                           &isnull);
          if (isnull)
--- 628,634 ----
          int            offset;

          offset = lbound + i;
!         elem = array_ref(PointerGetDatum(array), 1, &offset, arg->typlen,
                           elm->typlen, elm->typbyval, elm->typalign,
                           &isnull);
          if (isnull)

pgsql-hackers by date:

Previous
From: Tomas Vondra
Date:
Subject: Re: Really bad blowups with hash outer join and nulls
Next
From: David Steele
Date:
Subject: Re: Issue installing doc tools on OSX