/*------------------------------------------------------------------------- * * preptlist.c * Routines to preprocess the parse tree target list * * This module takes care of altering the query targetlist as needed for * INSERT, UPDATE, and DELETE queries. For INSERT and UPDATE queries, * the targetlist must contain an entry for each attribute of the target * relation in the correct order. For both UPDATE and DELETE queries, * we need a junk targetlist entry holding the CTID attribute --- the * executor relies on this to find the tuple to be replaced/deleted. * * * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.32 1999/10/30 23:06:32 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "optimizer/prep.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" static List *expand_targetlist(List *tlist, int command_type, Index result_relation, List *range_table); /* * preprocess_targetlist * Driver for preprocessing the parse tree targetlist. * * Returns the new targetlist. */ List * preprocess_targetlist(List *tlist, int command_type, Index result_relation, List *range_table) { /* * for heap_formtuple to work, the targetlist must match the exact * order of the attributes. We also need to fill in any missing * attributes. -ay 10/94 */ if (command_type == CMD_INSERT || command_type == CMD_UPDATE) tlist = expand_targetlist(tlist, command_type, result_relation, range_table); /* * for "update" and "delete" queries, add ctid of the result * relation into the target list so that the ctid will propagate * through execution and ExecutePlan() will be able to identify * the right tuple to replace or delete. This extra field is * marked "junk" so that it is not stored back into the tuple. */ if (command_type == CMD_UPDATE || command_type == CMD_DELETE) { Resdom *resdom; Var *var; resdom = makeResdom(length(tlist) + 1, TIDOID, -1, pstrdup("ctid"), 0, 0, true); var = makeVar(result_relation, SelfItemPointerAttributeNumber, TIDOID, -1, 0); /* For an UPDATE, expand_targetlist already created a fresh tlist. * For DELETE, better do a listCopy so that we don't destructively * modify the original tlist (is this really necessary?). */ if (command_type == CMD_DELETE) tlist = listCopy(tlist); tlist = lappend(tlist, makeTargetEntry(resdom, (Node *) var)); } return tlist; } /***************************************************************************** * * TARGETLIST EXPANSION * *****************************************************************************/ /* * expand_targetlist * Given a target list as generated by the parser and a result relation, * add targetlist entries for any missing attributes, and order the * non-junk attributes in proper field order. */ static List * expand_targetlist(List *tlist, int command_type, Index result_relation, List *range_table) { int old_tlist_len = length(tlist); List *new_tlist = NIL; bool *tlistentry_used; Relation rel; int attrno, numattrs, old_tlist_index; List *temp; /* * Keep a map of which tlist items we have transferred to new list. * +1 here keeps palloc from complaining if old_tlist_len=0. */ tlistentry_used = (bool *) palloc((old_tlist_len+1) * sizeof(bool)); memset(tlistentry_used, 0, (old_tlist_len+1) * sizeof(bool)); /* * Scan the tuple description in the relation's relcache entry to make * sure we have all the user attributes in the right order. */ rel = heap_open(getrelid(result_relation, range_table), AccessShareLock); numattrs = RelationGetNumberOfAttributes(rel); for (attrno = 1; attrno <= numattrs; attrno++) { Form_pg_attribute att_tup = rel->rd_att->attrs[attrno-1]; char *attrname = att_tup->attname.data; TargetEntry *new_tle = NULL; /* * We match targetlist entries to attributes using the resname. */ old_tlist_index = 0; foreach(temp, tlist) { TargetEntry *old_tle = (TargetEntry *) lfirst(temp); Resdom *resdom = old_tle->resdom; if (! tlistentry_used[old_tlist_index] && strcmp(resdom->resname, attrname) == 0 && ! resdom->resjunk) { /* * We can recycle the old TLE+resdom if right resno; else * make a new one to avoid modifying the old tlist structure. * (Is preserving old tlist actually necessary?) */ if (resdom->resno == attrno) { new_tle = old_tle; } else { resdom = (Resdom *) copyObject((Node *) resdom); resdom->resno = attrno; new_tle = makeTargetEntry(resdom, old_tle->expr); } tlistentry_used[old_tlist_index] = true; break; } old_tlist_index++; } if (new_tle == NULL) { /* * Didn't find a matching tlist entry, so make one. * * For INSERT, generate a constant of the default value for * the attribute type, or NULL if no default value. * * For UPDATE, generate a Var reference to the existing value * of the attribute, so that it gets copied to the new tuple. */ Oid atttype = att_tup->atttypid; int32 atttypmod = att_tup->atttypmod; switch (command_type) { case CMD_INSERT: { Datum typedefault = get_typdefault(atttype); int typlen; Const *temp_const; if (typedefault == PointerGetDatum(NULL)) typlen = 0; else { /* * Since this is an append or replace, the size of * any set attribute is the size of the OID used to * represent it. */ if (att_tup->attisset) typlen = get_typlen(OIDOID); else typlen = get_typlen(atttype); } temp_const = makeConst(atttype, typlen, typedefault, (typedefault == PointerGetDatum(NULL)), false, false, /* not a set */ false); new_tle = makeTargetEntry(makeResdom(attrno, atttype, -1, pstrdup(attrname), 0, (Oid) 0, false), (Node *) temp_const); break; } case CMD_UPDATE: { Var *temp_var; temp_var = makeVar(result_relation, attrno, atttype, atttypmod, 0); new_tle = makeTargetEntry(makeResdom(attrno, atttype, atttypmod, pstrdup(attrname), 0, (Oid) 0, false), (Node *) temp_var); break; } default: elog(ERROR, "expand_targetlist: unexpected command_type"); break; } } new_tlist = lappend(new_tlist, new_tle); } /* * Copy all unprocessed tlist entries to the end of the new tlist, * making sure they are marked resjunk = true. Typical junk entries * include ORDER BY or GROUP BY expressions (are these actually possible * in an INSERT or UPDATE?), system attribute references, etc. */ old_tlist_index = 0; foreach(temp, tlist) { TargetEntry *old_tle = (TargetEntry *) lfirst(temp); if (! tlistentry_used[old_tlist_index]) { Resdom *resdom; resdom = (Resdom *) copyObject((Node *) old_tle->resdom); resdom->resno = attrno++; resdom->resjunk = true; new_tlist = lappend(new_tlist, makeTargetEntry(resdom, old_tle->expr)); } old_tlist_index++; } heap_close(rel, AccessShareLock); pfree(tlistentry_used); return new_tlist; }