aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pl/plpgsql/src/gram.y35
-rw-r--r--src/pl/plpgsql/src/pl_exec.c240
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c8
-rw-r--r--src/pl/plpgsql/src/plpgsql.h17
4 files changed, 253 insertions, 47 deletions
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index f8adc55dcee..23dc8940290 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.40 2002/11/10 00:35:58 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.41 2003/03/25 03:16:40 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -105,7 +105,8 @@ static void check_assignable(PLpgSQL_datum *datum);
%type <nsitem> decl_aliasitem
%type <str> decl_stmts decl_stmt
-%type <expr> expr_until_semi expr_until_then expr_until_loop
+%type <expr> expr_until_semi expr_until_rightbracket
+%type <expr> expr_until_then expr_until_loop
%type <expr> opt_exitcond
%type <ival> assign_var cursor_variable
@@ -822,6 +823,21 @@ assign_var : T_VARIABLE
check_assignable(yylval.variable);
$$ = yylval.variable->dno;
}
+ | assign_var '[' expr_until_rightbracket
+ {
+ PLpgSQL_arrayelem *new;
+
+ new = malloc(sizeof(PLpgSQL_arrayelem));
+ memset(new, 0, sizeof(PLpgSQL_arrayelem));
+
+ new->dtype = PLPGSQL_DTYPE_ARRAYELEM;
+ new->subscript = $3;
+ new->arrayparentno = $1;
+
+ plpgsql_adddatum((PLpgSQL_datum *)new);
+
+ $$ = new->dno;
+ }
;
stmt_if : K_IF lno expr_until_then proc_sect stmt_else K_END K_IF ';'
@@ -1491,6 +1507,10 @@ expr_until_semi :
{ $$ = plpgsql_read_expression(';', ";"); }
;
+expr_until_rightbracket :
+ { $$ = plpgsql_read_expression(']', "]"); }
+ ;
+
expr_until_then :
{ $$ = plpgsql_read_expression(K_THEN, "THEN"); }
;
@@ -1577,16 +1597,16 @@ read_sql_construct(int until,
for (;;)
{
tok = yylex();
- if (tok == '(')
+ if (tok == until && parenlevel == 0)
+ break;
+ if (tok == '(' || tok == '[')
parenlevel++;
- else if (tok == ')')
+ else if (tok == ')' || tok == ']')
{
parenlevel--;
if (parenlevel < 0)
elog(ERROR, "mismatched parentheses");
}
- else if (parenlevel == 0 && tok == until)
- break;
/*
* End of function definition is an error, and we don't expect to
* hit a semicolon either (unless it's the until symbol, in which
@@ -1988,6 +2008,9 @@ check_assignable(PLpgSQL_datum *datum)
case PLPGSQL_DTYPE_RECFIELD:
/* always assignable? */
break;
+ case PLPGSQL_DTYPE_ARRAYELEM:
+ /* always assignable? */
+ break;
case PLPGSQL_DTYPE_TRIGARG:
yyerror("cannot assign to tg_argv");
break;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 984be78eafb..d105c0e8dcc 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.82 2003/03/25 00:34:23 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.83 2003/03/25 03:16:40 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -49,6 +49,7 @@
#include "optimizer/clauses.h"
#include "parser/parse_expr.h"
#include "tcop/tcopprot.h"
+#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -136,6 +137,9 @@ static void exec_eval_datum(PLpgSQL_execstate *estate,
Oid *typeid,
Datum *value,
bool *isnull);
+static int exec_eval_subscript(PLpgSQL_execstate * estate,
+ PLpgSQL_expr * expr,
+ bool *isNull);
static Datum exec_eval_expr(PLpgSQL_execstate * estate,
PLpgSQL_expr * expr,
bool *isNull,
@@ -152,6 +156,9 @@ static Datum exec_cast_value(Datum value, Oid valtype,
Oid reqtypelem,
int32 reqtypmod,
bool *isnull);
+static Datum exec_simple_cast_value(Datum value, Oid valtype,
+ Oid reqtype, int32 reqtypmod,
+ bool *isnull);
static void exec_init_tuple_store(PLpgSQL_execstate * estate);
static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);
static void exec_set_found(PLpgSQL_execstate * estate, bool state);
@@ -237,6 +244,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_RECFIELD:
+ case PLPGSQL_DTYPE_ARRAYELEM:
estate.datums[i] = func->datums[i];
break;
@@ -308,6 +316,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_REC:
case PLPGSQL_DTYPE_RECFIELD:
+ case PLPGSQL_DTYPE_ARRAYELEM:
break;
default:
@@ -507,6 +516,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_RECFIELD:
+ case PLPGSQL_DTYPE_ARRAYELEM:
case PLPGSQL_DTYPE_TRIGARG:
estate.datums[i] = func->datums[i];
break;
@@ -658,6 +668,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_REC:
case PLPGSQL_DTYPE_RECFIELD:
+ case PLPGSQL_DTYPE_ARRAYELEM:
case PLPGSQL_DTYPE_TRIGARG:
break;
@@ -822,6 +833,7 @@ exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block)
break;
case PLPGSQL_DTYPE_RECFIELD:
+ case PLPGSQL_DTYPE_ARRAYELEM:
break;
default:
@@ -1693,24 +1705,11 @@ exec_stmt_return_next(PLpgSQL_execstate * estate,
&rettype);
/* coerce type if needed */
- if (!isNull && rettype != tupdesc->attrs[0]->atttypid)
- {
- Oid targType = tupdesc->attrs[0]->atttypid;
- Oid typInput;
- Oid typElem;
- FmgrInfo finfo_input;
-
- getTypeInputInfo(targType, &typInput, &typElem);
- fmgr_info(typInput, &finfo_input);
-
- retval = exec_cast_value(retval,
- rettype,
- targType,
- &finfo_input,
- typElem,
- tupdesc->attrs[0]->atttypmod,
- &isNull);
- }
+ retval = exec_simple_cast_value(retval,
+ rettype,
+ tupdesc->attrs[0]->atttypid,
+ tupdesc->attrs[0]->atttypmod,
+ &isNull);
nullflag = isNull ? 'n' : ' ';
@@ -2709,10 +2708,21 @@ exec_assign_value(PLpgSQL_execstate * estate,
bool attisnull;
Oid atttype;
int32 atttypmod;
+ int nsubscripts;
+ PLpgSQL_expr *subscripts[MAXDIM];
+ int subscriptvals[MAXDIM];
+ bool havenullsubscript,
+ oldarrayisnull;
+ Oid arraytypeid,
+ arrayelemtypeid,
+ arrayInputFn;
+ int16 elemtyplen;
+ bool elemtypbyval;
+ char elemtypalign;
+ Datum oldarrayval,
+ coerced_value;
+ ArrayType *newarrayval;
HeapTuple newtup;
- Oid typInput;
- Oid typElem;
- FmgrInfo finfo_input;
switch (target->dtype)
{
@@ -2812,24 +2822,19 @@ exec_assign_value(PLpgSQL_execstate * estate,
*/
atttype = SPI_gettypeid(rec->tupdesc, fno + 1);
atttypmod = rec->tupdesc->attrs[fno]->atttypmod;
- getTypeInputInfo(atttype, &typInput, &typElem);
- fmgr_info(typInput, &finfo_input);
-
attisnull = *isNull;
- values[fno] = exec_cast_value(value,
- valtype,
- atttype,
- &finfo_input,
- typElem,
- atttypmod,
- &attisnull);
+ values[fno] = exec_simple_cast_value(value,
+ valtype,
+ atttype,
+ atttypmod,
+ &attisnull);
if (attisnull)
nulls[fno] = 'n';
else
nulls[fno] = ' ';
/*
- * Avoid leaking the result of exec_cast_value, if it
+ * Avoid leaking the result of exec_simple_cast_value, if it
* performed a conversion to a pass-by-ref type.
*/
if (!attisnull && values[fno] != value && !get_typbyval(atttype))
@@ -2856,6 +2861,103 @@ exec_assign_value(PLpgSQL_execstate * estate,
break;
+ case PLPGSQL_DTYPE_ARRAYELEM:
+
+ /*
+ * Target is an element of an array
+ *
+ * To handle constructs like x[1][2] := something, we have to
+ * be prepared to deal with a chain of arrayelem datums.
+ * Chase back to find the base array datum, and save the
+ * subscript expressions as we go. (We are scanning right to
+ * left here, but want to evaluate the subscripts left-to-right
+ * to minimize surprises.)
+ */
+ nsubscripts = 0;
+ do {
+ PLpgSQL_arrayelem *arrayelem = (PLpgSQL_arrayelem *) target;
+
+ if (nsubscripts >= MAXDIM)
+ elog(ERROR, "Too many subscripts");
+ subscripts[nsubscripts++] = arrayelem->subscript;
+ target = estate->datums[arrayelem->arrayparentno];
+ } while (target->dtype == PLPGSQL_DTYPE_ARRAYELEM);
+
+ /* Fetch current value of array datum */
+ exec_eval_datum(estate, target, InvalidOid,
+ &arraytypeid, &oldarrayval, &oldarrayisnull);
+
+ getTypeInputInfo(arraytypeid, &arrayInputFn, &arrayelemtypeid);
+ if (!OidIsValid(arrayelemtypeid))
+ elog(ERROR, "Subscripted item is not an array");
+
+ /* Evaluate the subscripts, switch into left-to-right order */
+ havenullsubscript = false;
+ for (i = 0; i < nsubscripts; i++)
+ {
+ bool subisnull;
+
+ subscriptvals[i] =
+ exec_eval_subscript(estate,
+ subscripts[nsubscripts-1-i],
+ &subisnull);
+ havenullsubscript |= subisnull;
+ }
+
+ /*
+ * Skip the assignment if we have any nulls, either in the
+ * original array value, the subscripts, or the righthand side.
+ * This is pretty bogus but it corresponds to the current
+ * behavior of ExecEvalArrayRef().
+ */
+ if (oldarrayisnull || havenullsubscript || *isNull)
+ return;
+
+ /* Coerce source value to match array element type. */
+ coerced_value = exec_simple_cast_value(value,
+ valtype,
+ arrayelemtypeid,
+ -1,
+ isNull);
+
+ /*
+ * Build the modified array value.
+ */
+ get_typlenbyvalalign(arrayelemtypeid,
+ &elemtyplen,
+ &elemtypbyval,
+ &elemtypalign);
+
+ newarrayval = array_set((ArrayType *) DatumGetPointer(oldarrayval),
+ nsubscripts,
+ subscriptvals,
+ coerced_value,
+ get_typlen(arraytypeid),
+ elemtyplen,
+ elemtypbyval,
+ elemtypalign,
+ isNull);
+
+ /*
+ * Assign it to the base variable.
+ */
+ exec_assign_value(estate, target,
+ PointerGetDatum(newarrayval),
+ arraytypeid, isNull);
+
+ /*
+ * Avoid leaking the result of exec_simple_cast_value, if it
+ * performed a conversion to a pass-by-ref type.
+ */
+ if (!*isNull && coerced_value != value && !elemtypbyval)
+ pfree(DatumGetPointer(coerced_value));
+
+ /*
+ * Avoid leaking the modified array value, too.
+ */
+ pfree(newarrayval);
+ break;
+
default:
elog(ERROR, "unknown dtype %d in exec_assign_value()",
target->dtype);
@@ -2888,7 +2990,6 @@ exec_eval_datum(PLpgSQL_execstate *estate,
PLpgSQL_recfield *recfield;
PLpgSQL_trigarg *trigarg;
int tgargno;
- Oid tgargoid;
int fno;
switch (datum->dtype)
@@ -2922,9 +3023,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
case PLPGSQL_DTYPE_TRIGARG:
trigarg = (PLpgSQL_trigarg *) datum;
*typeid = TEXTOID;
- tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
- isnull, &tgargoid);
- exec_eval_cleanup(estate);
+ tgargno = exec_eval_subscript(estate, trigarg->argnum, isnull);
if (*isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
{
*value = (Datum) 0;
@@ -2947,6 +3046,36 @@ exec_eval_datum(PLpgSQL_execstate *estate,
}
/* ----------
+ * exec_eval_subscript Hack to allow subscripting of result variables.
+ *
+ * The caller may already have an open eval_econtext, which we have to
+ * save and restore around the call of exec_eval_expr.
+ * ----------
+ */
+static int
+exec_eval_subscript(PLpgSQL_execstate * estate,
+ PLpgSQL_expr * expr,
+ bool *isNull)
+{
+ ExprContext *save_econtext;
+ Datum subscriptdatum;
+ Oid subscripttypeid;
+ int result;
+
+ save_econtext = estate->eval_econtext;
+ estate->eval_econtext = NULL;
+ subscriptdatum = exec_eval_expr(estate, expr, isNull, &subscripttypeid);
+ subscriptdatum = exec_simple_cast_value(subscriptdatum,
+ subscripttypeid,
+ INT4OID, -1,
+ isNull);
+ result = DatumGetInt32(subscriptdatum);
+ exec_eval_cleanup(estate);
+ estate->eval_econtext = save_econtext;
+ return result;
+}
+
+/* ----------
* exec_eval_expr Evaluate an expression and return
* the result Datum.
*
@@ -3323,6 +3452,43 @@ exec_cast_value(Datum value, Oid valtype,
return value;
}
+/* ----------
+ * exec_simple_cast_value Cast a value if required
+ *
+ * As above, but need not supply details about target type. Note that this
+ * is slower than exec_cast_value with cached type info, and so should be
+ * avoided in heavily used code paths.
+ * ----------
+ */
+static Datum
+exec_simple_cast_value(Datum value, Oid valtype,
+ Oid reqtype, int32 reqtypmod,
+ bool *isnull)
+{
+ if (!*isnull)
+ {
+ if (valtype != reqtype || reqtypmod != -1)
+ {
+ Oid typInput;
+ Oid typElem;
+ FmgrInfo finfo_input;
+
+ getTypeInputInfo(reqtype, &typInput, &typElem);
+ fmgr_info(typInput, &finfo_input);
+
+ value = exec_cast_value(value,
+ valtype,
+ reqtype,
+ &finfo_input,
+ typElem,
+ reqtypmod,
+ isnull);
+ }
+ }
+
+ return value;
+}
+
/* ----------
* exec_simple_check_node - Recursively check if an expression
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 6ad1a3b0f36..c19399e336d 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.24 2003/03/25 00:34:23 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.25 2003/03/25 03:16:41 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -1023,6 +1023,12 @@ plpgsql_dumptree(PLpgSQL_function * func)
((PLpgSQL_recfield *) d)->fieldname,
((PLpgSQL_recfield *) d)->recparentno);
break;
+ case PLPGSQL_DTYPE_ARRAYELEM:
+ printf("ARRAYELEM of VAR %d subscript ",
+ ((PLpgSQL_arrayelem *) d)->arrayparentno);
+ dump_expr(((PLpgSQL_arrayelem *) d)->subscript);
+ printf("\n");
+ break;
case PLPGSQL_DTYPE_TRIGARG:
printf("TRIGARG ");
dump_expr(((PLpgSQL_trigarg *) d)->argnum);
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 26612281629..9706db602c2 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.32 2003/03/25 00:34:24 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.33 2003/03/25 03:16:41 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -72,6 +72,7 @@ enum
PLPGSQL_DTYPE_ROW,
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
+ PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_EXPR,
PLPGSQL_DTYPE_TRIGARG
};
@@ -154,7 +155,8 @@ typedef struct
/*
* PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
- * PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, PLpgSQL_trigarg
+ * PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, PLpgSQL_arrayelem, and
+ * PLpgSQL_trigarg
*/
typedef struct
{ /* Generic datum array item */
@@ -231,11 +233,20 @@ typedef struct
int dtype;
int rfno;
char *fieldname;
- int recparentno; /* recno of parent record */
+ int recparentno; /* dno of parent record */
} PLpgSQL_recfield;
typedef struct
+{ /* Element of array variable */
+ int dtype;
+ int dno;
+ PLpgSQL_expr *subscript;
+ int arrayparentno; /* dno of parent array variable */
+} PLpgSQL_arrayelem;
+
+
+typedef struct
{ /* Positional argument to trigger */
int dtype;
int dno;