aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pl/plpgsql/src/pl_exec.c204
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c9
-rw-r--r--src/pl/plpgsql/src/pl_gram.y52
-rw-r--r--src/pl/plpgsql/src/plpgsql.h27
-rw-r--r--src/test/regress/expected/plpgsql.out10
-rw-r--r--src/test/regress/sql/plpgsql.sql10
6 files changed, 26 insertions, 286 deletions
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 5e4dbd25a2a..95f0adc81d9 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -1311,12 +1311,11 @@ copy_plpgsql_datums(PLpgSQL_execstate *estate,
case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_RECFIELD:
- case PLPGSQL_DTYPE_ARRAYELEM:
/*
* These datum records are read-only at runtime, so no need to
- * copy them (well, RECFIELD and ARRAYELEM contain cached
- * data, but we'd just as soon centralize the caching anyway).
+ * copy them (well, RECFIELD contains cached data, but we'd
+ * just as soon centralize the caching anyway).
*/
outdatum = indatum;
break;
@@ -4136,9 +4135,6 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
*
* NB: the result of the evaluation is no longer valid after this is done,
* unless it is a pass-by-value datatype.
- *
- * NB: if you change this code, see also the hacks in exec_assign_value's
- * PLPGSQL_DTYPE_ARRAYELEM case for partial cleanup after subscript evals.
* ----------
*/
static void
@@ -5288,198 +5284,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
break;
}
- case PLPGSQL_DTYPE_ARRAYELEM:
- {
- /*
- * Target is an element of an array
- */
- PLpgSQL_arrayelem *arrayelem;
- int nsubscripts;
- int i;
- PLpgSQL_expr *subscripts[MAXDIM];
- int subscriptvals[MAXDIM];
- Datum oldarraydatum,
- newarraydatum,
- coerced_value;
- bool oldarrayisnull;
- Oid parenttypoid;
- int32 parenttypmod;
- SPITupleTable *save_eval_tuptable;
- MemoryContext oldcontext;
-
- /*
- * We need to do subscript evaluation, which might require
- * evaluating general expressions; and the caller might have
- * done that too in order to prepare the input Datum. We have
- * to save and restore the caller's SPI_execute result, if
- * any.
- */
- save_eval_tuptable = estate->eval_tuptable;
- estate->eval_tuptable = NULL;
-
- /*
- * 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.) Note that arrayelem is left pointing
- * to the leftmost arrayelem datum, where we will cache the
- * array element type data.
- */
- nsubscripts = 0;
- do
- {
- arrayelem = (PLpgSQL_arrayelem *) target;
- if (nsubscripts >= MAXDIM)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
- nsubscripts + 1, MAXDIM)));
- 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,
- &parenttypoid, &parenttypmod,
- &oldarraydatum, &oldarrayisnull);
-
- /* Update cached type data if necessary */
- if (arrayelem->parenttypoid != parenttypoid ||
- arrayelem->parenttypmod != parenttypmod)
- {
- Oid arraytypoid;
- int32 arraytypmod = parenttypmod;
- int16 arraytyplen;
- Oid elemtypoid;
- int16 elemtyplen;
- bool elemtypbyval;
- char elemtypalign;
-
- /* If target is domain over array, reduce to base type */
- arraytypoid = getBaseTypeAndTypmod(parenttypoid,
- &arraytypmod);
-
- /* ... and identify the element type */
- elemtypoid = get_element_type(arraytypoid);
- if (!OidIsValid(elemtypoid))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("subscripted object is not an array")));
-
- /* Collect needed data about the types */
- arraytyplen = get_typlen(arraytypoid);
-
- get_typlenbyvalalign(elemtypoid,
- &elemtyplen,
- &elemtypbyval,
- &elemtypalign);
-
- /* Now safe to update the cached data */
- arrayelem->parenttypoid = parenttypoid;
- arrayelem->parenttypmod = parenttypmod;
- arrayelem->arraytypoid = arraytypoid;
- arrayelem->arraytypmod = arraytypmod;
- arrayelem->arraytyplen = arraytyplen;
- arrayelem->elemtypoid = elemtypoid;
- arrayelem->elemtyplen = elemtyplen;
- arrayelem->elemtypbyval = elemtypbyval;
- arrayelem->elemtypalign = elemtypalign;
- }
-
- /*
- * Evaluate the subscripts, switch into left-to-right order.
- * Like the expression built by ExecInitSubscriptingRef(),
- * complain if any subscript is null.
- */
- for (i = 0; i < nsubscripts; i++)
- {
- bool subisnull;
-
- subscriptvals[i] =
- exec_eval_integer(estate,
- subscripts[nsubscripts - 1 - i],
- &subisnull);
- if (subisnull)
- ereport(ERROR,
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
-
- /*
- * Clean up in case the subscript expression wasn't
- * simple. We can't do exec_eval_cleanup, but we can do
- * this much (which is safe because the integer subscript
- * value is surely pass-by-value), and we must do it in
- * case the next subscript expression isn't simple either.
- */
- if (estate->eval_tuptable != NULL)
- SPI_freetuptable(estate->eval_tuptable);
- estate->eval_tuptable = NULL;
- }
-
- /* Now we can restore caller's SPI_execute result if any. */
- Assert(estate->eval_tuptable == NULL);
- estate->eval_tuptable = save_eval_tuptable;
-
- /* Coerce source value to match array element type. */
- coerced_value = exec_cast_value(estate,
- value,
- &isNull,
- valtype,
- valtypmod,
- arrayelem->elemtypoid,
- arrayelem->arraytypmod);
-
- /*
- * 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 execExpr*.c.
- */
- if (arrayelem->arraytyplen > 0 && /* fixed-length array? */
- (oldarrayisnull || isNull))
- return;
-
- /* empty array, if any, and newarraydatum are short-lived */
- oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
-
- if (oldarrayisnull)
- oldarraydatum = PointerGetDatum(construct_empty_array(arrayelem->elemtypoid));
-
- /*
- * Build the modified array value.
- */
- newarraydatum = array_set_element(oldarraydatum,
- nsubscripts,
- subscriptvals,
- coerced_value,
- isNull,
- arrayelem->arraytyplen,
- arrayelem->elemtyplen,
- arrayelem->elemtypbyval,
- arrayelem->elemtypalign);
-
- MemoryContextSwitchTo(oldcontext);
-
- /*
- * Assign the new array to the base variable. It's never NULL
- * at this point. Note that if the target is a domain,
- * coercing the base array type back up to the domain will
- * happen within exec_assign_value.
- */
- exec_assign_value(estate, target,
- newarraydatum,
- false,
- arrayelem->arraytypoid,
- arrayelem->arraytypmod);
- break;
- }
-
default:
elog(ERROR, "unrecognized dtype: %d", target->dtype);
}
@@ -5490,8 +5294,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
*
* The type oid, typmod, value in Datum format, and null flag are returned.
*
- * At present this doesn't handle PLpgSQL_expr or PLpgSQL_arrayelem datums;
- * that's not needed because we never pass references to such datums to SPI.
+ * At present this doesn't handle PLpgSQL_expr datums; that's not needed
+ * because we never pass references to such datums to SPI.
*
* NOTE: the returned Datum points right at the stored value in the case of
* pass-by-reference datatypes. Generally callers should take care not to
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index ac72bfa8c03..919b968826c 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -768,9 +768,6 @@ plpgsql_free_function_memory(PLpgSQL_function *func)
break;
case PLPGSQL_DTYPE_RECFIELD:
break;
- case PLPGSQL_DTYPE_ARRAYELEM:
- free_expr(((PLpgSQL_arrayelem *) d)->subscript);
- break;
default:
elog(ERROR, "unrecognized data type: %d", d->dtype);
}
@@ -1704,12 +1701,6 @@ 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;
default:
printf("??? unknown data type %d\n", d->dtype);
}
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index ad248bc7648..0fdbaa5eab8 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -177,11 +177,10 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%type <list> decl_cursor_arglist
%type <nsitem> decl_aliasitem
-%type <expr> expr_until_semi expr_until_rightbracket
+%type <expr> expr_until_semi
%type <expr> expr_until_then expr_until_loop opt_expr_until_when
%type <expr> opt_exitcond
-%type <datum> assign_var
%type <var> cursor_variable
%type <datum> decl_cursor_arg
%type <forvariable> for_variable
@@ -1155,16 +1154,23 @@ getdiag_item :
}
;
-getdiag_target : assign_var
+getdiag_target : T_DATUM
{
- if ($1->dtype == PLPGSQL_DTYPE_ROW ||
- $1->dtype == PLPGSQL_DTYPE_REC)
+ /*
+ * In principle we should support a getdiag_target
+ * that is an array element, but for now we don't, so
+ * just throw an error if next token is '['.
+ */
+ if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ $1.datum->dtype == PLPGSQL_DTYPE_REC ||
+ plpgsql_peek() == '[')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
- ((PLpgSQL_variable *) $1)->refname),
+ NameOfDatum(&($1))),
parser_errposition(@1)));
- $$ = $1;
+ check_assignable($1.datum, @1);
+ $$ = $1.datum;
}
| T_WORD
{
@@ -1178,29 +1184,6 @@ getdiag_target : assign_var
}
;
-
-assign_var : T_DATUM
- {
- check_assignable($1.datum, @1);
- $$ = $1.datum;
- }
- | assign_var '[' expr_until_rightbracket
- {
- PLpgSQL_arrayelem *new;
-
- new = palloc0(sizeof(PLpgSQL_arrayelem));
- new->dtype = PLPGSQL_DTYPE_ARRAYELEM;
- new->subscript = $3;
- new->arrayparentno = $1->dno;
- /* initialize cached type data to "not valid" */
- new->parenttypoid = InvalidOid;
-
- plpgsql_adddatum((PLpgSQL_datum *) new);
-
- $$ = (PLpgSQL_datum *) new;
- }
- ;
-
stmt_if : K_IF expr_until_then proc_sect stmt_elsifs stmt_else K_END K_IF ';'
{
PLpgSQL_stmt_if *new;
@@ -2471,10 +2454,6 @@ expr_until_semi :
{ $$ = read_sql_expression(';', ";"); }
;
-expr_until_rightbracket :
- { $$ = read_sql_expression(']', "]"); }
- ;
-
expr_until_then :
{ $$ = read_sql_expression(K_THEN, "THEN"); }
;
@@ -3493,11 +3472,6 @@ check_assignable(PLpgSQL_datum *datum, int location)
check_assignable(plpgsql_Datums[((PLpgSQL_recfield *) datum)->recparentno],
location);
break;
- case PLPGSQL_DTYPE_ARRAYELEM:
- /* assignable if parent array is */
- check_assignable(plpgsql_Datums[((PLpgSQL_arrayelem *) datum)->arrayparentno],
- location);
- break;
default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
break;
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index f80d023a5ad..32061e54e00 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -64,7 +64,6 @@ typedef enum PLpgSQL_datum_type
PLPGSQL_DTYPE_ROW,
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
- PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_PROMISE
} PLpgSQL_datum_type;
@@ -261,7 +260,7 @@ typedef struct PLpgSQL_expr
* Generic datum array item
*
* PLpgSQL_datum is the common supertype for PLpgSQL_var, PLpgSQL_row,
- * PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem.
+ * PLpgSQL_rec, and PLpgSQL_recfield.
*/
typedef struct PLpgSQL_datum
{
@@ -423,30 +422,6 @@ typedef struct PLpgSQL_recfield
} PLpgSQL_recfield;
/*
- * Element of array variable
- */
-typedef struct PLpgSQL_arrayelem
-{
- PLpgSQL_datum_type dtype;
- int dno;
- /* end of PLpgSQL_datum fields */
-
- PLpgSQL_expr *subscript;
- int arrayparentno; /* dno of parent array variable */
-
- /* Remaining fields are cached info about the array variable's type */
- Oid parenttypoid; /* type of array variable; 0 if not yet set */
- int32 parenttypmod; /* typmod of array variable */
- Oid arraytypoid; /* OID of actual array type */
- int32 arraytypmod; /* typmod of array (and its elements too) */
- int16 arraytyplen; /* typlen of array type */
- Oid elemtypoid; /* OID of array element type */
- int16 elemtyplen; /* typlen of element type */
- bool elemtypbyval; /* element type is pass-by-value? */
- char elemtypalign; /* typalign of element type */
-} PLpgSQL_arrayelem;
-
-/*
* Item in the compilers namespace tree
*/
typedef struct PLpgSQL_nsitem
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 0c6c9ba1f59..6ea169d9add 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -4230,7 +4230,6 @@ drop function tftest(int);
create or replace function rttest()
returns setof int as $$
declare rc int;
- rca int[];
begin
return query values(10),(20);
get diagnostics rc = row_count;
@@ -4239,12 +4238,11 @@ begin
get diagnostics rc = row_count;
raise notice '% %', found, rc;
return query execute 'values(10),(20)';
- -- just for fun, let's use array elements as targets
- get diagnostics rca[1] = row_count;
- raise notice '% %', found, rca[1];
+ get diagnostics rc = row_count;
+ raise notice '% %', found, rc;
return query execute 'select * from (values(10),(20)) f(a) where false';
- get diagnostics rca[2] = row_count;
- raise notice '% %', found, rca[2];
+ get diagnostics rc = row_count;
+ raise notice '% %', found, rc;
end;
$$ language plpgsql;
select * from rttest();
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 07c60c80e48..781666a83a1 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -3497,7 +3497,6 @@ drop function tftest(int);
create or replace function rttest()
returns setof int as $$
declare rc int;
- rca int[];
begin
return query values(10),(20);
get diagnostics rc = row_count;
@@ -3506,12 +3505,11 @@ begin
get diagnostics rc = row_count;
raise notice '% %', found, rc;
return query execute 'values(10),(20)';
- -- just for fun, let's use array elements as targets
- get diagnostics rca[1] = row_count;
- raise notice '% %', found, rca[1];
+ get diagnostics rc = row_count;
+ raise notice '% %', found, rc;
return query execute 'select * from (values(10),(20)) f(a) where false';
- get diagnostics rca[2] = row_count;
- raise notice '% %', found, rca[2];
+ get diagnostics rc = row_count;
+ raise notice '% %', found, rc;
end;
$$ language plpgsql;