Hello
This patch add iteration over array to plpgsql
now is supported only iteration over scalar array
Regards
Pavel Stehule
*** ./gram.y.orig 2010-09-29 10:53:44.663270537 +0200
--- ./gram.y 2010-09-30 09:04:04.809900052 +0200
***************
*** 239,244 ****
--- 239,245 ----
%token <keyword> K_ABSOLUTE
%token <keyword> K_ALIAS
%token <keyword> K_ALL
+ %token <keyword> K_ARRAY
%token <keyword> K_BACKWARD
%token <keyword> K_BEGIN
%token <keyword> K_BY
***************
*** 1057,1063 ****
Assert($3->cmd_type == PLPGSQL_STMT_FORS ||
$3->cmd_type == PLPGSQL_STMT_FORC ||
! $3->cmd_type == PLPGSQL_STMT_DYNFORS);
/* forq is the common supertype of all three */
new = (PLpgSQL_stmt_forq *) $3;
new->lineno = plpgsql_location_to_lineno(@2);
--- 1058,1065 ----
Assert($3->cmd_type == PLPGSQL_STMT_FORS ||
$3->cmd_type == PLPGSQL_STMT_FORC ||
! $3->cmd_type == PLPGSQL_STMT_DYNFORS ||
! $3->cmd_type == PLPGSQL_STMT_FORA);
/* forq is the common supertype of all three */
new = (PLpgSQL_stmt_forq *) $3;
new->lineno = plpgsql_location_to_lineno(@2);
***************
*** 1077,1083 ****
int tok = yylex();
int tokloc = yylloc;
! if (tok == K_EXECUTE)
{
/* EXECUTE means it's a dynamic FOR loop */
PLpgSQL_stmt_dynfors *new;
--- 1079,1122 ----
int tok = yylex();
int tokloc = yylloc;
! if (tok == K_ARRAY)
! {
! PLpgSQL_stmt_fora *new;
! PLpgSQL_expr *expr;
!
! new = palloc0(sizeof(PLpgSQL_stmt_fora));
! new->cmd_type = PLPGSQL_STMT_FORA;
!
! expr = read_sql_expression(K_LOOP, "LOOP");
!
! if ($1.rec)
! {
! new->rec = $1.rec;
! check_assignable((PLpgSQL_datum *) new->rec, @1);
! }
! else if ($1.row)
! {
! new->row = $1.row;
! check_assignable((PLpgSQL_datum *) new->row, @1);
! }
! else if ($1.scalar)
! {
! /* convert single scalar to list */
! new->var = (PLpgSQL_var *) $1.scalar;
! check_assignable((PLpgSQL_datum *) new->var, @1);
! }
! else
! {
! ereport(ERROR,
! (errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("loop variable of loop over rows must be a record or row variable or list of scalar variables"),
! parser_errposition(@1)));
! }
!
! new->expr = expr;
! $$ = (PLpgSQL_stmt *) new;
! }
! else if (tok == K_EXECUTE)
{
/* EXECUTE means it's a dynamic FOR loop */
PLpgSQL_stmt_dynfors *new;
*** ./pl_exec.c.orig 2010-09-29 11:22:32.435395512 +0200
--- ./pl_exec.c 2010-09-30 09:53:53.310900849 +0200
***************
*** 8,14 ****
*
*
* IDENTIFICATION
! * src/pl/plpgsql/src/pl_exec.c
*
*-------------------------------------------------------------------------
*/
--- 8,14 ----
*
*
* IDENTIFICATION
! * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.266 2010/08/19 18:10:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
***************
*** 107,112 ****
--- 107,114 ----
PLpgSQL_stmt_fors *stmt);
static int exec_stmt_forc(PLpgSQL_execstate *estate,
PLpgSQL_stmt_forc *stmt);
+ static int exec_stmt_fora(PLpgSQL_execstate *estate,
+ PLpgSQL_stmt_fora *stmt);
static int exec_stmt_open(PLpgSQL_execstate *estate,
PLpgSQL_stmt_open *stmt);
static int exec_stmt_fetch(PLpgSQL_execstate *estate,
***************
*** 1309,1314 ****
--- 1311,1320 ----
rc = exec_stmt_forc(estate, (PLpgSQL_stmt_forc *) stmt);
break;
+ case PLPGSQL_STMT_FORA:
+ rc = exec_stmt_fora(estate, (PLpgSQL_stmt_fora *) stmt);
+ break;
+
case PLPGSQL_STMT_EXIT:
rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt);
break;
***************
*** 1860,1893 ****
return rc;
}
/* ----------
! * exec_stmt_fors Execute a query, assign each
! * tuple to a record or row and
! * execute a group of statements
! * for it.
* ----------
*/
static int
! exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt)
{
! Portal portal;
! int rc;
! /*
! * Open the implicit cursor for the statement using exec_run_select
! */
! exec_run_select(estate, stmt->query, 0, &portal);
/*
! * Execute the loop
*/
! rc = exec_for_query(estate, (PLpgSQL_stmt_forq *) stmt, portal, true);
/*
! * Close the implicit cursor
*/
! SPI_cursor_close(portal);
return rc;
}
--- 1866,2072 ----
return rc;
}
+ /*
+ * Check whether a specific array element is NULL
+ *
+ * nullbitmap: pointer to array's null bitmap (NULL if none)
+ * offset: 0-based linear element number of array element
+ */
+ static bool
+ array_get_isnull(const bits8 *nullbitmap, int offset)
+ {
+ if (nullbitmap == NULL)
+ return false; /* assume not null */
+ if (nullbitmap[offset / 8] & (1 << (offset % 8)))
+ return false; /* not null */
+ return true;
+ }
/* ----------
! * exec_stmt_fora Execute a loop for each field of array
* ----------
*/
static int
! exec_stmt_fora(PLpgSQL_execstate *estate, PLpgSQL_stmt_fora *stmt)
{
! Datum value;
! bool isnull;
! Oid valtype;
! bool found = false;
! int rc = PLPGSQL_RC_OK;
! int numelems = 0;
! Oid array_typelem;
! int idx;
! ArrayType *arr;
! char *ptr;
! bits8 *arraynullsptr;
! int16 elmlen;
! bool elmbyval;
! char elmalign;
!
! PLpgSQL_rec *rec = NULL;
! PLpgSQL_row *row = NULL;
! PLpgSQL_var *var = NULL;
!
! HeapTuple tuple;
! Datum dvalues[1];
! bool nulls[1];
! TupleDesc tupdesc = NULL;
!
! /* 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 FOR-IN-ARRAY")));
!
! /* 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))));
!
! /* get a control variables */
! if (stmt->rec != NULL)
! var = (PLpgSQL_var *) (estate->datums[stmt->var->dno]);
! else if (stmt->rec != NULL)
! rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->dno]);
! else if (stmt->row != NULL)
! row = (PLpgSQL_row *) (estate->datums[stmt->row->dno]);
! else
! elog(ERROR, "unsupported targer");
!
! /* 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),
! &elmlen,
! &elmbyval,
! &elmalign);
!
! /* clean a stack */
! exec_eval_cleanup(estate);
!
! /* Prepare tupledesc when we need it */
! if (rec != NULL || row != NULL)
! {
! /* have to create a TupleDesc and HeapTuple */
! tupdesc = CreateTemplateTupleDesc(1, false);
! TupleDescInitEntry(tupdesc,1, "fieldvalue", array_typelem, -1, 0);
! BlessTupleDesc(tupdesc);
! }
/*
! * Now do the loop
*/
! for (idx = 0; idx < numelems; idx++)
! {
! found = true; /* looped at least once */
!
! if (array_get_isnull(arraynullsptr, idx))
! {
! dvalues[0] = (Datum) 0;
! nulls[0] = true;
! }
! else
! {
! value = fetch_att(ptr, elmbyval, elmlen);
!
! nulls[0] = false;
! dvalues[0] = value;
!
! ptr = att_addlength_pointer(ptr, elmlen, ptr);
! ptr = (char *) att_align_nominal(ptr, elmalign);
! }
!
! /*
! * 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 (var != NULL)
! {
! exec_assign_value(estate, (PLpgSQL_datum *) var,
! dvalues[0], array_typelem, &nulls[0]);
! }
! else
! {
! Assert(row != NULL || rec != NULL);
! tuple = heap_form_tuple(tupdesc, dvalues, nulls);
! exec_move_row(estate, rec, row, tuple, tupdesc);
! heap_freetuple(tuple);
! }
!
! /*
! * Execute the statements
! */
! rc = exec_stmts(estate, stmt->body);
!
! if (rc == PLPGSQL_RC_RETURN)
! break; /* break out of the loop */
! else if (rc == PLPGSQL_RC_EXIT)
! {
! if (estate->exitlabel == NULL)
! /* unlabelled exit, finish the current loop */
! rc = PLPGSQL_RC_OK;
! else if (stmt->label != NULL &&
! strcmp(stmt->label, estate->exitlabel) == 0)
! {
! /* labelled exit, matches the current stmt's label */
! estate->exitlabel = NULL;
! rc = PLPGSQL_RC_OK;
! }
!
! /*
! * otherwise, this is a labelled exit that does not match the
! * current statement's label, if any: return RC_EXIT so that the
! * EXIT continues to propagate up the stack.
! */
! break;
! }
! else if (rc == PLPGSQL_RC_CONTINUE)
! {
! if (estate->exitlabel == NULL)
! /* unlabelled continue, so re-run the current loop */
! rc = PLPGSQL_RC_OK;
! else if (stmt->label != NULL &&
! strcmp(stmt->label, estate->exitlabel) == 0)
! {
! /* label matches named continue, so re-run loop */
! estate->exitlabel = NULL;
! rc = PLPGSQL_RC_OK;
! }
! else
! {
! /*
! * otherwise, this is a named continue that does not match the
! * current statement's label, if any: return RC_CONTINUE so
! * that the CONTINUE will propagate up the stack.
! */
! break;
! }
! }
!
! }
!
! pfree(arr);
! ReleaseTupleDesc(tupdesc);
/*
! * Set the FOUND variable to indicate the result of executing the loop
! * (namely, whether we looped one or more times). This must be set here so
! * that it does not interfere with the value of the FOUND variable inside
! * the loop processing itself.
*/
! exec_set_found(estate, found);
return rc;
}
***************
*** 2023,2028 ****
--- 2202,2239 ----
return rc;
}
+ /* ----------
+ * exec_stmt_fors Execute a query, assign each
+ * tuple to a record or row and
+ * execute a group of statements
+ * for it.
+ * ----------
+ */
+ static int
+ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt)
+ {
+ Portal portal;
+ int rc;
+
+ /*
+ * Open the implicit cursor for the statement using exec_run_select
+ */
+ exec_run_select(estate, stmt->query, 0, &portal);
+
+ /*
+ * Execute the loop
+ */
+ rc = exec_for_query(estate, (PLpgSQL_stmt_forq *) stmt, portal, true);
+
+ /*
+ * Close the implicit cursor
+ */
+ SPI_cursor_close(portal);
+
+ return rc;
+ }
+
+
/* ----------
* exec_stmt_exit Implements EXIT and CONTINUE
***************
*** 2064,2070 ****
exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
{
/*
! * If processing a set-returning PL/pgSQL function, the final RETURN
* indicates that the function is finished producing tuples. The rest of
* the work will be done at the top level.
*/
--- 2275,2281 ----
exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
{
/*
! * If processing a set-returning PL/PgSQL function, the final RETURN
* indicates that the function is finished producing tuples. The rest of
* the work will be done at the top level.
*/
*** ./pl_funcs.c.orig 2010-09-29 11:29:27.956270569 +0200
--- ./pl_funcs.c 2010-09-29 11:36:49.521272006 +0200
***************
*** 230,235 ****
--- 230,237 ----
return _("FOR over SELECT rows");
case PLPGSQL_STMT_FORC:
return _("FOR over cursor");
+ case PLPGSQL_STMT_FORA:
+ return _("FOR over array");
case PLPGSQL_STMT_EXIT:
return "EXIT";
case PLPGSQL_STMT_RETURN:
***************
*** 277,282 ****
--- 279,285 ----
static void dump_while(PLpgSQL_stmt_while *stmt);
static void dump_fori(PLpgSQL_stmt_fori *stmt);
static void dump_fors(PLpgSQL_stmt_fors *stmt);
+ static void dump_fora(PLpgSQL_stmt_fora *stmt);
static void dump_forc(PLpgSQL_stmt_forc *stmt);
static void dump_exit(PLpgSQL_stmt_exit *stmt);
static void dump_return(PLpgSQL_stmt_return *stmt);
***************
*** 337,342 ****
--- 340,348 ----
case PLPGSQL_STMT_FORC:
dump_forc((PLpgSQL_stmt_forc *) stmt);
break;
+ case PLPGSQL_STMT_FORA:
+ dump_fora((PLpgSQL_stmt_fora *) stmt);
+ break;
case PLPGSQL_STMT_EXIT:
dump_exit((PLpgSQL_stmt_exit *) stmt);
break;
***************
*** 596,601 ****
--- 602,619 ----
}
static void
+ dump_fora(PLpgSQL_stmt_fora *stmt)
+ {
+ dump_ind();
+ printf("FORA %s ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
+ dump_expr(stmt->expr);
+ printf("\n");
+ dump_stmts(stmt->body);
+ dump_ind();
+ printf(" ENDFORA\n");
+ }
+
+ static void
dump_open(PLpgSQL_stmt_open *stmt)
{
dump_ind();
*** ./plpgsql.h.orig 2010-09-29 11:10:35.745270541 +0200
--- ./plpgsql.h 2010-09-30 09:00:27.512903020 +0200
***************
*** 87,92 ****
--- 87,93 ----
PLPGSQL_STMT_CASE,
PLPGSQL_STMT_LOOP,
PLPGSQL_STMT_WHILE,
+ PLPGSQL_STMT_FORA,
PLPGSQL_STMT_FORI,
PLPGSQL_STMT_FORS,
PLPGSQL_STMT_FORC,
***************
*** 440,447 ****
/*
* PLpgSQL_stmt_forq represents a FOR statement running over a SQL query.
! * It is the common supertype of PLpgSQL_stmt_fors, PLpgSQL_stmt_forc
! * and PLpgSQL_dynfors.
*/
typedef struct
{
--- 441,448 ----
/*
* PLpgSQL_stmt_forq represents a FOR statement running over a SQL query.
! * It is the common supertype of PLpgSQL_stmt_fors, PLpgSQL_stmt_forc,
! * PLpgSQL_stmt_fora and PLpgSQL_dynfors.
*/
typedef struct
{
***************
*** 454,459 ****
--- 455,472 ----
} PLpgSQL_stmt_forq;
typedef struct
+ {
+ int cmd_type;
+ int lineno;
+ char *label;
+ PLpgSQL_var *var;
+ PLpgSQL_rec *rec;
+ PLpgSQL_row *row;
+ List *body; /* List of statements */
+ PLpgSQL_expr *expr;
+ } PLpgSQL_stmt_fora;
+
+ typedef struct
{ /* FOR statement running over SELECT */
int cmd_type;
int lineno;
*** ./pl_scanner.c.orig 2010-09-30 08:47:40.631901895 +0200
--- ./pl_scanner.c 2010-09-29 11:02:01.005270250 +0200
***************
*** 60,65 ****
--- 60,66 ----
static const ScanKeyword reserved_keywords[] = {
PG_KEYWORD("all", K_ALL, RESERVED_KEYWORD)
+ PG_KEYWORD("array", K_ARRAY, RESERVED_KEYWORD)
PG_KEYWORD("begin", K_BEGIN, RESERVED_KEYWORD)
PG_KEYWORD("by", K_BY, RESERVED_KEYWORD)
PG_KEYWORD("case", K_CASE, RESERVED_KEYWORD)
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers