/*------------------------------------------------------------------------- * * junk.c * Junk attribute support stuff.... * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.16 1999/02/13 23:15:16 momjian Exp $ * *------------------------------------------------------------------------- */ #include #include "postgres.h" #include "utils/palloc.h" #include "access/heapam.h" #include "executor/executor.h" #include "nodes/relation.h" #include "nodes/makefuncs.h" /*------------------------------------------------------------------------- * XXX this stuff should be rewritten to take advantage * of ExecProject() and the ProjectionInfo node. * -cim 6/3/91 * * An attribute of a tuple living inside the executor, can be * either a normal attribute or a "junk" attribute. "junk" attributes * never make it out of the executor, i.e. they are never printed, * returned or stored in disk. Their only purpose in life is to * store some information useful only to the executor, mainly the values * of some system attributes like "ctid" or rule locks. * * The general idea is the following: A target list consists of a list of * Resdom nodes & expression pairs. Each Resdom node has an attribute * called 'resjunk'. If the value of this attribute is 1 then the * corresponding attribute is a "junk" attribute. * * When we initialize a plan we call 'ExecInitJunkFilter' to create * and store the appropriate information in the 'es_junkFilter' attribute of * EState. * * We then execute the plan ignoring the "resjunk" attributes. * * Finally, when at the top level we get back a tuple, we can call * 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we * are interested in, and 'ExecRemoveJunk' to remove all the junk attributes * from a tuple. This new "clean" tuple is then printed, replaced, deleted * or inserted. * *------------------------------------------------------------------------- */ /*------------------------------------------------------------------------- * ExecInitJunkFilter * * Initialize the Junk filter. *------------------------------------------------------------------------- */ JunkFilter * ExecInitJunkFilter(List *targetList) { JunkFilter *junkfilter; List *cleanTargetList; int len, cleanLength; TupleDesc tupType, cleanTupType; List *t; TargetEntry *tle; Resdom *resdom, *cleanResdom; int resjunk; AttrNumber cleanResno; AttrNumber *cleanMap; Size size; Node *expr; /* --------------------- * First find the "clean" target list, i.e. all the entries * in the original target list which have a zero 'resjunk' * NOTE: make copy of the Resdom nodes, because we have * to change the 'resno's... * --------------------- */ cleanTargetList = NIL; cleanResno = 1; foreach(t, targetList) { TargetEntry *rtarget = lfirst(t); if (rtarget->resdom != NULL) { resdom = rtarget->resdom; expr = rtarget->expr; resjunk = resdom->resjunk; if (resjunk == 0) { /* * make a copy of the resdom node, changing its resno. */ cleanResdom = (Resdom *) copyObject(resdom); cleanResdom->resno = cleanResno; cleanResno++; /* * create a new target list entry */ tle = makeTargetEntry(cleanResdom, expr); cleanTargetList = lappend(cleanTargetList, tle); } } else { #ifdef SETS_FIXED List *fjListP; Fjoin *cleanFjoin; List *cleanFjList; List *fjList = lfirst(t); Fjoin *fjNode = (Fjoin *) tl_node(fjList); cleanFjoin = (Fjoin) copyObject((Node) fjNode); cleanFjList = lcons(cleanFjoin, NIL); resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); expr = lsecond(get_fj_innerNode(fjNode)); cleanResdom = (Resdom) copyObject((Node) resdom); set_resno(cleanResdom, cleanResno); cleanResno++; tle = (List) makeTargetEntry(cleanResdom, (Node *) expr); set_fj_innerNode(cleanFjoin, tle); foreach(fjListP, lnext(fjList)) { TargetEntry *tle = lfirst(fjListP); resdom = tle->resdom; expr = tle->expr; cleanResdom = (Resdom *) copyObject((Node) resdom); cleanResno++; cleanResdom->Resno = cleanResno; /* * create a new target list entry */ tle = (List) makeTargetEntry(cleanResdom, (Node *) expr); cleanFjList = lappend(cleanFjList, tle); } lappend(cleanTargetList, cleanFjList); #endif } } /* --------------------- * Now calculate the tuple types for the original and the clean tuple * * XXX ExecTypeFromTL should be used sparingly. Don't we already * have the tupType corresponding to the targetlist we are passed? * -cim 5/31/91 * --------------------- */ tupType = (TupleDesc) ExecTypeFromTL(targetList); cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList); len = ExecTargetListLength(targetList); cleanLength = ExecTargetListLength(cleanTargetList); /* --------------------- * Now calculate the "map" between the original tuples attributes * and the "clean" tuple's attributes. * * The "map" is an array of "cleanLength" attribute numbers, i.e. * one entry for every attribute of the "clean" tuple. * The value of this entry is the attribute number of the corresponding * attribute of the "original" tuple. * --------------------- */ if (cleanLength > 0) { size = cleanLength * sizeof(AttrNumber); cleanMap = (AttrNumber *) palloc(size); cleanResno = 1; foreach(t, targetList) { TargetEntry *tle = lfirst(t); if (tle->resdom != NULL) { resdom = tle->resdom; expr = tle->expr; resjunk = resdom->resjunk; if (resjunk == 0) { cleanMap[cleanResno - 1] = resdom->resno; cleanResno++; } } else { #ifdef SETS_FIXED List fjListP; List fjList = lfirst(t); Fjoin fjNode = (Fjoin) lfirst(fjList); /* what the hell is this????? */ resdom = (Resdom) lfirst(get_fj_innerNode(fjNode)); #endif cleanMap[cleanResno - 1] = tle->resdom->resno; cleanResno++; #ifdef SETS_FIXED foreach(fjListP, lnext(fjList)) { TargetEntry *tle = lfirst(fjListP); resdom = tle->resdom; cleanMap[cleanResno - 1] = resdom->resno; cleanResno++; } #endif } } } else cleanMap = NULL; /* --------------------- * Finally create and initialize the JunkFilter. * --------------------- */ junkfilter = makeNode(JunkFilter); junkfilter->jf_targetList = targetList; junkfilter->jf_length = len; junkfilter->jf_tupType = tupType; junkfilter->jf_cleanTargetList = cleanTargetList; junkfilter->jf_cleanLength = cleanLength; junkfilter->jf_cleanTupType = cleanTupType; junkfilter->jf_cleanMap = cleanMap; return junkfilter; } /*------------------------------------------------------------------------- * ExecGetJunkAttribute * * Given a tuple (slot), the junk filter and a junk attribute's name, * extract & return the value of this attribute. * * It returns false iff no junk attribute with such name was found. * * NOTE: isNull might be NULL ! *------------------------------------------------------------------------- */ bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, char *attrName, Datum *value, bool *isNull) { List *targetList; List *t; Resdom *resdom; AttrNumber resno; char *resname; int resjunk; TupleDesc tupType; HeapTuple tuple; /* --------------------- * first look in the junkfilter's target list for * an attribute with the given name * --------------------- */ resno = InvalidAttrNumber; targetList = junkfilter->jf_targetList; foreach(t, targetList) { TargetEntry *tle = lfirst(t); resdom = tle->resdom; resname = resdom->resname; resjunk = resdom->resjunk; if (resjunk != 0 && (strcmp(resname, attrName) == 0)) { /* We found it ! */ resno = resdom->resno; break; } } if (resno == InvalidAttrNumber) { /* Ooops! We couldn't find this attribute... */ return false; } /* --------------------- * Now extract the attribute value from the tuple. * --------------------- */ tuple = slot->val; tupType = (TupleDesc) junkfilter->jf_tupType; *value = heap_getattr(tuple, resno, tupType, isNull); return true; } /*------------------------------------------------------------------------- * ExecRemoveJunk * * Construct and return a tuple with all the junk attributes removed. *------------------------------------------------------------------------- */ HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) { HeapTuple tuple; HeapTuple cleanTuple; AttrNumber *cleanMap; TupleDesc cleanTupType; TupleDesc tupType; int cleanLength; bool isNull; int i; Size size; Datum *values; char *nulls; Datum values_array[64]; char nulls_array[64]; /* ---------------- * get info from the slot and the junk filter * ---------------- */ tuple = slot->val; tupType = (TupleDesc) junkfilter->jf_tupType; cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType; cleanLength = junkfilter->jf_cleanLength; cleanMap = junkfilter->jf_cleanMap; /* --------------------- * Handle the trivial case first. * --------------------- */ if (cleanLength == 0) return (HeapTuple) NULL; /* --------------------- * Create the arrays that will hold the attribute values * and the null information for the new "clean" tuple. * * Note: we use memory on the stack to optimize things when * we are dealing with a small number of tuples. * for large tuples we just use palloc. * --------------------- */ if (cleanLength > 64) { size = cleanLength * sizeof(Datum); values = (Datum *) palloc(size); size = cleanLength * sizeof(char); nulls = (char *) palloc(size); } else { values = values_array; nulls = nulls_array; } /* --------------------- * Exctract one by one all the values of the "clean" tuple. * --------------------- */ for (i = 0; i < cleanLength; i++) { values[i] = heap_getattr(tuple, cleanMap[i], tupType, &isNull); if (isNull) nulls[i] = 'n'; else nulls[i] = ' '; } /* --------------------- * Now form the new tuple. * --------------------- */ cleanTuple = heap_formtuple(cleanTupType, values, nulls); /* --------------------- * We are done. Free any space allocated for 'values' and 'nulls' * and return the new tuple. * --------------------- */ if (cleanLength > 64) { pfree(values); pfree(nulls); } return cleanTuple; }