From 41e09bbbad3a3506f7491d141208d68fa1d2488e Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 17 Jan 2006 17:33:37 +0000 Subject: [PATCH] Repair problems with the result of lookup_rowtype_tupdesc() possibly being discarded by cache flush while still in use. This is a minimal patch that just copies the tupdesc anywhere it could be needed across a flush. Applied to back branches only; Neil Conway is working on a better long-term solution for HEAD. --- src/backend/access/heap/tuptoaster.c | 5 +++ src/backend/parser/parse_coerce.c | 4 +- src/backend/parser/parse_target.c | 3 ++ src/backend/utils/adt/rowtypes.c | 66 ++++++++++++++++------------ src/backend/utils/adt/ruleutils.c | 1 + src/backend/utils/cache/typcache.c | 2 + src/pl/plperl/plperl.c | 4 +- src/pl/plpgsql/src/pl_exec.c | 6 +++ src/pl/plpython/plpython.c | 2 + src/pl/tcl/pltcl.c | 2 + 10 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index afde5b556c..4b3a4c55da 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -820,6 +820,7 @@ toast_flatten_tuple_attribute(Datum value, if (tupleDesc == NULL) return value; /* not a composite type */ + tupleDesc = CreateTupleDescCopy(tupleDesc); att = tupleDesc->attrs; numAttrs = tupleDesc->natts; @@ -866,7 +867,10 @@ toast_flatten_tuple_attribute(Datum value, * If nothing to untoast, just return the original tuple. */ if (!need_change) + { + FreeTupleDesc(tupleDesc); return value; + } /* * Calculate the new size of the tuple. Header size should not @@ -903,6 +907,7 @@ toast_flatten_tuple_attribute(Datum value, for (i = 0; i < numAttrs; i++) if (toast_free[i]) pfree(DatumGetPointer(toast_values[i])); + FreeTupleDesc(tupleDesc); return PointerGetDatum(new_data); } diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 838ff01a7d..fe87c23a5b 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -700,7 +700,7 @@ coerce_record_to_complex(ParseState *pstate, Node *node, format_type_be(RECORDOID), format_type_be(targetTypeId)))); - tupdesc = lookup_rowtype_tupdesc(targetTypeId, -1); + tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(targetTypeId, -1)); newargs = NIL; ucolno = 1; arg = list_head(args); @@ -758,6 +758,8 @@ coerce_record_to_complex(ParseState *pstate, Node *node, format_type_be(targetTypeId)), errdetail("Input has too many columns."))); + FreeTupleDesc(tupdesc); + rowexpr = makeNode(RowExpr); rowexpr->args = newargs; rowexpr->row_typeid = targetTypeId; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index fa72a9bdeb..dc5c55daba 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -839,6 +839,7 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind) /* Verify it's a composite type, and get the tupdesc */ tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr)); + tupleDesc = CreateTupleDescCopy(tupleDesc); /* Generate a list of references to the individual fields */ numAttrs = tupleDesc->natts; @@ -889,6 +890,8 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind) te_list = lappend(te_list, te); } + FreeTupleDesc(tupleDesc); + return te_list; } diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c index debb779dfa..bb25ec3c75 100644 --- a/src/backend/utils/adt/rowtypes.c +++ b/src/backend/utils/adt/rowtypes.c @@ -54,6 +54,7 @@ record_in(PG_FUNCTION_ARGS) { char *string = PG_GETARG_CSTRING(0); Oid tupType = PG_GETARG_OID(1); + HeapTupleHeader result; int32 tupTypmod; TupleDesc tupdesc; HeapTuple tuple; @@ -78,6 +79,7 @@ record_in(PG_FUNCTION_ARGS) errmsg("input of anonymous composite types is not implemented"))); tupTypmod = -1; /* for all non-anonymous types */ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); ncolumns = tupdesc->natts; /* @@ -244,11 +246,21 @@ record_in(PG_FUNCTION_ARGS) tuple = heap_formtuple(tupdesc, values, nulls); + /* + * We cannot return tuple->t_data because heap_formtuple allocates it + * as part of a larger chunk, and our caller may expect to be able to + * pfree our result. So must copy the info into a new palloc chunk. + */ + result = (HeapTupleHeader) palloc(tuple->t_len); + memcpy(result, tuple->t_data, tuple->t_len); + + heap_freetuple(tuple); pfree(buf.data); pfree(values); pfree(nulls); + FreeTupleDesc(tupdesc); - PG_RETURN_HEAPTUPLEHEADER(tuple->t_data); + PG_RETURN_HEAPTUPLEHEADER(result); } /* @@ -258,7 +270,7 @@ Datum record_out(PG_FUNCTION_ARGS) { HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); - Oid tupType = PG_GETARG_OID(1); + Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; @@ -270,19 +282,11 @@ record_out(PG_FUNCTION_ARGS) char *nulls; StringInfoData buf; - /* - * Use the passed type unless it's RECORD; in that case, we'd better - * get the type info out of the datum itself. Note that for RECORD, - * what we'll probably actually get is RECORD's typelem, ie, zero. - */ - if (tupType == InvalidOid || tupType == RECORDOID) - { - tupType = HeapTupleHeaderGetTypeId(rec); - tupTypmod = HeapTupleHeaderGetTypMod(rec); - } - else - tupTypmod = -1; + /* Extract type info from the tuple itself */ + tupType = HeapTupleHeaderGetTypeId(rec); + tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); ncolumns = tupdesc->natts; /* Build a temporary HeapTuple control structure */ @@ -407,6 +411,7 @@ record_out(PG_FUNCTION_ARGS) pfree(values); pfree(nulls); + FreeTupleDesc(tupdesc); PG_RETURN_CSTRING(buf.data); } @@ -419,6 +424,7 @@ record_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); Oid tupType = PG_GETARG_OID(1); + HeapTupleHeader result; int32 tupTypmod; TupleDesc tupdesc; HeapTuple tuple; @@ -442,6 +448,7 @@ record_recv(PG_FUNCTION_ARGS) errmsg("input of anonymous composite types is not implemented"))); tupTypmod = -1; /* for all non-anonymous types */ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); ncolumns = tupdesc->natts; /* @@ -580,10 +587,20 @@ record_recv(PG_FUNCTION_ARGS) tuple = heap_formtuple(tupdesc, values, nulls); + /* + * We cannot return tuple->t_data because heap_formtuple allocates it + * as part of a larger chunk, and our caller may expect to be able to + * pfree our result. So must copy the info into a new palloc chunk. + */ + result = (HeapTupleHeader) palloc(tuple->t_len); + memcpy(result, tuple->t_data, tuple->t_len); + + heap_freetuple(tuple); pfree(values); pfree(nulls); + FreeTupleDesc(tupdesc); - PG_RETURN_HEAPTUPLEHEADER(tuple->t_data); + PG_RETURN_HEAPTUPLEHEADER(result); } /* @@ -593,7 +610,7 @@ Datum record_send(PG_FUNCTION_ARGS) { HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); - Oid tupType = PG_GETARG_OID(1); + Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; @@ -605,19 +622,11 @@ record_send(PG_FUNCTION_ARGS) char *nulls; StringInfoData buf; - /* - * Use the passed type unless it's RECORD; in that case, we'd better - * get the type info out of the datum itself. Note that for RECORD, - * what we'll probably actually get is RECORD's typelem, ie, zero. - */ - if (tupType == InvalidOid || tupType == RECORDOID) - { - tupType = HeapTupleHeaderGetTypeId(rec); - tupTypmod = HeapTupleHeaderGetTypMod(rec); - } - else - tupTypmod = -1; + /* Extract type info from the tuple itself */ + tupType = HeapTupleHeaderGetTypeId(rec); + tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); ncolumns = tupdesc->natts; /* Build a temporary HeapTuple control structure */ @@ -720,6 +729,7 @@ record_send(PG_FUNCTION_ARGS) pfree(values); pfree(nulls); + FreeTupleDesc(tupdesc); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index f359919036..c185b05a9d 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3245,6 +3245,7 @@ get_rule_expr(Node *node, deparse_context *context, if (rowexpr->row_typeid != RECORDOID) { tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); + tupdesc = CreateTupleDescCopy(tupdesc); Assert(list_length(rowexpr->args) <= tupdesc->natts); } diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 03c26d9325..d3553b9e40 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -383,6 +383,8 @@ lookup_default_opclass(Oid type_id, Oid am_id) * * Note: returned TupleDesc points to cached copy; caller must copy it * if intending to scribble on it or keep a reference for a long time. + * ("A long time" basically means "across any possible cache flush", + * which typically could occur at any relation open or catalog lookup.) */ TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod) diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 1cd4dce259..016b0cfd5f 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -705,12 +705,14 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo) tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; hashref = plperl_hash_from_tuple(&tmptup, tupdesc); XPUSHs(sv_2mortal(hashref)); + FreeTupleDesc(tupdesc); } else { @@ -1009,7 +1011,7 @@ plperl_func_handler(PG_FUNCTION_ARGS) */ td = get_function_tupdesc(prodesc->result_oid, (ReturnSetInfo *) fcinfo->resultinfo); - /* td = CreateTupleDescCopy(td); */ + td = CreateTupleDescCopy(td); attinmeta = TupleDescGetAttInMetadata(td); tup = plperl_build_tuple_result(perlhash, attinmeta); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 588c880118..13f1c17c82 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -273,12 +273,14 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = td; exec_move_row(&estate, NULL, row, &tmptup, tupdesc); + FreeTupleDesc(tupdesc); } else { @@ -2948,12 +2950,14 @@ exec_assign_value(PLpgSQL_execstate *estate, tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = td; exec_move_row(estate, NULL, row, &tmptup, tupdesc); + FreeTupleDesc(tupdesc); } break; } @@ -2990,12 +2994,14 @@ exec_assign_value(PLpgSQL_execstate *estate, tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = td; exec_move_row(estate, rec, NULL, &tmptup, tupdesc); + FreeTupleDesc(tupdesc); } break; } diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index 160531b7ff..8a75fa2ba5 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -861,6 +861,7 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc) tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); /* Set up I/O funcs if not done yet */ if (proc->args[i].is_rowtype != 1) @@ -871,6 +872,7 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc) tmptup.t_data = td; arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc); + FreeTupleDesc(tupdesc); } } else diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index 64b5185f69..f4ecb390ca 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -533,6 +533,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + tupdesc = CreateTupleDescCopy(tupdesc); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; @@ -541,6 +542,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp); Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&list_tmp)); + FreeTupleDesc(tupdesc); } } else -- 2.39.5