From 1d0d8d3c38e4c5611769c0cd9dd9c0faeee57b84 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 18 Nov 2005 02:38:24 +0000 Subject: [PATCH] Mop-up for nulls-in-arrays patch: fix some places that access array contents directly. --- contrib/dblink/dblink.c | 312 +++++++++++------------------ contrib/tsearch2/rank.c | 21 +- contrib/tsearch2/snowball/header.h | 3 - contrib/tsearch2/ts_cfg.c | 2 + src/backend/utils/adt/acl.c | 47 ++++- src/backend/utils/adt/varlena.c | 54 +++-- src/include/utils/acl.h | 25 +-- src/pl/plpgsql/src/pl_exec.c | 61 +++--- 8 files changed, 240 insertions(+), 285 deletions(-) diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c index 54e787bb20..bb646807c5 100644 --- a/contrib/dblink/dblink.c +++ b/contrib/dblink/dblink.c @@ -73,6 +73,7 @@ static HTAB *createConnHash(void); static void createNewConnection(const char *name, remoteConn * rconn); static void deleteConnection(const char *name); static char **get_pkey_attnames(Oid relid, int16 *numatts); +static char **get_text_array_contents(ArrayType *array, int *numitems); static char *get_sql_insert(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals); static char *get_sql_delete(Oid relid, int2vector *pkattnums, int16 pknumatts, char **tgt_pkattvals); static char *get_sql_update(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals); @@ -1120,29 +1121,18 @@ PG_FUNCTION_INFO_V1(dblink_build_sql_insert); Datum dblink_build_sql_insert(PG_FUNCTION_ARGS) { + text *relname_text = PG_GETARG_TEXT_P(0); + int2vector *pkattnums = (int2vector *) PG_GETARG_POINTER(1); + int32 pknumatts_tmp = PG_GETARG_INT32(2); + ArrayType *src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); + ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); Oid relid; - text *relname_text; - int2vector *pkattnums; - int pknumatts_tmp; int16 pknumatts = 0; char **src_pkattvals; char **tgt_pkattvals; - ArrayType *src_pkattvals_arry; - ArrayType *tgt_pkattvals_arry; - int src_ndim; - int *src_dim; int src_nitems; - int tgt_ndim; - int *tgt_dim; int tgt_nitems; - int i; - char *ptr; char *sql; - int16 typlen; - bool typbyval; - char typalign; - - relname_text = PG_GETARG_TEXT_P(0); /* * Convert relname to rel OID. @@ -1154,8 +1144,14 @@ dblink_build_sql_insert(PG_FUNCTION_ARGS) errmsg("relation \"%s\" does not exist", GET_STR(relname_text)))); - pkattnums = (int2vector *) PG_GETARG_POINTER(1); - pknumatts_tmp = PG_GETARG_INT32(2); + /* + * There should be at least one key attribute + */ + if (pknumatts_tmp <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("number of key attributes must be > 0"))); + if (pknumatts_tmp <= SHRT_MAX) pknumatts = pknumatts_tmp; else @@ -1164,24 +1160,11 @@ dblink_build_sql_insert(PG_FUNCTION_ARGS) errmsg("input for number of primary key " \ "attributes too large"))); - /* - * There should be at least one key attribute - */ - if (pknumatts == 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("number of key attributes must be > 0"))); - - src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); - tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); - /* * Source array is made up of key values that will be used to locate the * tuple of interest from the local system. */ - src_ndim = ARR_NDIM(src_pkattvals_arry); - src_dim = ARR_DIMS(src_pkattvals_arry); - src_nitems = ArrayGetNItems(src_ndim, src_dim); + src_pkattvals = get_text_array_contents(src_pkattvals_arry, &src_nitems); /* * There should be one source array key value for each key attnum @@ -1192,29 +1175,11 @@ dblink_build_sql_insert(PG_FUNCTION_ARGS) errmsg("source key array length must match number of key " \ "attributes"))); - /* - * get array of pointers to c-strings from the input source array - */ - Assert(ARR_ELEMTYPE(src_pkattvals_arry) == TEXTOID); - get_typlenbyvalalign(ARR_ELEMTYPE(src_pkattvals_arry), - &typlen, &typbyval, &typalign); - - src_pkattvals = (char **) palloc(src_nitems * sizeof(char *)); - ptr = ARR_DATA_PTR(src_pkattvals_arry); - for (i = 0; i < src_nitems; i++) - { - src_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr))); - ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr)); - ptr = (char *) att_align(ptr, typalign); - } - /* * Target array is made up of key values that will be used to build the * SQL string for use on the remote system. */ - tgt_ndim = ARR_NDIM(tgt_pkattvals_arry); - tgt_dim = ARR_DIMS(tgt_pkattvals_arry); - tgt_nitems = ArrayGetNItems(tgt_ndim, tgt_dim); + tgt_pkattvals = get_text_array_contents(tgt_pkattvals_arry, &tgt_nitems); /* * There should be one target array key value for each key attnum @@ -1225,22 +1190,6 @@ dblink_build_sql_insert(PG_FUNCTION_ARGS) errmsg("target key array length must match number of key " \ "attributes"))); - /* - * get array of pointers to c-strings from the input target array - */ - Assert(ARR_ELEMTYPE(tgt_pkattvals_arry) == TEXTOID); - get_typlenbyvalalign(ARR_ELEMTYPE(tgt_pkattvals_arry), - &typlen, &typbyval, &typalign); - - tgt_pkattvals = (char **) palloc(tgt_nitems * sizeof(char *)); - ptr = ARR_DATA_PTR(tgt_pkattvals_arry); - for (i = 0; i < tgt_nitems; i++) - { - tgt_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr))); - ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr)); - ptr = (char *) att_align(ptr, typalign); - } - /* * Prep work is finally done. Go get the SQL string. */ @@ -1272,24 +1221,15 @@ PG_FUNCTION_INFO_V1(dblink_build_sql_delete); Datum dblink_build_sql_delete(PG_FUNCTION_ARGS) { + text *relname_text = PG_GETARG_TEXT_P(0); + int2vector *pkattnums = (int2vector *) PG_GETARG_POINTER(1); + int32 pknumatts_tmp = PG_GETARG_INT32(2); + ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); Oid relid; - text *relname_text; - int2vector *pkattnums; - int pknumatts_tmp; int16 pknumatts = 0; char **tgt_pkattvals; - ArrayType *tgt_pkattvals_arry; - int tgt_ndim; - int *tgt_dim; int tgt_nitems; - int i; - char *ptr; char *sql; - int16 typlen; - bool typbyval; - char typalign; - - relname_text = PG_GETARG_TEXT_P(0); /* * Convert relname to rel OID. @@ -1301,8 +1241,14 @@ dblink_build_sql_delete(PG_FUNCTION_ARGS) errmsg("relation \"%s\" does not exist", GET_STR(relname_text)))); - pkattnums = (int2vector *) PG_GETARG_POINTER(1); - pknumatts_tmp = PG_GETARG_INT32(2); + /* + * There should be at least one key attribute + */ + if (pknumatts_tmp <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("number of key attributes must be > 0"))); + if (pknumatts_tmp <= SHRT_MAX) pknumatts = pknumatts_tmp; else @@ -1311,23 +1257,11 @@ dblink_build_sql_delete(PG_FUNCTION_ARGS) errmsg("input for number of primary key " \ "attributes too large"))); - /* - * There should be at least one key attribute - */ - if (pknumatts == 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("number of key attributes must be > 0"))); - - tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); - /* * Target array is made up of key values that will be used to build the * SQL string for use on the remote system. */ - tgt_ndim = ARR_NDIM(tgt_pkattvals_arry); - tgt_dim = ARR_DIMS(tgt_pkattvals_arry); - tgt_nitems = ArrayGetNItems(tgt_ndim, tgt_dim); + tgt_pkattvals = get_text_array_contents(tgt_pkattvals_arry, &tgt_nitems); /* * There should be one target array key value for each key attnum @@ -1338,22 +1272,6 @@ dblink_build_sql_delete(PG_FUNCTION_ARGS) errmsg("target key array length must match number of key " \ "attributes"))); - /* - * get array of pointers to c-strings from the input target array - */ - Assert(ARR_ELEMTYPE(tgt_pkattvals_arry) == TEXTOID); - get_typlenbyvalalign(ARR_ELEMTYPE(tgt_pkattvals_arry), - &typlen, &typbyval, &typalign); - - tgt_pkattvals = (char **) palloc(tgt_nitems * sizeof(char *)); - ptr = ARR_DATA_PTR(tgt_pkattvals_arry); - for (i = 0; i < tgt_nitems; i++) - { - tgt_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr))); - ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr)); - ptr = (char *) att_align(ptr, typalign); - } - /* * Prep work is finally done. Go get the SQL string. */ @@ -1389,29 +1307,18 @@ PG_FUNCTION_INFO_V1(dblink_build_sql_update); Datum dblink_build_sql_update(PG_FUNCTION_ARGS) { + text *relname_text = PG_GETARG_TEXT_P(0); + int2vector *pkattnums = (int2vector *) PG_GETARG_POINTER(1); + int32 pknumatts_tmp = PG_GETARG_INT32(2); + ArrayType *src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); + ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); Oid relid; - text *relname_text; - int2vector *pkattnums; - int pknumatts_tmp; int16 pknumatts = 0; char **src_pkattvals; char **tgt_pkattvals; - ArrayType *src_pkattvals_arry; - ArrayType *tgt_pkattvals_arry; - int src_ndim; - int *src_dim; int src_nitems; - int tgt_ndim; - int *tgt_dim; int tgt_nitems; - int i; - char *ptr; char *sql; - int16 typlen; - bool typbyval; - char typalign; - - relname_text = PG_GETARG_TEXT_P(0); /* * Convert relname to rel OID. @@ -1423,8 +1330,14 @@ dblink_build_sql_update(PG_FUNCTION_ARGS) errmsg("relation \"%s\" does not exist", GET_STR(relname_text)))); - pkattnums = (int2vector *) PG_GETARG_POINTER(1); - pknumatts_tmp = PG_GETARG_INT32(2); + /* + * There should be one source array key values for each key attnum + */ + if (pknumatts_tmp <= 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("number of key attributes must be > 0"))); + if (pknumatts_tmp <= SHRT_MAX) pknumatts = pknumatts_tmp; else @@ -1433,24 +1346,11 @@ dblink_build_sql_update(PG_FUNCTION_ARGS) errmsg("input for number of primary key " \ "attributes too large"))); - /* - * There should be one source array key values for each key attnum - */ - if (pknumatts == 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("number of key attributes must be > 0"))); - - src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); - tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(4); - /* * Source array is made up of key values that will be used to locate the * tuple of interest from the local system. */ - src_ndim = ARR_NDIM(src_pkattvals_arry); - src_dim = ARR_DIMS(src_pkattvals_arry); - src_nitems = ArrayGetNItems(src_ndim, src_dim); + src_pkattvals = get_text_array_contents(src_pkattvals_arry, &src_nitems); /* * There should be one source array key value for each key attnum @@ -1461,29 +1361,11 @@ dblink_build_sql_update(PG_FUNCTION_ARGS) errmsg("source key array length must match number of key " \ "attributes"))); - /* - * get array of pointers to c-strings from the input source array - */ - Assert(ARR_ELEMTYPE(src_pkattvals_arry) == TEXTOID); - get_typlenbyvalalign(ARR_ELEMTYPE(src_pkattvals_arry), - &typlen, &typbyval, &typalign); - - src_pkattvals = (char **) palloc(src_nitems * sizeof(char *)); - ptr = ARR_DATA_PTR(src_pkattvals_arry); - for (i = 0; i < src_nitems; i++) - { - src_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr))); - ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr)); - ptr = (char *) att_align(ptr, typalign); - } - /* * Target array is made up of key values that will be used to build the * SQL string for use on the remote system. */ - tgt_ndim = ARR_NDIM(tgt_pkattvals_arry); - tgt_dim = ARR_DIMS(tgt_pkattvals_arry); - tgt_nitems = ArrayGetNItems(tgt_ndim, tgt_dim); + tgt_pkattvals = get_text_array_contents(tgt_pkattvals_arry, &tgt_nitems); /* * There should be one target array key value for each key attnum @@ -1494,22 +1376,6 @@ dblink_build_sql_update(PG_FUNCTION_ARGS) errmsg("target key array length must match number of key " \ "attributes"))); - /* - * get array of pointers to c-strings from the input target array - */ - Assert(ARR_ELEMTYPE(tgt_pkattvals_arry) == TEXTOID); - get_typlenbyvalalign(ARR_ELEMTYPE(tgt_pkattvals_arry), - &typlen, &typbyval, &typalign); - - tgt_pkattvals = (char **) palloc(tgt_nitems * sizeof(char *)); - ptr = ARR_DATA_PTR(tgt_pkattvals_arry); - for (i = 0; i < tgt_nitems; i++) - { - tgt_pkattvals[i] = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(ptr))); - ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr)); - ptr = (char *) att_align(ptr, typalign); - } - /* * Prep work is finally done. Go get the SQL string. */ @@ -1598,6 +1464,67 @@ get_pkey_attnames(Oid relid, int16 *numatts) return result; } +/* + * Deconstruct a text[] into C-strings (note any NULL elements will be + * returned as NULL pointers) + */ +static char ** +get_text_array_contents(ArrayType *array, int *numitems) +{ + int ndim = ARR_NDIM(array); + int *dims = ARR_DIMS(array); + int nitems; + int16 typlen; + bool typbyval; + char typalign; + char **values; + char *ptr; + bits8 *bitmap; + int bitmask; + int i; + + Assert(ARR_ELEMTYPE(array) == TEXTOID); + + *numitems = nitems = ArrayGetNItems(ndim, dims); + + get_typlenbyvalalign(ARR_ELEMTYPE(array), + &typlen, &typbyval, &typalign); + + values = (char **) palloc(nitems * sizeof(char *)); + + ptr = ARR_DATA_PTR(array); + bitmap = ARR_NULLBITMAP(array); + bitmask = 1; + + for (i = 0; i < nitems; i++) + { + if (bitmap && (*bitmap & bitmask) == 0) + { + values[i] = NULL; + } + else + { + values[i] = DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(ptr))); + ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr)); + ptr = (char *) att_align(ptr, typalign); + } + + /* advance bitmap pointer if any */ + if (bitmap) + { + bitmask <<= 1; + if (bitmask == 0x100) + { + bitmap++; + bitmask = 1; + } + } + } + + return values; +} + static char * get_sql_insert(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals) { @@ -1665,7 +1592,7 @@ get_sql_insert(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka key = -1; if (key > -1) - val = pstrdup(tgt_pkattvals[key]); + val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL; else val = SPI_getvalue(tuple, tupdesc, i + 1); @@ -1697,7 +1624,6 @@ get_sql_delete(Oid relid, int2vector *pkattnums, int16 pknumatts, char **tgt_pka int natts; StringInfo str = makeStringInfo(); char *sql; - char *val = NULL; int i; /* get relation name including any needed schema prefix and quoting */ @@ -1721,17 +1647,13 @@ get_sql_delete(Oid relid, int2vector *pkattnums, int16 pknumatts, char **tgt_pka appendStringInfo(str, "%s", quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); - if (tgt_pkattvals != NULL) - val = pstrdup(tgt_pkattvals[i]); - else + if (tgt_pkattvals == NULL) /* internal error */ elog(ERROR, "target key array must not be NULL"); - if (val != NULL) - { - appendStringInfo(str, " = %s", quote_literal_cstr(val)); - pfree(val); - } + if (tgt_pkattvals[i] != NULL) + appendStringInfo(str, " = %s", + quote_literal_cstr(tgt_pkattvals[i])); else appendStringInfo(str, " IS NULL"); } @@ -1795,7 +1717,7 @@ get_sql_update(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka key = -1; if (key > -1) - val = pstrdup(tgt_pkattvals[key]); + val = tgt_pkattvals[key] ? pstrdup(tgt_pkattvals[key]) : NULL; else val = SPI_getvalue(tuple, tupdesc, i + 1); @@ -1822,7 +1744,7 @@ get_sql_update(Oid relid, int2vector *pkattnums, int16 pknumatts, char **src_pka quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); if (tgt_pkattvals != NULL) - val = pstrdup(tgt_pkattvals[i]); + val = tgt_pkattvals[i] ? pstrdup(tgt_pkattvals[i]) : NULL; else val = SPI_getvalue(tuple, tupdesc, pkattnum); @@ -1905,7 +1827,6 @@ get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char ** int ret; HeapTuple tuple; int i; - char *val = NULL; /* get relation name including any needed schema prefix and quoting */ relname = generate_relation_name(relid); @@ -1940,12 +1861,9 @@ get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 pknumatts, char ** appendStringInfo(str, "%s", quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname))); - val = pstrdup(src_pkattvals[i]); - if (val != NULL) - { - appendStringInfo(str, " = %s", quote_literal_cstr(val)); - pfree(val); - } + if (src_pkattvals[i] != NULL) + appendStringInfo(str, " = %s", + quote_literal_cstr(src_pkattvals[i])); else appendStringInfo(str, " IS NULL"); } diff --git a/contrib/tsearch2/rank.c b/contrib/tsearch2/rank.c index e678b205d6..d5cb777401 100644 --- a/contrib/tsearch2/rank.c +++ b/contrib/tsearch2/rank.c @@ -3,20 +3,20 @@ * Teodor Sigaev */ #include "postgres.h" + #include #include "access/gist.h" #include "access/itup.h" -#include "utils/builtins.h" +#include "catalog/namespace.h" +#include "commands/trigger.h" +#include "executor/spi.h" #include "fmgr.h" #include "funcapi.h" -#include "storage/bufpage.h" -#include "executor/spi.h" -#include "commands/trigger.h" #include "nodes/pg_list.h" -#include "catalog/namespace.h" - +#include "storage/bufpage.h" #include "utils/array.h" +#include "utils/builtins.h" #include "tsvector.h" #include "query.h" @@ -354,6 +354,7 @@ rank(PG_FUNCTION_ARGS) int method = DEF_NORM_METHOD; float res = 0.0; float ws[lengthof(weights)]; + float4 *arrdata; int i; if (ARR_NDIM(win) != 1) @@ -366,9 +367,15 @@ rank(PG_FUNCTION_ARGS) (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array of weight is too short"))); + if (ARR_HASNULL(win)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("array of weight must not contain nulls"))); + + arrdata = (float4 *) ARR_DATA_PTR(win); for (i = 0; i < lengthof(weights); i++) { - ws[i] = (((float4 *) ARR_DATA_PTR(win))[i] >= 0) ? ((float4 *) ARR_DATA_PTR(win))[i] : weights[i]; + ws[i] = (arrdata[i] >= 0) ? arrdata[i] : weights[i]; if (ws[i] > 1.0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), diff --git a/contrib/tsearch2/snowball/header.h b/contrib/tsearch2/snowball/header.h index ad07388154..3889ede53e 100644 --- a/contrib/tsearch2/snowball/header.h +++ b/contrib/tsearch2/snowball/header.h @@ -3,9 +3,6 @@ #include "api.h" -#define MAXINT INT_MAX -#define MININT INT_MIN - #define HEAD 2*sizeof(int) #define SIZE(p) ((int *)(p))[-1] diff --git a/contrib/tsearch2/ts_cfg.c b/contrib/tsearch2/ts_cfg.c index a1fcf0b4f2..50a355e464 100644 --- a/contrib/tsearch2/ts_cfg.c +++ b/contrib/tsearch2/ts_cfg.c @@ -113,6 +113,8 @@ init_cfg(Oid id, TSCfgInfo * cfg) ts_error(ERROR, "Wrong dimension"); if (ARRNELEMS(a) < 1) continue; + if (ARR_HASNULL(a)) + ts_error(ERROR, "Array must not contain nulls"); cfg->map[lexid].len = ARRNELEMS(a); cfg->map[lexid].dict_id = (Datum *) malloc(sizeof(Datum) * cfg->map[lexid].len); diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index a1080b59f6..9282c417d0 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.128 2005/11/17 22:14:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.129 2005/11/18 02:38:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,6 +67,7 @@ static List *cached_membership_roles = NIL; static const char *getid(const char *s, char *n); static void putid(char *p, const char *s); static Acl *allocacl(int n); +static void check_acl(const Acl *acl); static const char *aclparse(const char *s, AclItem *aip); static bool aclitem_match(const AclItem *a1, const AclItem *a2); static void check_circularity(const Acl *old_acl, const AclItem *mod_aip, @@ -359,6 +360,26 @@ allocacl(int n) return new_acl; } +/* + * Verify that an ACL array is acceptable (one-dimensional and has no nulls) + */ +static void +check_acl(const Acl *acl) +{ + if (ARR_ELEMTYPE(acl) != ACLITEMOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("ACL array contains wrong datatype"))); + if (ARR_NDIM(acl) != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("ACL arrays must be one-dimensional"))); + if (ARR_HASNULL(acl)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("ACL arrays must not contain nulls"))); +} + /* * aclitemin * Allocates storage for, and fills in, a new AclItem given a string @@ -612,15 +633,8 @@ aclupdate(const Acl *old_acl, const AclItem *mod_aip, int dst, num; - /* These checks for null input are probably dead code, but... */ - if (!old_acl || ACL_NUM(old_acl) < 0) - old_acl = allocacl(0); - if (!mod_aip) - { - new_acl = allocacl(ACL_NUM(old_acl)); - memcpy(new_acl, old_acl, ACL_SIZE(old_acl)); - return new_acl; - } + /* Caller probably already checked old_acl, but be safe */ + check_acl(old_acl); /* If granting grant options, check for circularity */ if (modechg != ACL_MODECHG_DEL && @@ -740,6 +754,8 @@ aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId) targ, num; + check_acl(old_acl); + /* * Make a copy of the given ACL, substituting new owner ID for old * wherever it appears as either grantor or grantee. Also note if the new @@ -836,6 +852,8 @@ check_circularity(const Acl *old_acl, const AclItem *mod_aip, num; AclMode own_privs; + check_acl(old_acl); + /* * For now, grant options can only be granted to roles, not PUBLIC. * Otherwise we'd have to work a bit harder here. @@ -916,6 +934,8 @@ recursive_revoke(Acl *acl, int i, num; + check_acl(acl); + /* The owner can never truly lose grant options, so short-circuit */ if (grantee == ownerId) return acl; @@ -1005,6 +1025,8 @@ aclmask(const Acl *acl, Oid roleid, Oid ownerId, if (acl == NULL) elog(ERROR, "null ACL"); + check_acl(acl); + /* Quick exit for mask == 0 */ if (mask == 0) return 0; @@ -1091,6 +1113,8 @@ aclmask_direct(const Acl *acl, Oid roleid, Oid ownerId, if (acl == NULL) elog(ERROR, "null ACL"); + check_acl(acl); + /* Quick exit for mask == 0 */ if (mask == 0) return 0; @@ -1151,6 +1175,8 @@ aclmembers(const Acl *acl, Oid **roleids) return 0; } + check_acl(acl); + /* Allocate the worst-case space requirement */ list = palloc(ACL_NUM(acl) * 2 * sizeof(Oid)); acldat = ACL_DAT(acl); @@ -1240,6 +1266,7 @@ aclcontains(PG_FUNCTION_ARGS) int i, num; + check_acl(acl); num = ACL_NUM(acl); aidat = ACL_DAT(acl); for (i = 0; i < num; ++i) diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 1f6c176f64..dd877c7d3f 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.139 2005/10/29 00:31:51 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.140 2005/11/18 02:38:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2491,16 +2491,18 @@ array_to_text(PG_FUNCTION_ARGS) int nitems, *dims, ndims; - char *p; Oid element_type; int typlen; bool typbyval; char typalign; StringInfo result_str = makeStringInfo(); + bool printed = false; + char *p; + bits8 *bitmap; + int bitmask; int i; ArrayMetaState *my_extra; - p = ARR_DATA_PTR(v); ndims = ARR_NDIM(v); dims = ARR_DIMS(v); nitems = ArrayGetNItems(ndims, dims); @@ -2522,7 +2524,7 @@ array_to_text(PG_FUNCTION_ARGS) fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(ArrayMetaState)); my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; - my_extra->element_type = InvalidOid; + my_extra->element_type = ~element_type; } if (my_extra->element_type != element_type) @@ -2542,23 +2544,47 @@ array_to_text(PG_FUNCTION_ARGS) typbyval = my_extra->typbyval; typalign = my_extra->typalign; + p = ARR_DATA_PTR(v); + bitmap = ARR_NULLBITMAP(v); + bitmask = 1; + for (i = 0; i < nitems; i++) { Datum itemvalue; char *value; - itemvalue = fetch_att(p, typbyval, typlen); - - value = DatumGetCString(FunctionCall1(&my_extra->proc, - itemvalue)); - - if (i > 0) - appendStringInfo(result_str, "%s%s", fldsep, value); + /* Get source element, checking for NULL */ + if (bitmap && (*bitmap & bitmask) == 0) + { + /* we ignore nulls */ + } else - appendStringInfoString(result_str, value); + { + itemvalue = fetch_att(p, typbyval, typlen); - p = att_addlength(p, typlen, PointerGetDatum(p)); - p = (char *) att_align(p, typalign); + value = DatumGetCString(FunctionCall1(&my_extra->proc, + itemvalue)); + + if (printed) + appendStringInfo(result_str, "%s%s", fldsep, value); + else + appendStringInfoString(result_str, value); + printed = true; + + p = att_addlength(p, typlen, PointerGetDatum(p)); + p = (char *) att_align(p, typalign); + } + + /* advance bitmap pointer if any */ + if (bitmap) + { + bitmask <<= 1; + if (bitmask == 0x100) + { + bitmap++; + bitmask = 1; + } + } } PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data)); diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index c668382ba3..8699913eb9 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.87 2005/11/17 22:14:55 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.88 2005/11/18 02:38:24 tgl Exp $ * * NOTES * An ACL array is simply an array of AclItems, representing the union @@ -78,9 +78,9 @@ typedef struct AclItem #define ACLITEM_ALL_GOPTION_BITS ((AclMode) 0xFFFF << 16) /* - * Definitions for convenient access to Acl (array of AclItem) and IdList - * (array of Oid). These are standard PostgreSQL arrays, but are restricted - * to have one dimension. We also ignore the lower bound when reading, + * Definitions for convenient access to Acl (array of AclItem). + * These are standard PostgreSQL arrays, but are restricted to have one + * dimension and no nulls. We also ignore the lower bound when reading, * and set it to one when writing. * * CAUTION: as of PostgreSQL 7.1, these arrays are toastable (just like all @@ -100,16 +100,6 @@ typedef ArrayType Acl; #define ACL_N_SIZE(N) (ARR_OVERHEAD_NONULLS(1) + ((N) * sizeof(AclItem))) #define ACL_SIZE(ACL) ARR_SIZE(ACL) -/* - * IdList a one-dimensional array of Oid - */ -typedef ArrayType IdList; - -#define IDLIST_NUM(IDL) (ARR_DIMS(IDL)[0]) -#define IDLIST_DAT(IDL) ((Oid *) ARR_DATA_PTR(IDL)) -#define IDLIST_N_SIZE(N) (ARR_OVERHEAD_NONULLS(1) + ((N) * sizeof(Oid))) -#define IDLIST_SIZE(IDL) ARR_SIZE(IDL) - /* * fmgr macros for these types */ @@ -123,13 +113,6 @@ typedef ArrayType IdList; #define PG_GETARG_ACL_P_COPY(n) DatumGetAclPCopy(PG_GETARG_DATUM(n)) #define PG_RETURN_ACL_P(x) PG_RETURN_POINTER(x) -#define DatumGetIdListP(X) ((IdList *) PG_DETOAST_DATUM(X)) -#define DatumGetIdListPCopy(X) ((IdList *) PG_DETOAST_DATUM_COPY(X)) -#define PG_GETARG_IDLIST_P(n) DatumGetIdListP(PG_GETARG_DATUM(n)) -#define PG_GETARG_IDLIST_P_COPY(n) DatumGetIdListPCopy(PG_GETARG_DATUM(n)) -#define PG_RETURN_IDLIST_P(x) PG_RETURN_POINTER(x) - - /* * ACL modification opcodes for aclupdate */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 608854cbb5..58f6d814b8 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.155 2005/11/17 22:14:55 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.156 2005/11/18 02:38:24 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -3241,8 +3241,7 @@ exec_assign_value(PLpgSQL_execstate * estate, int i; PLpgSQL_expr *subscripts[MAXDIM]; int subscriptvals[MAXDIM]; - bool havenullsubscript, - oldarrayisnull; + bool oldarrayisnull; Oid arraytypeid, arrayelemtypeid; int16 arraytyplen, @@ -3295,9 +3294,9 @@ exec_assign_value(PLpgSQL_execstate * estate, arraytyplen = get_typlen(arraytypeid); /* - * Evaluate the subscripts, switch into left-to-right order + * Evaluate the subscripts, switch into left-to-right order. + * Like ExecEvalArrayRef(), complain if any subscript is null. */ - havenullsubscript = false; for (i = 0; i < nsubscripts; i++) { bool subisnull; @@ -3306,36 +3305,12 @@ exec_assign_value(PLpgSQL_execstate * estate, exec_eval_integer(estate, subscripts[nsubscripts - 1 - i], &subisnull); - havenullsubscript |= subisnull; + if (subisnull) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("array subscript in assignment must not be NULL"))); } - /* - * Skip the assignment if we have any nulls in the subscripts - * or the righthand side. This is pretty bogus but it - * corresponds to the current behavior of ExecEvalArrayRef(). - */ - if (havenullsubscript || *isNull) - return; - - /* - * If the original array is null, cons up an empty array so - * that the assignment can proceed; we'll end with a - * one-element array containing just the assigned-to - * subscript. This only works for varlena arrays, though; for - * fixed-length array types we skip the assignment. Again, - * this corresponds to the current behavior of - * ExecEvalArrayRef(). - */ - if (oldarrayisnull) - { - if (arraytyplen > 0) /* fixed-length array? */ - return; - - oldarrayval = construct_empty_array(arrayelemtypeid); - } - else - oldarrayval = (ArrayType *) DatumGetPointer(oldarraydatum); - /* Coerce source value to match array element type. */ coerced_value = exec_simple_cast_value(value, valtype, @@ -3343,6 +3318,26 @@ exec_assign_value(PLpgSQL_execstate * estate, -1, *isNull); + /* + * If the original array is null, cons up an empty array so + * that the assignment can proceed; we'll end with a + * one-element array containing just the assigned-to + * subscript. This only works for varlena arrays, though; for + * fixed-length array types we skip the assignment. We can't + * support assignment of a null entry into a fixed-length + * array, either, so that's a no-op too. This is all ugly + * but corresponds to the current behavior of + * ExecEvalArrayRef(). + */ + if (arraytyplen > 0 && /* fixed-length array? */ + (oldarrayisnull || *isNull)) + return; + + if (oldarrayisnull) + oldarrayval = construct_empty_array(arrayelemtypeid); + else + oldarrayval = (ArrayType *) DatumGetPointer(oldarraydatum); + /* * Build the modified array value. */