aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-11-17 22:14:56 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-11-17 22:14:56 +0000
commitcecb6075594a407b7adcd9c9a0c243ca4b43c9a3 (patch)
treed3febb775476b082255aa6122b0ba80a8ba79b37 /src/backend/executor
parentc859308aba7edef428994e6de90ff35f35a328c5 (diff)
downloadpostgresql-cecb6075594a407b7adcd9c9a0c243ca4b43c9a3.tar.gz
postgresql-cecb6075594a407b7adcd9c9a0c243ca4b43c9a3.zip
Make SQL arrays support null elements. This commit fixes the core array
functionality, but I still need to make another pass looking at places that incidentally use arrays (such as ACL manipulation) to make sure they are null-safe. Contrib needs work too. I have not changed the behaviors that are still under discussion about array comparison and what to do with lower bounds.
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execQual.c244
1 files changed, 136 insertions, 108 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 4ee9a4ca622..7debc3fcd59 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.183 2005/10/19 22:30:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.184 2005/11/17 22:14:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -202,16 +202,8 @@ static Datum ExecEvalRelabelType(GenericExprState *exprstate,
* if it's a simple reference, or the modified array value if it's
* an array assignment (i.e., array element or slice insertion).
*
- * NOTE: if we get a NULL result from a subexpression, we return NULL when
- * it's an array reference, or the unmodified source array when it's an
- * array assignment. This may seem peculiar, but if we return NULL (as was
- * done in versions up through 7.0) then an assignment like
- * UPDATE table SET arrayfield[4] = NULL
- * will result in setting the whole array to NULL, which is certainly not
- * very desirable. By returning the source array we make the assignment
- * into a no-op, instead. (Eventually we need to redesign arrays so that
- * individual elements can be NULL, but for now, let's try to protect users
- * from shooting themselves in the foot.)
+ * 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
@@ -270,15 +262,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
econtext,
&eisnull,
NULL));
- /* If any index expr yields NULL, result is NULL or source array */
+ /* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
- if (!isAssignment)
- {
- *isNull = true;
- return (Datum) NULL;
- }
- return PointerGetDatum(array_source);
+ if (isAssignment)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("array subscript in assignment must not be NULL")));
+ *isNull = true;
+ return (Datum) NULL;
}
}
@@ -298,18 +290,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
econtext,
&eisnull,
NULL));
-
- /*
- * If any index expr yields NULL, result is NULL or source array
- */
+ /* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
- if (!isAssignment)
- {
- *isNull = true;
- return (Datum) NULL;
- }
- return PointerGetDatum(array_source);
+ if (isAssignment)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("array subscript in assignment must not be NULL")));
+ *isNull = true;
+ return (Datum) NULL;
}
}
/* this can't happen unless parser messed up */
@@ -327,8 +316,8 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
/*
* Evaluate the value to be assigned into the array.
*
- * XXX At some point we'll need to look into making the old value of the
- * array element available via CaseTestExpr, as is done by
+ * XXX At some point we'll need to look into making the old value of
+ * the array element available via CaseTestExpr, as is done by
* ExecEvalFieldStore. This is not needed now but will be needed to
* support arrays of composite types; in an assignment to a field of
* an array member, the parser would generate a FieldStore that
@@ -340,29 +329,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
NULL);
/*
- * For now, can't cope with inserting NULL into an array, so make it a
- * no-op per discussion above...
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
*/
- if (eisnull)
- return PointerGetDatum(array_source);
+ if (astate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *isNull)
+ return PointerGetDatum(array_source);
/*
- * For an assignment, if all the subscripts and the input expression
- * are non-null but the original array is null, then substitute an
- * empty (zero-dimensional) array and proceed with the assignment.
- * This only works for varlena arrays, though; for fixed-length array
- * types we punt and return the null input array.
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of
+ * the new element will result in a singleton array value. It does
+ * not matter whether the new element is NULL.
*/
if (*isNull)
{
- if (astate->refattrlength > 0) /* fixed-length array? */
- return PointerGetDatum(array_source);
-
- array_source = construct_md_array(NULL, 0, NULL, NULL,
- arrayRef->refelemtype,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
+ array_source = construct_empty_array(arrayRef->refelemtype);
*isNull = false;
}
@@ -370,20 +353,20 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
resultArray = array_set(array_source, i,
upper.indx,
sourceData,
+ eisnull,
astate->refattrlength,
astate->refelemlength,
astate->refelembyval,
- astate->refelemalign,
- isNull);
+ 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,
- isNull);
+ astate->refelemalign);
return PointerGetDatum(resultArray);
}
@@ -401,8 +384,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
astate->refattrlength,
astate->refelemlength,
astate->refelembyval,
- astate->refelemalign,
- isNull);
+ astate->refelemalign);
return PointerGetDatum(resultArray);
}
}
@@ -1620,6 +1602,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
bool typbyval;
char typalign;
char *s;
+ bits8 *bitmap;
+ int bitmask;
/* Set default values for result flags: non-null, not a set result */
*isNull = false;
@@ -1668,9 +1652,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
return BoolGetDatum(!useOr);
/*
- * If the scalar is NULL, and the function is strict, return NULL. This is
- * just to avoid having to test for strictness inside the loop. (XXX but
- * if arrays could have null elements, we'd need a test anyway.)
+ * If the scalar is NULL, and the function is strict, return NULL;
+ * no point in iterating the loop.
*/
if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict)
{
@@ -1699,22 +1682,40 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
/* Loop over the array elements */
s = (char *) ARR_DATA_PTR(arr);
+ bitmap = ARR_NULLBITMAP(arr);
+ bitmask = 1;
+
for (i = 0; i < nitems; i++)
{
Datum elt;
Datum thisresult;
- /* Get array element */
- elt = fetch_att(s, typbyval, typlen);
-
- s = att_addlength(s, typlen, PointerGetDatum(s));
- s = (char *) att_align(s, typalign);
+ /* Get array element, checking for NULL */
+ if (bitmap && (*bitmap & bitmask) == 0)
+ {
+ fcinfo.arg[1] = (Datum) 0;
+ fcinfo.argnull[1] = true;
+ }
+ else
+ {
+ elt = fetch_att(s, typbyval, typlen);
+ s = att_addlength(s, typlen, PointerGetDatum(s));
+ s = (char *) att_align(s, typalign);
+ fcinfo.arg[1] = elt;
+ fcinfo.argnull[1] = false;
+ }
/* Call comparison function */
- fcinfo.arg[1] = elt;
- fcinfo.argnull[1] = false;
- fcinfo.isnull = false;
- thisresult = FunctionCallInvoke(&fcinfo);
+ if (fcinfo.argnull[1] && sstate->fxprstate.func.fn_strict)
+ {
+ fcinfo.isnull = true;
+ thisresult = (Datum) 0;
+ }
+ else
+ {
+ fcinfo.isnull = false;
+ thisresult = FunctionCallInvoke(&fcinfo);
+ }
/* Combine results per OR or AND semantics */
if (fcinfo.isnull)
@@ -1737,6 +1738,17 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
break; /* needn't look at any more elements */
}
}
+
+ /* advance bitmap pointer if any */
+ if (bitmap)
+ {
+ bitmask <<= 1;
+ if (bitmask == 0x100)
+ {
+ bitmap++;
+ bitmask = 1;
+ }
+ }
}
*isNull = resultnull;
@@ -2053,10 +2065,6 @@ ExecEvalCaseTestExpr(ExprState *exprstate,
/* ----------------------------------------------------------------
* ExecEvalArray - ARRAY[] expressions
- *
- * NOTE: currently, if any input value is NULL then we return a NULL array,
- * so the ARRAY[] construct can be considered strict. Eventually this will
- * change; when it does, be sure to fix contain_nonstrict_functions().
* ----------------------------------------------------------------
*/
static Datum
@@ -2081,39 +2089,33 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
/* Elements are presumably of scalar type */
int nelems;
Datum *dvalues;
+ bool *dnulls;
int i = 0;
ndims = 1;
nelems = list_length(astate->elements);
- /* Shouldn't happen here, but if length is 0, return NULL */
+ /* Shouldn't happen here, but if length is 0, return empty array */
if (nelems == 0)
- {
- *isNull = true;
- return (Datum) 0;
- }
+ return PointerGetDatum(construct_empty_array(element_type));
dvalues = (Datum *) palloc(nelems * sizeof(Datum));
+ dnulls = (bool *) palloc(nelems * sizeof(bool));
/* loop through and build array of datums */
foreach(element, astate->elements)
{
ExprState *e = (ExprState *) lfirst(element);
- bool eisnull;
- dvalues[i++] = ExecEvalExpr(e, econtext, &eisnull, NULL);
- if (eisnull)
- {
- *isNull = true;
- return (Datum) 0;
- }
+ dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL);
+ i++;
}
/* setup for 1-D array of the given length */
dims[0] = nelems;
lbs[0] = 1;
- result = construct_md_array(dvalues, ndims, dims, lbs,
+ result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
element_type,
astate->elemlength,
astate->elembyval,
@@ -2122,15 +2124,28 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
else
{
/* Must be nested array expressions */
- char *dat = NULL;
- Size ndatabytes = 0;
- int nbytes;
- int outer_nelems = list_length(astate->elements);
+ int nbytes = 0;
+ int nitems = 0;
+ int outer_nelems = 0;
int elem_ndims = 0;
int *elem_dims = NULL;
int *elem_lbs = NULL;
bool firstone = true;
+ bool havenulls = false;
+ char **subdata;
+ bits8 **subbitmaps;
+ int *subbytes;
+ int *subnitems;
int i;
+ int32 dataoffset;
+ char *dat;
+ int iitem;
+
+ i = list_length(astate->elements);
+ subdata = (char **) palloc(i * sizeof(char *));
+ subbitmaps = (bits8 **) palloc(i * sizeof(bits8 *));
+ subbytes = (int *) palloc(i * sizeof(int));
+ subnitems = (int *) palloc(i * sizeof(int));
/* loop through and get data area from each element */
foreach(element, astate->elements)
@@ -2139,14 +2154,11 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
bool eisnull;
Datum arraydatum;
ArrayType *array;
- int elem_ndatabytes;
arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL);
+ /* ignore null subarrays */
if (eisnull)
- {
- *isNull = true;
- return (Datum) 0;
- }
+ continue;
array = DatumGetArrayTypeP(arraydatum);
@@ -2192,16 +2204,15 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
"expressions with matching dimensions")));
}
- elem_ndatabytes = ARR_SIZE(array) - ARR_OVERHEAD(elem_ndims);
- ndatabytes += elem_ndatabytes;
- if (dat == NULL)
- dat = (char *) palloc(ndatabytes);
- else
- dat = (char *) repalloc(dat, ndatabytes);
-
- memcpy(dat + (ndatabytes - elem_ndatabytes),
- ARR_DATA_PTR(array),
- elem_ndatabytes);
+ subdata[outer_nelems] = ARR_DATA_PTR(array);
+ subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
+ subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
+ nbytes += subbytes[outer_nelems];
+ subnitems[outer_nelems] = ArrayGetNItems(ARR_NDIM(array),
+ ARR_DIMS(array));
+ nitems += subnitems[outer_nelems];
+ havenulls |= ARR_HASNULL(array);
+ outer_nelems++;
}
/* setup for multi-D array */
@@ -2213,20 +2224,37 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
lbs[i] = elem_lbs[i - 1];
}
- nbytes = ndatabytes + ARR_OVERHEAD(ndims);
- result = (ArrayType *) palloc(nbytes);
+ if (havenulls)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0; /* marker for no null bitmap */
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+ result = (ArrayType *) palloc(nbytes);
result->size = nbytes;
result->ndim = ndims;
- result->flags = 0;
+ result->dataoffset = dataoffset;
result->elemtype = element_type;
memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
- if (ndatabytes > 0)
- memcpy(ARR_DATA_PTR(result), dat, ndatabytes);
- if (dat != NULL)
- pfree(dat);
+ dat = ARR_DATA_PTR(result);
+ iitem = 0;
+ for (i = 0; i < outer_nelems; i++)
+ {
+ memcpy(dat, subdata[i], subbytes[i]);
+ dat += subbytes[i];
+ if (havenulls)
+ array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
+ subbitmaps[i], 0,
+ subnitems[i]);
+ iitem += subnitems[i];
+ }
}
return PointerGetDatum(result);