mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-21 08:29:39 +08:00
Make plpgsql provide the typmods for its variables to the main parser.
Historically we didn't do this, even though we had the information, because plpgsql passed its Params via SPI APIs that only include type OIDs not typmods. Now that plpgsql uses parser callbacks to create Params, it's easy to insert the right typmod. This should generally result in lower surprise factors, because a plpgsql variable that is declared with a typmod will now work more like a table column with the same typmod. In particular it's the "right" way to fix bug #6020, in which plpgsql's attempt to return an anonymous record type is defeated by stricter record-type matching checks that were added in 9.0. However, it's not impossible that this could result in subtle behavioral changes that could break somebody's existing plpgsql code, so I'm afraid to back-patch this change into released branches. In those branches we'll have to lobotomize the record-type checks instead.
This commit is contained in:
parent
fae625e7b9
commit
219dfae4e1
@ -1267,9 +1267,11 @@ make_datum_param(PLpgSQL_expr *expr, int dno, int location)
|
||||
param = makeNode(Param);
|
||||
param->paramkind = PARAM_EXTERN;
|
||||
param->paramid = dno + 1;
|
||||
param->paramtype = exec_get_datum_type(estate, datum);
|
||||
param->paramtypmod = -1;
|
||||
param->paramcollid = exec_get_datum_collation(estate, datum);
|
||||
exec_get_datum_type_info(estate,
|
||||
datum,
|
||||
¶m->paramtype,
|
||||
¶m->paramtypmod,
|
||||
¶m->paramcollid);
|
||||
param->location = location;
|
||||
|
||||
return (Node *) param;
|
||||
|
@ -4307,29 +4307,63 @@ exec_get_datum_type(PLpgSQL_execstate *estate,
|
||||
}
|
||||
|
||||
/*
|
||||
* exec_get_datum_collation Get collation of a PLpgSQL_datum
|
||||
* exec_get_datum_type_info Get datatype etc of a PLpgSQL_datum
|
||||
*
|
||||
* An extended version of exec_get_datum_type, which also retrieves the
|
||||
* typmod and collation of the datum.
|
||||
*/
|
||||
Oid
|
||||
exec_get_datum_collation(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_datum *datum)
|
||||
void
|
||||
exec_get_datum_type_info(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_datum *datum,
|
||||
Oid *typeid, int32 *typmod, Oid *collation)
|
||||
{
|
||||
Oid collid;
|
||||
|
||||
switch (datum->dtype)
|
||||
{
|
||||
case PLPGSQL_DTYPE_VAR:
|
||||
{
|
||||
PLpgSQL_var *var = (PLpgSQL_var *) datum;
|
||||
|
||||
collid = var->datatype->collation;
|
||||
*typeid = var->datatype->typoid;
|
||||
*typmod = var->datatype->atttypmod;
|
||||
*collation = var->datatype->collation;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLPGSQL_DTYPE_ROW:
|
||||
case PLPGSQL_DTYPE_REC:
|
||||
{
|
||||
PLpgSQL_row *row = (PLpgSQL_row *) datum;
|
||||
|
||||
if (!row->rowtupdesc) /* should not happen */
|
||||
elog(ERROR, "row variable has no tupdesc");
|
||||
/* Make sure we have a valid type/typmod setting */
|
||||
BlessTupleDesc(row->rowtupdesc);
|
||||
*typeid = row->rowtupdesc->tdtypeid;
|
||||
/* do NOT return the mutable typmod of a RECORD variable */
|
||||
*typmod = -1;
|
||||
/* composite types are never collatable */
|
||||
collid = InvalidOid;
|
||||
*collation = InvalidOid;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLPGSQL_DTYPE_REC:
|
||||
{
|
||||
PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
|
||||
|
||||
if (rec->tupdesc == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("record \"%s\" is not assigned yet",
|
||||
rec->refname),
|
||||
errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
|
||||
/* Make sure we have a valid type/typmod setting */
|
||||
BlessTupleDesc(rec->tupdesc);
|
||||
*typeid = rec->tupdesc->tdtypeid;
|
||||
/* do NOT return the mutable typmod of a RECORD variable */
|
||||
*typmod = -1;
|
||||
/* composite types are never collatable */
|
||||
*collation = InvalidOid;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLPGSQL_DTYPE_RECFIELD:
|
||||
{
|
||||
@ -4350,21 +4384,27 @@ exec_get_datum_collation(PLpgSQL_execstate *estate,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("record \"%s\" has no field \"%s\"",
|
||||
rec->refname, recfield->fieldname)));
|
||||
/* XXX there's no SPI_getcollid, as yet */
|
||||
*typeid = SPI_gettypeid(rec->tupdesc, fno);
|
||||
/* XXX there's no SPI_gettypmod, for some reason */
|
||||
if (fno > 0)
|
||||
collid = rec->tupdesc->attrs[fno - 1]->attcollation;
|
||||
*typmod = rec->tupdesc->attrs[fno - 1]->atttypmod;
|
||||
else
|
||||
*typmod = -1;
|
||||
/* XXX there's no SPI_getcollation either */
|
||||
if (fno > 0)
|
||||
*collation = rec->tupdesc->attrs[fno - 1]->attcollation;
|
||||
else /* no system column types have collation */
|
||||
collid = InvalidOid;
|
||||
*collation = InvalidOid;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
|
||||
collid = InvalidOid; /* keep compiler quiet */
|
||||
*typeid = InvalidOid; /* keep compiler quiet */
|
||||
*typmod = -1;
|
||||
*collation = InvalidOid;
|
||||
break;
|
||||
}
|
||||
|
||||
return collid;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
|
@ -905,8 +905,9 @@ extern void plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
|
||||
SubTransactionId parentSubid, void *arg);
|
||||
extern Oid exec_get_datum_type(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_datum *datum);
|
||||
extern Oid exec_get_datum_collation(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_datum *datum);
|
||||
extern void exec_get_datum_type_info(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_datum *datum,
|
||||
Oid *typeid, int32 *typmod, Oid *collation);
|
||||
|
||||
/* ----------
|
||||
* Functions for namespace handling in pl_funcs.c
|
||||
|
Loading…
Reference in New Issue
Block a user