mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
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.
This commit is contained in:
@ -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);
|
||||
|
Reference in New Issue
Block a user