test_plpgsql_array_cleanup.patch
text/x-diff
Filename: test_plpgsql_array_cleanup.patch
Type: text/x-diff
Part: 1
Message:
REVIEW: WIP: plpgsql - foreach in
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/pl/plpgsql/src/gram.y | 3 | 0 |
| src/pl/plpgsql/src/pl_exec.c | 60 | 0 |
| src/pl/plpgsql/src/pl_funcs.c | 1 | 0 |
| src/pl/plpgsql/src/plpgsql.h | 1 | 0 |
| src/test/regress/expected/plpgsql.out | 2 | 0 |
*** a/src/pl/plpgsql/src/gram.y
--- b/src/pl/plpgsql/src/gram.y
***************
*** 192,198 **** static List *read_raise_options(void);
%type <stmt> stmt_return stmt_raise stmt_execsql
%type <stmt> stmt_dynexecute stmt_for stmt_perform stmt_getdiag
%type <stmt> stmt_open stmt_fetch stmt_move stmt_close stmt_null
! %type <stmt> stmt_case stmt_foreach_a
%type <list> proc_exceptions
%type <exception_block> exception_sect
--- 192,198 ----
%type <stmt> stmt_return stmt_raise stmt_execsql
%type <stmt> stmt_dynexecute stmt_for stmt_perform stmt_getdiag
%type <stmt> stmt_open stmt_fetch stmt_move stmt_close stmt_null
! %type <stmt> stmt_case stmt_foreach_a
%type <list> proc_exceptions
%type <exception_block> exception_sect
***************
*** 1392,1398 **** for_variable : T_DATUM
}
;
! stmt_foreach_a : opt_block_label K_FOREACH foreach_control K_IN expr_until_loop loop_body
{
PLpgSQL_stmt_foreach_a *new = palloc0(sizeof(PLpgSQL_stmt_foreach_a));
new->cmd_type = PLPGSQL_STMT_FOREACH_A;
--- 1392,1398 ----
}
;
! stmt_foreach_a : opt_block_label K_FOREACH foreach_control K_IN expr_until_loop loop_body
{
PLpgSQL_stmt_foreach_a *new = palloc0(sizeof(PLpgSQL_stmt_foreach_a));
new->cmd_type = PLPGSQL_STMT_FOREACH_A;
***************
*** 1423,1429 **** stmt_foreach_a : opt_block_label K_FOREACH foreach_control K_IN expr_until_loo
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("loop variable of loop over arrat must be a record, row variable, scalar variable or list of scalar variables"),
parser_errposition(@3)));
}
--- 1423,1429 ----
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("loop variable of loop over array must be a record, row variable, scalar variable or list of scalar variables"),
parser_errposition(@3)));
}
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 2035,2083 **** exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
/* ----------
* exec_stmt_foreach_a Implements loop over array
- *
* ----------
*/
! static int
exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
{
Datum value;
bool isnull;
Oid valtype;
! int numelems = 0;
! Oid array_typelem;
! int idx;
! ArrayType *arr;
! char *ptr;
! bits8 *arraynullsptr;
! int16 elmlen;
! bool elmbyval;
! char elmalign;
PLpgSQL_datum *ctrl_var;
bool found = false;
int rc = PLPGSQL_RC_OK;
! int nitems = 1;
! /* get a result of array_expr */
value = exec_eval_expr(estate, stmt->expr, &isnull, &valtype);
if (isnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("NULL value isn't allowed as parameter of FOREACH-IN")));
! /* check a result of expression - must be a array */
array_typelem = get_element_type(valtype);
if (!OidIsValid(array_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("result of expression isn't array"),
! errdetail("result of expression is %s",
format_type_be(valtype))));
!
! /* copy a result and takes some infos */
arr = DatumGetArrayTypePCopy(value);
numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
ptr = ARR_DATA_PTR(arr);
arraynullsptr = ARR_NULLBITMAP(arr);
get_typlenbyvalalign(ARR_ELEMTYPE(arr),
--- 2035,2086 ----
/* ----------
* exec_stmt_foreach_a Implements loop over array
* ----------
*/
! static int
exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
{
Datum value;
bool isnull;
Oid valtype;
! int numelems = 0;
! Oid array_typelem;
! int idx;
! ArrayType *arr;
! char *ptr;
! bits8 *arraynullsptr;
! int16 elmlen;
! bool elmbyval;
! char elmalign;
PLpgSQL_datum *ctrl_var;
bool found = false;
int rc = PLPGSQL_RC_OK;
! int nitems = 1;
! /* get the value of the array expression using array_expr */
value = exec_eval_expr(estate, stmt->expr, &isnull, &valtype);
if (isnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("NULL value isn't allowed as parameter of FOREACH-IN")));
! /* check the type of the expression - must be an array */
array_typelem = get_element_type(valtype);
if (!OidIsValid(array_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("result of expression isn't an array"),
! errdetail("result of expression is %s",
format_type_be(valtype))));
!
! /* make a copy of the result */
arr = DatumGetArrayTypePCopy(value);
+
+ /* Calculate the number of elements we're going to loop through */
numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+
+ /* Gather information about the array */
ptr = ARR_DATA_PTR(arr);
arraynullsptr = ARR_NULLBITMAP(arr);
get_typlenbyvalalign(ARR_ELEMTYPE(arr),
***************
*** 2085,2101 **** exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
&elmbyval,
&elmalign);
! /* clean a stack */
exec_eval_cleanup(estate);
if (stmt->slice > 0)
{
if (stmt->rec != NULL || stmt->row != NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("target variable \"%s\" isn't a of array type",
stmt->row ? stmt->row->refname : stmt->rec->refname),
! errhint("Assigned value will be a value of array type.")));
if (stmt->slice > ARR_NDIM(arr))
ereport(ERROR,
--- 2088,2112 ----
&elmbyval,
&elmalign);
! /* Clean up any leftover temporary memory */
exec_eval_cleanup(estate);
+ /*
+ * If the target needs to be an array, check that it actually is,
+ * and that it has a valid dimension for the array we're looping
+ * through.
+ *
+ * XXX: Is this redundant? Could it be merged with the below
+ * set of conditionals?
+ */
if (stmt->slice > 0)
{
if (stmt->rec != NULL || stmt->row != NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("target variable \"%s\" isn't an array type",
stmt->row ? stmt->row->refname : stmt->rec->refname),
! errhint("Value assigned will be of an array type.")));
if (stmt->slice > ARR_NDIM(arr))
ereport(ERROR,
***************
*** 2104,2110 **** exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
stmt->slice)));
}
! /* get a target variable */
if (stmt->var != NULL)
{
int typoid = stmt->var->datatype->typoid;
--- 2115,2121 ----
stmt->slice)));
}
! /* Set up the target variable */
if (stmt->var != NULL)
{
int typoid = stmt->var->datatype->typoid;
***************
*** 2112,2123 **** exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
if (stmt->slice > 0)
{
! /* target variable have to be a array type */
if (elmoid == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("target variable \"%s\" for sliced array should be a array",
stmt->var->refname)));
nitems = ArrayGetNItems(stmt->slice, ARR_DIMS(arr) + ARR_NDIM(arr) - stmt->slice);
}
else
--- 2123,2136 ----
if (stmt->slice > 0)
{
! /* target variable has to be an array */
if (elmoid == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("target variable \"%s\" for sliced array should be an array type",
stmt->var->refname)));
+
+ /* Determine the number of items in the slice */
nitems = ArrayGetNItems(stmt->slice, ARR_DIMS(arr) + ARR_NDIM(arr) - stmt->slice);
}
else
***************
*** 2126,2133 **** exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
if (elmoid != InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("target variable \"%s\" is a array",
! stmt->var->refname)));
}
ctrl_var = estate->datums[stmt->var->dno];
}
--- 2139,2147 ----
if (elmoid != InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("target variable \"%s\" is an array",
! stmt->var->refname),
! errhint("Value assigned will be of a scalar type")));
}
ctrl_var = estate->datums[stmt->var->dno];
}
***************
*** 2135,2147 **** exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
{
ctrl_var = estate->datums[stmt->row->dno];
}
! else
{
ctrl_var = estate->datums[stmt->rec->dno];
}
/*
* Now do the loop
*/
idx = 0;
while (idx < numelems)
--- 2149,2164 ----
{
ctrl_var = estate->datums[stmt->row->dno];
}
! else
{
ctrl_var = estate->datums[stmt->rec->dno];
}
/*
* Now do the loop
+ *
+ * XXX: These variable names are.. unfortunate. Could we come up
+ * with soe which are more descriptive?
*/
idx = 0;
while (idx < numelems)
***************
*** 2151,2158 **** exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
--- 2168,2180 ----
found = true; /* looped at least once */
+ /* Loop through the items in this slice to build up the
+ * array slice to be used through the exec statements ... ?
+ * XXX: This really needs some better commenting.. */
for (j = 0; j < nitems; j++)
{
+ /* XXX: Do we have a better way to check arraynullsptr?
+ * Maybe a macro of some kind ..? */
if (arraynullsptr != NULL && !(arraynullsptr[idx / 8] & (1 << (idx % 8))))
{
isnull = true;
***************
*** 2168,2193 **** exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
}
if (stmt->slice > 0)
! astate = accumArrayResult(astate, value, isnull,
! array_typelem,
CurrentMemoryContext);
idx++;
}
! /*
! * store a item to variable - we expecting so almost all
! * iteration will be over scalar values, so it is reason
! * why we don't create a fake tuple over scalar and we
! * don't use a exec_move_row for scalars. This is about
! * four times faster.
*/
if (astate != NULL)
{
Datum sliced_arr;
bool isnull = false;
! sliced_arr = makeMdArrayResult(astate, stmt->slice,
! ARR_DIMS(arr) + ARR_NDIM(arr) - stmt->slice,
ARR_LBOUND(arr) ? ARR_LBOUND(arr) + ARR_NDIM(arr) - stmt->slice : NULL,
CurrentMemoryContext, true);
exec_assign_value(estate, ctrl_var, sliced_arr, valtype, &isnull);
--- 2190,2216 ----
}
if (stmt->slice > 0)
! astate = accumArrayResult(astate, value, isnull,
! array_typelem,
CurrentMemoryContext);
+
+ /* XXX: Why are we updating this here..? */
idx++;
}
! /*
! * store an item in to the variable - we are expecting almost all
! * iterations will be over scalar values, so we don't create
! * a fake tuple over scalar and we don't use exec_move_row for
! * scalars. This is about four times faster.
*/
if (astate != NULL)
{
Datum sliced_arr;
bool isnull = false;
! sliced_arr = makeMdArrayResult(astate, stmt->slice,
! ARR_DIMS(arr) + ARR_NDIM(arr) - stmt->slice,
ARR_LBOUND(arr) ? ARR_LBOUND(arr) + ARR_NDIM(arr) - stmt->slice : NULL,
CurrentMemoryContext, true);
exec_assign_value(estate, ctrl_var, sliced_arr, valtype, &isnull);
*** a/src/pl/plpgsql/src/pl_funcs.c
--- b/src/pl/plpgsql/src/pl_funcs.c
***************
*** 605,611 **** static void
dump_foreach_a(PLpgSQL_stmt_foreach_a *stmt)
{
dump_ind();
! printf("FOREACHA %s", (stmt->rec != NULL) ? stmt->rec->refname :
(stmt->row != NULL) ? stmt->row->refname : stmt->var->refname);
if (stmt->slice != 0)
printf("SLICE %d ", stmt->slice);
--- 605,611 ----
dump_foreach_a(PLpgSQL_stmt_foreach_a *stmt)
{
dump_ind();
! printf("FOREACHA %s", (stmt->rec != NULL) ? stmt->rec->refname :
(stmt->row != NULL) ? stmt->row->refname : stmt->var->refname);
if (stmt->slice != 0)
printf("SLICE %d ", stmt->slice);
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
***************
*** 436,442 **** typedef struct
PLpgSQL_rec *rec;
PLpgSQL_row *row;
PLpgSQL_expr *expr;
! int slice;
List *body; /* List of statements */
} PLpgSQL_stmt_foreach_a;
--- 436,442 ----
PLpgSQL_rec *rec;
PLpgSQL_row *row;
PLpgSQL_expr *expr;
! int slice;
List *body; /* List of statements */
} PLpgSQL_stmt_foreach_a;
*** a/src/test/regress/expected/plpgsql.out
--- b/src/test/regress/expected/plpgsql.out
***************
*** 4283,4292 **** begin
$$ language plpgsql;
-- should fail
select foreach_test(ARRAY[1,2,3,4]);
! ERROR: target variable "x" for sliced array should be a array
CONTEXT: PL/pgSQL function "foreach_test" line 4 at FOREACH over array
select foreach_test(ARRAY[[1,2],[3,4]]);
! ERROR: target variable "x" for sliced array should be a array
CONTEXT: PL/pgSQL function "foreach_test" line 4 at FOREACH over array
create or replace function foreach_test(anyarray)
returns void as $$
--- 4283,4292 ----
$$ language plpgsql;
-- should fail
select foreach_test(ARRAY[1,2,3,4]);
! ERROR: target variable "x" for sliced array should be an array type
CONTEXT: PL/pgSQL function "foreach_test" line 4 at FOREACH over array
select foreach_test(ARRAY[[1,2],[3,4]]);
! ERROR: target variable "x" for sliced array should be an array type
CONTEXT: PL/pgSQL function "foreach_test" line 4 at FOREACH over array
create or replace function foreach_test(anyarray)
returns void as $$