Commit 1788828d authored by Tom Lane's avatar Tom Lane

Remove PLPGSQL_DTYPE_ARRAYELEM datum type within pl/pgsql.

In the wake of the previous commit, we don't really need this anymore,
since array assignment is primarily handled by the core code.

The only way that that code could still be reached is that a GET
DIAGNOSTICS target variable could be an array element.  But that
doesn't seem like a particularly essential feature.  I'd added it
in commit 55caaaeb, but just because it was easy not because
anyone had actually asked for it.  Hence, revert that patch and
then remove the now-unreachable stuff.  (If we really had to,
we could probably reimplement GET DIAGNOSTICS using the new
assignment machinery; but the cost/benefit ratio looks very poor,
and it'd likely be a bit slower.)

Note that PLPGSQL_DTYPE_RECFIELD remains.  It's possible that we
could get rid of that too, but maintaining the existing behaviors
for RECORD-type variables seems like it might be difficult.  Since
there's not any functional limitation in those code paths as there
was in the ARRAYELEM code, I've not pursued the idea.

Discussion: https://postgr.es/m/4165684.1607707277@sss.pgh.pa.us
parent c9d52984
...@@ -1311,12 +1311,11 @@ copy_plpgsql_datums(PLpgSQL_execstate *estate, ...@@ -1311,12 +1311,11 @@ copy_plpgsql_datums(PLpgSQL_execstate *estate,
case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_RECFIELD:
case PLPGSQL_DTYPE_ARRAYELEM:
/* /*
* These datum records are read-only at runtime, so no need to * These datum records are read-only at runtime, so no need to
* copy them (well, RECFIELD and ARRAYELEM contain cached * copy them (well, RECFIELD contains cached data, but we'd
* data, but we'd just as soon centralize the caching anyway). * just as soon centralize the caching anyway).
*/ */
outdatum = indatum; outdatum = indatum;
break; break;
...@@ -4136,9 +4135,6 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate, ...@@ -4136,9 +4135,6 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
* *
* NB: the result of the evaluation is no longer valid after this is done, * NB: the result of the evaluation is no longer valid after this is done,
* unless it is a pass-by-value datatype. * 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 static void
...@@ -5288,198 +5284,6 @@ exec_assign_value(PLpgSQL_execstate *estate, ...@@ -5288,198 +5284,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
break; 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: default:
elog(ERROR, "unrecognized dtype: %d", target->dtype); elog(ERROR, "unrecognized dtype: %d", target->dtype);
} }
...@@ -5490,8 +5294,8 @@ exec_assign_value(PLpgSQL_execstate *estate, ...@@ -5490,8 +5294,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
* *
* The type oid, typmod, value in Datum format, and null flag are returned. * 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; * At present this doesn't handle PLpgSQL_expr datums; that's not needed
* that's not needed because we never pass references to such datums to SPI. * because we never pass references to such datums to SPI.
* *
* NOTE: the returned Datum points right at the stored value in the case of * 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 * pass-by-reference datatypes. Generally callers should take care not to
......
...@@ -768,9 +768,6 @@ plpgsql_free_function_memory(PLpgSQL_function *func) ...@@ -768,9 +768,6 @@ plpgsql_free_function_memory(PLpgSQL_function *func)
break; break;
case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_RECFIELD:
break; break;
case PLPGSQL_DTYPE_ARRAYELEM:
free_expr(((PLpgSQL_arrayelem *) d)->subscript);
break;
default: default:
elog(ERROR, "unrecognized data type: %d", d->dtype); elog(ERROR, "unrecognized data type: %d", d->dtype);
} }
...@@ -1704,12 +1701,6 @@ plpgsql_dumptree(PLpgSQL_function *func) ...@@ -1704,12 +1701,6 @@ plpgsql_dumptree(PLpgSQL_function *func)
((PLpgSQL_recfield *) d)->fieldname, ((PLpgSQL_recfield *) d)->fieldname,
((PLpgSQL_recfield *) d)->recparentno); ((PLpgSQL_recfield *) d)->recparentno);
break; 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: default:
printf("??? unknown data type %d\n", d->dtype); printf("??? unknown data type %d\n", d->dtype);
} }
......
...@@ -177,11 +177,10 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); ...@@ -177,11 +177,10 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%type <list> decl_cursor_arglist %type <list> decl_cursor_arglist
%type <nsitem> decl_aliasitem %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> expr_until_then expr_until_loop opt_expr_until_when
%type <expr> opt_exitcond %type <expr> opt_exitcond
%type <datum> assign_var
%type <var> cursor_variable %type <var> cursor_variable
%type <datum> decl_cursor_arg %type <datum> decl_cursor_arg
%type <forvariable> for_variable %type <forvariable> for_variable
...@@ -1155,16 +1154,23 @@ getdiag_item : ...@@ -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, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable", errmsg("\"%s\" is not a scalar variable",
((PLpgSQL_variable *) $1)->refname), NameOfDatum(&($1))),
parser_errposition(@1))); parser_errposition(@1)));
$$ = $1; check_assignable($1.datum, @1);
$$ = $1.datum;
} }
| T_WORD | T_WORD
{ {
...@@ -1178,29 +1184,6 @@ getdiag_target : assign_var ...@@ -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 ';' stmt_if : K_IF expr_until_then proc_sect stmt_elsifs stmt_else K_END K_IF ';'
{ {
PLpgSQL_stmt_if *new; PLpgSQL_stmt_if *new;
...@@ -2471,10 +2454,6 @@ expr_until_semi : ...@@ -2471,10 +2454,6 @@ expr_until_semi :
{ $$ = read_sql_expression(';', ";"); } { $$ = read_sql_expression(';', ";"); }
; ;
expr_until_rightbracket :
{ $$ = read_sql_expression(']', "]"); }
;
expr_until_then : expr_until_then :
{ $$ = read_sql_expression(K_THEN, "THEN"); } { $$ = read_sql_expression(K_THEN, "THEN"); }
; ;
...@@ -3493,11 +3472,6 @@ check_assignable(PLpgSQL_datum *datum, int location) ...@@ -3493,11 +3472,6 @@ check_assignable(PLpgSQL_datum *datum, int location)
check_assignable(plpgsql_Datums[((PLpgSQL_recfield *) datum)->recparentno], check_assignable(plpgsql_Datums[((PLpgSQL_recfield *) datum)->recparentno],
location); location);
break; break;
case PLPGSQL_DTYPE_ARRAYELEM:
/* assignable if parent array is */
check_assignable(plpgsql_Datums[((PLpgSQL_arrayelem *) datum)->arrayparentno],
location);
break;
default: default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype); elog(ERROR, "unrecognized dtype: %d", datum->dtype);
break; break;
......
...@@ -64,7 +64,6 @@ typedef enum PLpgSQL_datum_type ...@@ -64,7 +64,6 @@ typedef enum PLpgSQL_datum_type
PLPGSQL_DTYPE_ROW, PLPGSQL_DTYPE_ROW,
PLPGSQL_DTYPE_REC, PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD, PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_PROMISE PLPGSQL_DTYPE_PROMISE
} PLpgSQL_datum_type; } PLpgSQL_datum_type;
...@@ -261,7 +260,7 @@ typedef struct PLpgSQL_expr ...@@ -261,7 +260,7 @@ typedef struct PLpgSQL_expr
* Generic datum array item * Generic datum array item
* *
* PLpgSQL_datum is the common supertype for PLpgSQL_var, PLpgSQL_row, * 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 typedef struct PLpgSQL_datum
{ {
...@@ -422,30 +421,6 @@ typedef struct PLpgSQL_recfield ...@@ -422,30 +421,6 @@ typedef struct PLpgSQL_recfield
/* if rectupledescid == INVALID_TUPLEDESC_IDENTIFIER, finfo isn't valid */ /* if rectupledescid == INVALID_TUPLEDESC_IDENTIFIER, finfo isn't valid */
} 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 * Item in the compilers namespace tree
*/ */
......
...@@ -4230,7 +4230,6 @@ drop function tftest(int); ...@@ -4230,7 +4230,6 @@ drop function tftest(int);
create or replace function rttest() create or replace function rttest()
returns setof int as $$ returns setof int as $$
declare rc int; declare rc int;
rca int[];
begin begin
return query values(10),(20); return query values(10),(20);
get diagnostics rc = row_count; get diagnostics rc = row_count;
...@@ -4239,12 +4238,11 @@ begin ...@@ -4239,12 +4238,11 @@ begin
get diagnostics rc = row_count; get diagnostics rc = row_count;
raise notice '% %', found, rc; raise notice '% %', found, rc;
return query execute 'values(10),(20)'; return query execute 'values(10),(20)';
-- just for fun, let's use array elements as targets get diagnostics rc = row_count;
get diagnostics rca[1] = row_count; raise notice '% %', found, rc;
raise notice '% %', found, rca[1];
return query execute 'select * from (values(10),(20)) f(a) where false'; return query execute 'select * from (values(10),(20)) f(a) where false';
get diagnostics rca[2] = row_count; get diagnostics rc = row_count;
raise notice '% %', found, rca[2]; raise notice '% %', found, rc;
end; end;
$$ language plpgsql; $$ language plpgsql;
select * from rttest(); select * from rttest();
......
...@@ -3497,7 +3497,6 @@ drop function tftest(int); ...@@ -3497,7 +3497,6 @@ drop function tftest(int);
create or replace function rttest() create or replace function rttest()
returns setof int as $$ returns setof int as $$
declare rc int; declare rc int;
rca int[];
begin begin
return query values(10),(20); return query values(10),(20);
get diagnostics rc = row_count; get diagnostics rc = row_count;
...@@ -3506,12 +3505,11 @@ begin ...@@ -3506,12 +3505,11 @@ begin
get diagnostics rc = row_count; get diagnostics rc = row_count;
raise notice '% %', found, rc; raise notice '% %', found, rc;
return query execute 'values(10),(20)'; return query execute 'values(10),(20)';
-- just for fun, let's use array elements as targets get diagnostics rc = row_count;
get diagnostics rca[1] = row_count; raise notice '% %', found, rc;
raise notice '% %', found, rca[1];
return query execute 'select * from (values(10),(20)) f(a) where false'; return query execute 'select * from (values(10),(20)) f(a) where false';
get diagnostics rca[2] = row_count; get diagnostics rc = row_count;
raise notice '% %', found, rca[2]; raise notice '% %', found, rc;
end; end;
$$ language plpgsql; $$ language plpgsql;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment