ColumnRef *cref, bool error_if_no_field);
static Node *make_datum_param(PLpgSQL_expr *expr, int dno, int location);
static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars);
-static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod, Oid collation);
+static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod,
+ Oid collation, TypeName *origtypname);
static void plpgsql_start_datums(void);
static void plpgsql_finish_datums(PLpgSQL_function *function);
static void compute_function_hashkey(FunctionCallInfo fcinfo,
/* Create datatype info */
argdtype = plpgsql_build_datatype(argtypeid,
-1,
- function->fn_input_collation);
+ function->fn_input_collation,
+ NULL);
/* Disallow pseudotype argument */
/* (note we already replaced polymorphic types) */
(void) plpgsql_build_variable("$0", 0,
build_datatype(typeTup,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
}
var = plpgsql_build_variable("tg_name", 0,
plpgsql_build_datatype(NAMEOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_when", 0,
plpgsql_build_datatype(TEXTOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_level", 0,
plpgsql_build_datatype(TEXTOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_op", 0,
plpgsql_build_datatype(TEXTOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_relid", 0,
plpgsql_build_datatype(OIDOID,
-1,
- InvalidOid),
+ InvalidOid,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_relname", 0,
plpgsql_build_datatype(NAMEOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_table_name", 0,
plpgsql_build_datatype(NAMEOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_table_schema", 0,
plpgsql_build_datatype(NAMEOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_nargs", 0,
plpgsql_build_datatype(INT4OID,
-1,
- InvalidOid),
+ InvalidOid,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_argv", 0,
plpgsql_build_datatype(TEXTARRAYOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_event", 0,
plpgsql_build_datatype(TEXTOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_tag", 0,
plpgsql_build_datatype(TEXTOID,
-1,
- function->fn_input_collation),
+ function->fn_input_collation,
+ NULL),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("found", 0,
plpgsql_build_datatype(BOOLOID,
-1,
- InvalidOid),
+ InvalidOid,
+ NULL),
true);
function->found_varno = var->dno;
var = plpgsql_build_variable("found", 0,
plpgsql_build_datatype(BOOLOID,
-1,
- InvalidOid),
+ InvalidOid,
+ NULL),
true);
function->found_varno = var->dno;
{
PLpgSQL_type *dtype;
PLpgSQL_nsitem *nse;
+ TypeName *typeName;
HeapTuple typeTup;
/*
* Word wasn't found in the namespace stack. Try to find a data type with
* that name, but ignore shell types and complex types.
*/
- typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL, false);
+ typeName = makeTypeName(ident);
+ typeTup = LookupTypeName(NULL, typeName, NULL, false);
if (typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
}
dtype = build_datatype(typeTup, -1,
- plpgsql_curr_compile->fn_input_collation);
+ plpgsql_curr_compile->fn_input_collation,
+ typeName);
ReleaseSysCache(typeTup);
return dtype;
/*
* Found that - build a compiler type struct in the caller's cxt and
- * return it
+ * return it. Note that we treat the type as being found-by-OID; no
+ * attempt to re-look-up the type name will happen during invalidations.
*/
MemoryContextSwitchTo(oldCxt);
dtype = build_datatype(typetup,
attrStruct->atttypmod,
- attrStruct->attcollation);
+ attrStruct->attcollation,
+ NULL);
MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
done:
{
Oid classOid;
- /* Lookup the relation */
+ /*
+ * Look up the relation. Note that because relation rowtypes have the
+ * same names as their relations, this could be handled as a type lookup
+ * equally well; we use the relation lookup code path only because the
+ * errors thrown here have traditionally referred to relations not types.
+ * But we'll make a TypeName in case we have to do re-look-up of the type.
+ */
classOid = RelnameGetRelid(ident);
if (!OidIsValid(classOid))
ereport(ERROR,
errmsg("relation \"%s\" does not exist", ident)));
/* Build and return the row type struct */
- return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid);
+ return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid,
+ makeTypeName(ident));
}
/* ----------
RangeVar *relvar;
MemoryContext oldCxt;
+ /*
+ * As above, this is a relation lookup but could be a type lookup if we
+ * weren't being backwards-compatible about error wording.
+ */
if (list_length(idents) != 2)
return NULL;
MemoryContextSwitchTo(oldCxt);
/* Build and return the row type struct */
- return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid);
+ return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid,
+ makeTypeNameFromNameList(idents));
}
/*
break;
case PLPGSQL_DTYPE_REC:
+ /* shouldn't need to revalidate rectypeid already... */
typoid = ((PLpgSQL_rec *) var)->rectypeid;
typmod = -1; /* don't know typmod, if it's used at all */
typcoll = InvalidOid; /* composite types have no collation */
/*
* plpgsql_build_datatype
- * Build PLpgSQL_type struct given type OID, typmod, and collation.
+ * Build PLpgSQL_type struct given type OID, typmod, collation,
+ * and type's parsed name.
*
* If collation is not InvalidOid then it overrides the type's default
* collation. But collation is ignored if the datatype is non-collatable.
+ *
+ * origtypname is the parsed form of what the user wrote as the type name.
+ * It can be NULL if the type could not be a composite type, or if it was
+ * identified by OID to begin with (e.g., it's a function argument type).
*/
PLpgSQL_type *
-plpgsql_build_datatype(Oid typeOid, int32 typmod, Oid collation)
+plpgsql_build_datatype(Oid typeOid, int32 typmod,
+ Oid collation, TypeName *origtypname)
{
HeapTuple typeTup;
PLpgSQL_type *typ;
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup failed for type %u", typeOid);
- typ = build_datatype(typeTup, typmod, collation);
+ typ = build_datatype(typeTup, typmod, collation, origtypname);
ReleaseSysCache(typeTup);
/*
* Utility subroutine to make a PLpgSQL_type struct given a pg_type entry
+ * and additional details (see comments for plpgsql_build_datatype).
*/
static PLpgSQL_type *
-build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
+build_datatype(HeapTuple typeTup, int32 typmod,
+ Oid collation, TypeName *origtypname)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
PLpgSQL_type *typ;
typ->typisarray = false;
typ->atttypmod = typmod;
+ /*
+ * If it's a named composite type (or domain over one), find the typcache
+ * entry and record the current tupdesc ID, so we can detect changes
+ * (including drops). We don't currently support on-the-fly replacement
+ * of non-composite types, else we might want to do this for them too.
+ */
+ if (typ->ttype == PLPGSQL_TTYPE_REC && typ->typoid != RECORDOID)
+ {
+ TypeCacheEntry *typentry;
+
+ typentry = lookup_type_cache(typ->typoid,
+ TYPECACHE_TUPDESC |
+ TYPECACHE_DOMAIN_BASE_INFO);
+ if (typentry->typtype == TYPTYPE_DOMAIN)
+ typentry = lookup_type_cache(typentry->domainBaseType,
+ TYPECACHE_TUPDESC);
+ if (typentry->tupDesc == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("type %s is not composite",
+ format_type_be(typ->typoid))));
+
+ typ->origtypname = origtypname;
+ typ->tcache = typentry;
+ typ->tupdesc_id = typentry->tupDesc_identifier;
+ }
+ else
+ {
+ typ->origtypname = NULL;
+ typ->tcache = NULL;
+ typ->tupdesc_id = 0;
+ }
+
return typ;
}
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_type.h"
#include "parser/scansup.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
static void exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_variable *target,
HeapTuple tup, TupleDesc tupdesc);
+static void revalidate_rectypeid(PLpgSQL_rec *rec);
static ExpandedRecordHeader *make_expanded_record_for_rec(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec,
TupleDesc srctupdesc,
t_var->datatype->atttypmod != t_typmod)
t_var->datatype = plpgsql_build_datatype(t_typoid,
t_typmod,
- estate->func->fn_input_collation);
+ estate->func->fn_input_collation,
+ NULL);
/* now we can assign to the variable */
exec_assign_value(estate,
}
}
+/*
+ * Verify that a PLpgSQL_rec's rectypeid is up-to-date.
+ */
+static void
+revalidate_rectypeid(PLpgSQL_rec *rec)
+{
+ PLpgSQL_type *typ = rec->datatype;
+ TypeCacheEntry *typentry;
+
+ if (rec->rectypeid == RECORDOID)
+ return; /* it's RECORD, so nothing to do */
+ Assert(typ != NULL);
+ if (typ->tcache &&
+ typ->tcache->tupDesc_identifier == typ->tupdesc_id)
+ return; /* known up-to-date */
+
+ /*
+ * typcache entry has suffered invalidation, so re-look-up the type name
+ * if possible, and then recheck the type OID. If we don't have a
+ * TypeName, then we just have to soldier on with the OID we've got.
+ */
+ if (typ->origtypname != NULL)
+ {
+ /* this bit should match parse_datatype() in pl_gram.y */
+ typenameTypeIdAndMod(NULL, typ->origtypname,
+ &typ->typoid,
+ &typ->atttypmod);
+ }
+
+ /* this bit should match build_datatype() in pl_comp.c */
+ typentry = lookup_type_cache(typ->typoid,
+ TYPECACHE_TUPDESC |
+ TYPECACHE_DOMAIN_BASE_INFO);
+ if (typentry->typtype == TYPTYPE_DOMAIN)
+ typentry = lookup_type_cache(typentry->domainBaseType,
+ TYPECACHE_TUPDESC);
+ if (typentry->tupDesc == NULL)
+ {
+ /*
+ * If we get here, user tried to replace a composite type with a
+ * non-composite one. We're not gonna support that.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("type %s is not composite",
+ format_type_be(typ->typoid))));
+ }
+
+ /*
+ * Update tcache and tupdesc_id. Since we don't support changing to a
+ * non-composite type, none of the rest of *typ needs to change.
+ */
+ typ->tcache = typentry;
+ typ->tupdesc_id = typentry->tupDesc_identifier;
+
+ /*
+ * Update *rec, too. (We'll deal with subsidiary RECFIELDs as needed.)
+ */
+ rec->rectypeid = typ->typoid;
+}
+
/*
* Build an expanded record object suitable for assignment to "rec".
*
if (rec->rectypeid != RECORDOID)
{
+ /*
+ * Make sure rec->rectypeid is up-to-date before using it.
+ */
+ revalidate_rectypeid(rec);
+
/*
* New record must be of desired type, but maybe srcerh has already
* done all the same lookups.
if (erh == rec->erh)
return;
+ /*
+ * Make sure rec->rectypeid is up-to-date before using it.
+ */
+ revalidate_rectypeid(rec);
+
/*
* If we have a R/W pointer, we're allowed to just commandeer
* ownership of the expanded record. If it's of the right type to
errmsg("record \"%s\" is not assigned yet", rec->refname),
errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
+ /* Make sure rec->rectypeid is up-to-date before using it */
+ revalidate_rectypeid(rec);
+
/* OK, do it */
rec->erh = make_expanded_record_from_typeid(rec->rectypeid, -1,
estate->datum_context);