from a constraint condition does not violate the constraint (cf. discussion on pghackers 12/9/99). Implemented by adding a parameter to ExecQual, specifying whether to return TRUE or FALSE when the qual result is really NULL in three-valued boolean logic. Currently, ExecRelCheck is the only caller that asks for TRUE, but if we find any other places that have the wrong response to NULL, it'll be easy to fix them.
1249 lines
32 KiB
C
1249 lines
32 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* execUtils.c
|
|
* miscellanious executor utility routines
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.52 2000/01/19 23:54:54 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/*
|
|
* INTERFACE ROUTINES
|
|
* ExecAssignNodeBaseInfo \
|
|
* ExecAssignDebugHooks > preforms misc work done in all the
|
|
* ExecAssignExprContext / init node routines.
|
|
*
|
|
* ExecGetTypeInfo | old execCStructs interface
|
|
* ExecMakeTypeInfo | code from the version 1
|
|
* ExecOrderTypeInfo | lisp system. These should
|
|
* ExecSetTypeInfo | go away or be updated soon.
|
|
* ExecFreeTypeInfo | -cim 11/1/89
|
|
* ExecTupleAttributes /
|
|
*
|
|
|
|
* QueryDescGetTypeInfo - moved here from main.c
|
|
* am not sure what uses it -cim 10/12/89
|
|
*
|
|
* ExecGetIndexKeyInfo \
|
|
* ExecOpenIndices | referenced by InitPlan, EndPlan,
|
|
* ExecCloseIndices | ExecAppend, ExecReplace
|
|
* ExecFormIndexTuple |
|
|
* ExecInsertIndexTuple /
|
|
*
|
|
* NOTES
|
|
* This file has traditionally been the place to stick misc.
|
|
* executor support stuff that doesn't really go anyplace else.
|
|
*
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/heapam.h"
|
|
#include "catalog/catname.h"
|
|
#include "catalog/index.h"
|
|
#include "catalog/pg_index.h"
|
|
#include "executor/execdebug.h"
|
|
#include "executor/executor.h"
|
|
|
|
static void ExecGetIndexKeyInfo(Form_pg_index indexTuple, int *numAttsOutP,
|
|
AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP);
|
|
|
|
/* ----------------------------------------------------------------
|
|
* global counters for number of tuples processed, retrieved,
|
|
* appended, replaced, deleted.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
int NTupleProcessed;
|
|
int NTupleRetrieved;
|
|
int NTupleReplaced;
|
|
int NTupleAppended;
|
|
int NTupleDeleted;
|
|
int NIndexTupleInserted;
|
|
extern int NIndexTupleProcessed; /* have to be defined in the
|
|
* access method level so that the
|
|
* cinterface.a will link ok. */
|
|
|
|
/* ----------------------------------------------------------------
|
|
* statistic functions
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ResetTupleCount
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
#ifdef NOT_USED
|
|
void
|
|
ResetTupleCount(void)
|
|
{
|
|
NTupleProcessed = 0;
|
|
NTupleRetrieved = 0;
|
|
NTupleAppended = 0;
|
|
NTupleDeleted = 0;
|
|
NTupleReplaced = 0;
|
|
NIndexTupleProcessed = 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ----------------------------------------------------------------
|
|
* PrintTupleCount
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
#ifdef NOT_USED
|
|
void
|
|
DisplayTupleCount(FILE *statfp)
|
|
{
|
|
if (NTupleProcessed > 0)
|
|
fprintf(statfp, "!\t%d tuple%s processed, ", NTupleProcessed,
|
|
(NTupleProcessed == 1) ? "" : "s");
|
|
else
|
|
{
|
|
fprintf(statfp, "!\tno tuples processed.\n");
|
|
return;
|
|
}
|
|
if (NIndexTupleProcessed > 0)
|
|
fprintf(statfp, "%d indextuple%s processed, ", NIndexTupleProcessed,
|
|
(NIndexTupleProcessed == 1) ? "" : "s");
|
|
if (NIndexTupleInserted > 0)
|
|
fprintf(statfp, "%d indextuple%s inserted, ", NIndexTupleInserted,
|
|
(NIndexTupleInserted == 1) ? "" : "s");
|
|
if (NTupleRetrieved > 0)
|
|
fprintf(statfp, "%d tuple%s retrieved. ", NTupleRetrieved,
|
|
(NTupleRetrieved == 1) ? "" : "s");
|
|
if (NTupleAppended > 0)
|
|
fprintf(statfp, "%d tuple%s appended. ", NTupleAppended,
|
|
(NTupleAppended == 1) ? "" : "s");
|
|
if (NTupleDeleted > 0)
|
|
fprintf(statfp, "%d tuple%s deleted. ", NTupleDeleted,
|
|
(NTupleDeleted == 1) ? "" : "s");
|
|
if (NTupleReplaced > 0)
|
|
fprintf(statfp, "%d tuple%s replaced. ", NTupleReplaced,
|
|
(NTupleReplaced == 1) ? "" : "s");
|
|
fprintf(statfp, "\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ----------------------------------------------------------------
|
|
* miscellanious init node support functions
|
|
*
|
|
* ExecAssignNodeBaseInfo - assigns the baseid field of the node
|
|
* ExecAssignDebugHooks - assigns the node's debugging hooks
|
|
* ExecAssignExprContext - assigns the node's expression context
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------
|
|
* ExecAssignNodeBaseInfo
|
|
*
|
|
* as it says, this assigns the baseid field of the node and
|
|
* increments the counter in the estate. In addition, it initializes
|
|
* the base_parent field of the basenode.
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent)
|
|
{
|
|
int baseId;
|
|
|
|
baseId = estate->es_BaseId;
|
|
cstate->cs_base_id = baseId;
|
|
estate->es_BaseId = baseId + 1;
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecAssignExprContext
|
|
*
|
|
* This initializes the ExprContext field. It is only necessary
|
|
* to do this for nodes which use ExecQual or ExecTargetList
|
|
* because those routines depend on econtext. Other nodes which
|
|
* dont have to evaluate expressions don't need to do this.
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecAssignExprContext(EState *estate, CommonState *commonstate)
|
|
{
|
|
ExprContext *econtext;
|
|
|
|
econtext = makeNode(ExprContext);
|
|
econtext->ecxt_scantuple = NULL; /* scan tuple slot */
|
|
econtext->ecxt_innertuple = NULL; /* inner tuple slot */
|
|
econtext->ecxt_outertuple = NULL; /* outer tuple slot */
|
|
econtext->ecxt_relation = NULL; /* relation */
|
|
econtext->ecxt_relid = 0; /* relid */
|
|
econtext->ecxt_param_list_info = estate->es_param_list_info;
|
|
econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
|
|
econtext->ecxt_range_table = estate->es_range_table; /* range table */
|
|
|
|
commonstate->cs_ExprContext = econtext;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Result slot tuple type and ProjectionInfo support
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------
|
|
* ExecAssignResultType
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecAssignResultType(CommonState *commonstate,
|
|
TupleDesc tupDesc)
|
|
{
|
|
TupleTableSlot *slot;
|
|
|
|
slot = commonstate->cs_ResultTupleSlot;
|
|
slot->ttc_tupleDescriptor = tupDesc;
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecAssignResultTypeFromOuterPlan
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
|
|
{
|
|
Plan *outerPlan;
|
|
TupleDesc tupDesc;
|
|
|
|
outerPlan = outerPlan(node);
|
|
tupDesc = ExecGetTupType(outerPlan);
|
|
|
|
ExecAssignResultType(commonstate, tupDesc);
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecAssignResultTypeFromTL
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
|
|
{
|
|
List *targetList;
|
|
int i;
|
|
int len;
|
|
List *tl;
|
|
TargetEntry *tle;
|
|
List *fjtl;
|
|
TupleDesc origTupDesc;
|
|
|
|
targetList = node->targetlist;
|
|
origTupDesc = ExecTypeFromTL(targetList);
|
|
len = ExecTargetListLength(targetList);
|
|
|
|
fjtl = NIL;
|
|
tl = targetList;
|
|
i = 0;
|
|
while (tl != NIL || fjtl != NIL)
|
|
{
|
|
if (fjtl != NIL)
|
|
{
|
|
tle = lfirst(fjtl);
|
|
fjtl = lnext(fjtl);
|
|
}
|
|
else
|
|
{
|
|
tle = lfirst(tl);
|
|
tl = lnext(tl);
|
|
}
|
|
#ifdef SETS_FIXED
|
|
if (!tl_is_resdom(tle))
|
|
{
|
|
Fjoin *fj = (Fjoin *) lfirst(tle);
|
|
|
|
/* it is a FJoin */
|
|
fjtl = lnext(tle);
|
|
tle = fj->fj_innerNode;
|
|
}
|
|
#endif
|
|
i++;
|
|
}
|
|
|
|
if (len > 0)
|
|
{
|
|
ExecAssignResultType(commonstate,
|
|
origTupDesc);
|
|
}
|
|
else
|
|
ExecAssignResultType(commonstate,
|
|
(TupleDesc) NULL);
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecGetResultType
|
|
* ----------------
|
|
*/
|
|
TupleDesc
|
|
ExecGetResultType(CommonState *commonstate)
|
|
{
|
|
TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
|
|
|
|
return slot->ttc_tupleDescriptor;
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecFreeResultType
|
|
* ----------------
|
|
*/
|
|
#ifdef NOT_USED
|
|
void
|
|
ExecFreeResultType(CommonState *commonstate)
|
|
{
|
|
TupleTableSlot *slot;
|
|
TupleDesc tupType;
|
|
|
|
slot = commonstate->cs_ResultTupleSlot;
|
|
tupType = slot->ttc_tupleDescriptor;
|
|
|
|
ExecFreeTypeInfo(tupType);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ----------------
|
|
* ExecAssignProjectionInfo
|
|
forms the projection information from the node's targetlist
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecAssignProjectionInfo(Plan *node, CommonState *commonstate)
|
|
{
|
|
ProjectionInfo *projInfo;
|
|
List *targetList;
|
|
int len;
|
|
|
|
targetList = node->targetlist;
|
|
len = ExecTargetListLength(targetList);
|
|
|
|
projInfo = makeNode(ProjectionInfo);
|
|
projInfo->pi_targetlist = targetList;
|
|
projInfo->pi_len = len;
|
|
projInfo->pi_tupValue = (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len);
|
|
projInfo->pi_exprContext = commonstate->cs_ExprContext;
|
|
projInfo->pi_slot = commonstate->cs_ResultTupleSlot;
|
|
|
|
commonstate->cs_ProjInfo = projInfo;
|
|
}
|
|
|
|
|
|
/* ----------------
|
|
* ExecFreeProjectionInfo
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecFreeProjectionInfo(CommonState *commonstate)
|
|
{
|
|
ProjectionInfo *projInfo;
|
|
|
|
/* ----------------
|
|
* get projection info. if NULL then this node has
|
|
* none so we just return.
|
|
* ----------------
|
|
*/
|
|
projInfo = commonstate->cs_ProjInfo;
|
|
if (projInfo == NULL)
|
|
return;
|
|
|
|
/* ----------------
|
|
* clean up memory used.
|
|
* ----------------
|
|
*/
|
|
if (projInfo->pi_tupValue != NULL)
|
|
pfree(projInfo->pi_tupValue);
|
|
|
|
pfree(projInfo);
|
|
commonstate->cs_ProjInfo = NULL;
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecFreeExprContext
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecFreeExprContext(CommonState *commonstate)
|
|
{
|
|
ExprContext *econtext;
|
|
|
|
/* ----------------
|
|
* get expression context. if NULL then this node has
|
|
* none so we just return.
|
|
* ----------------
|
|
*/
|
|
econtext = commonstate->cs_ExprContext;
|
|
if (econtext == NULL)
|
|
return;
|
|
|
|
/* ----------------
|
|
* clean up memory used.
|
|
* ----------------
|
|
*/
|
|
pfree(econtext);
|
|
commonstate->cs_ExprContext = NULL;
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecFreeTypeInfo
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecFreeTypeInfo(CommonState *commonstate)
|
|
{
|
|
TupleDesc tupDesc;
|
|
|
|
tupDesc = commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor;
|
|
if (tupDesc == NULL)
|
|
return;
|
|
|
|
/* ----------------
|
|
* clean up memory used.
|
|
* ----------------
|
|
*/
|
|
FreeTupleDesc(tupDesc);
|
|
commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor = NULL;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* the following scan type support functions are for
|
|
* those nodes which are stubborn and return tuples in
|
|
* their Scan tuple slot instead of their Result tuple
|
|
* slot.. luck fur us, these nodes do not do projections
|
|
* so we don't have to worry about getting the ProjectionInfo
|
|
* right for them... -cim 6/3/91
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------
|
|
* ExecGetScanType
|
|
* ----------------
|
|
*/
|
|
TupleDesc
|
|
ExecGetScanType(CommonScanState *csstate)
|
|
{
|
|
TupleTableSlot *slot = csstate->css_ScanTupleSlot;
|
|
|
|
return slot->ttc_tupleDescriptor;
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecFreeScanType
|
|
* ----------------
|
|
*/
|
|
#ifdef NOT_USED
|
|
void
|
|
ExecFreeScanType(CommonScanState *csstate)
|
|
{
|
|
TupleTableSlot *slot;
|
|
TupleDesc tupType;
|
|
|
|
slot = csstate->css_ScanTupleSlot;
|
|
tupType = slot->ttc_tupleDescriptor;
|
|
|
|
ExecFreeTypeInfo(tupType);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ----------------
|
|
* ExecAssignScanType
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecAssignScanType(CommonScanState *csstate,
|
|
TupleDesc tupDesc)
|
|
{
|
|
TupleTableSlot *slot;
|
|
|
|
slot = (TupleTableSlot *) csstate->css_ScanTupleSlot;
|
|
slot->ttc_tupleDescriptor = tupDesc;
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecAssignScanTypeFromOuterPlan
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate)
|
|
{
|
|
Plan *outerPlan;
|
|
TupleDesc tupDesc;
|
|
|
|
outerPlan = outerPlan(node);
|
|
tupDesc = ExecGetTupType(outerPlan);
|
|
|
|
ExecAssignScanType(csstate, tupDesc);
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecTypeFromTL support routines.
|
|
*
|
|
* these routines are used mainly from ExecTypeFromTL.
|
|
* -cim 6/12/90
|
|
*
|
|
* old comments
|
|
* Routines dealing with the structure 'attribute' which conatains
|
|
* the type information about attributes in a tuple:
|
|
*
|
|
* ExecMakeTypeInfo(noType)
|
|
* returns pointer to array of 'noType' structure 'attribute'.
|
|
* ExecSetTypeInfo(index, typeInfo, attNum, attLen)
|
|
* sets the element indexed by 'index' in typeInfo with
|
|
* the values: attNum, attLen.
|
|
* ExecFreeTypeInfo(typeInfo)
|
|
* frees the structure 'typeInfo'.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------
|
|
* ExecSetTypeInfo
|
|
*
|
|
* This initializes fields of a single attribute in a
|
|
* tuple descriptor from the specified parameters.
|
|
*
|
|
* XXX this duplicates much of the functionality of TupleDescInitEntry.
|
|
* the routines should be moved to the same place and be rewritten
|
|
* to share common code.
|
|
* ----------------
|
|
*/
|
|
#ifdef NOT_USED
|
|
void
|
|
ExecSetTypeInfo(int index,
|
|
TupleDesc typeInfo,
|
|
Oid typeID,
|
|
int attNum,
|
|
int attLen,
|
|
char *attName,
|
|
bool attbyVal,
|
|
char attalign)
|
|
{
|
|
Form_pg_attribute att;
|
|
|
|
/* ----------------
|
|
* get attribute pointer and preform a sanity check..
|
|
* ----------------
|
|
*/
|
|
att = typeInfo[index];
|
|
if (att == NULL)
|
|
elog(ERROR, "ExecSetTypeInfo: trying to assign through NULL");
|
|
|
|
/* ----------------
|
|
* assign values to the tuple descriptor, being careful not
|
|
* to copy a null attName..
|
|
*
|
|
* XXX it is unknown exactly what information is needed to
|
|
* initialize the attribute struct correctly so for now
|
|
* we use 0. this should be fixed -- otherwise we run the
|
|
* risk of using garbage data. -cim 5/5/91
|
|
* ----------------
|
|
*/
|
|
att->attrelid = 0; /* dummy value */
|
|
|
|
if (attName != (char *) NULL)
|
|
StrNCpy(NameStr(att->attname), attName, NAMEDATALEN);
|
|
else
|
|
MemSet(NameStr(att->attname), 0, NAMEDATALEN);
|
|
|
|
att->atttypid = typeID;
|
|
att->attdefrel = 0; /* dummy value */
|
|
att->attdisbursion = 0; /* dummy value */
|
|
att->atttyparg = 0; /* dummy value */
|
|
att->attlen = attLen;
|
|
att->attnum = attNum;
|
|
att->attbound = 0; /* dummy value */
|
|
att->attbyval = attbyVal;
|
|
att->attcanindex = 0; /* dummy value */
|
|
att->attproc = 0; /* dummy value */
|
|
att->attnelems = 0; /* dummy value */
|
|
att->attcacheoff = -1;
|
|
att->atttypmod = -1;
|
|
att->attisset = false;
|
|
att->attstorage = 'p';
|
|
att->attalign = attalign;
|
|
}
|
|
|
|
/* ----------------
|
|
* ExecFreeTypeInfo frees the array of attrbutes
|
|
* created by ExecMakeTypeInfo and returned by ExecTypeFromTL...
|
|
* ----------------
|
|
*/
|
|
void
|
|
ExecFreeTypeInfo(TupleDesc typeInfo)
|
|
{
|
|
/* ----------------
|
|
* do nothing if asked to free a null pointer
|
|
* ----------------
|
|
*/
|
|
if (typeInfo == NULL)
|
|
return;
|
|
|
|
/* ----------------
|
|
* the entire array of typeinfo pointers created by
|
|
* ExecMakeTypeInfo was allocated with a single palloc()
|
|
* so we can deallocate the whole array with a single pfree().
|
|
* (we should not try and free all the elements in the array)
|
|
* -cim 6/12/90
|
|
* ----------------
|
|
*/
|
|
pfree(typeInfo);
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* QueryDescGetTypeInfo
|
|
*
|
|
*| I don't know how this is used, all I know is that it
|
|
*| appeared one day in main.c so I moved it here. -cim 11/1/89
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
TupleDesc
|
|
QueryDescGetTypeInfo(QueryDesc *queryDesc)
|
|
{
|
|
Plan *plan;
|
|
TupleDesc tupleType;
|
|
List *targetList;
|
|
AttrInfo *attinfo = (AttrInfo *) palloc(sizeof(AttrInfo));
|
|
|
|
plan = queryDesc->plantree;
|
|
tupleType = (TupleDesc) ExecGetTupType(plan);
|
|
/*
|
|
targetList = plan->targetlist;
|
|
|
|
attinfo->numAttr = ExecTargetListLength(targetList);
|
|
attinfo->attrs = tupleType;
|
|
*/
|
|
attinfo->numAttr = tupleType->natts;
|
|
attinfo->attrs = tupleType->attrs;
|
|
return attinfo;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInsertIndexTuples support
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
/* ----------------------------------------------------------------
|
|
* ExecGetIndexKeyInfo
|
|
*
|
|
* Extracts the index key attribute numbers from
|
|
* an index tuple form (i.e. a tuple from the pg_index relation)
|
|
* into an array of attribute numbers. The array and the
|
|
* size of the array are returned to the caller via return
|
|
* parameters.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static void
|
|
ExecGetIndexKeyInfo(Form_pg_index indexTuple,
|
|
int *numAttsOutP,
|
|
AttrNumber **attsOutP,
|
|
FuncIndexInfoPtr fInfoP)
|
|
{
|
|
int i;
|
|
int numKeys;
|
|
AttrNumber *attKeys;
|
|
|
|
/* ----------------
|
|
* check parameters
|
|
* ----------------
|
|
*/
|
|
if (numAttsOutP == NULL && attsOutP == NULL)
|
|
{
|
|
elog(DEBUG, "ExecGetIndexKeyInfo: %s",
|
|
"invalid parameters: numAttsOutP and attsOutP must be non-NULL");
|
|
}
|
|
|
|
/* ----------------
|
|
* set the procid for a possible functional index.
|
|
* ----------------
|
|
*/
|
|
FIsetProcOid(fInfoP, indexTuple->indproc);
|
|
|
|
/* ----------------
|
|
* count the number of keys..
|
|
* ----------------
|
|
*/
|
|
numKeys = 0;
|
|
for (i = 0; i < INDEX_MAX_KEYS &&
|
|
indexTuple->indkey[i] != InvalidAttrNumber; i++)
|
|
numKeys++;
|
|
|
|
/* ----------------
|
|
* place number keys in callers return area
|
|
* or the number of arguments for a functional index.
|
|
*
|
|
* If we have a functional index then the number of
|
|
* attributes defined in the index must 1 (the function's
|
|
* single return value).
|
|
* ----------------
|
|
*/
|
|
if (FIgetProcOid(fInfoP) != InvalidOid)
|
|
{
|
|
FIsetnArgs(fInfoP, numKeys);
|
|
(*numAttsOutP) = 1;
|
|
}
|
|
else
|
|
(*numAttsOutP) = numKeys;
|
|
|
|
if (numKeys < 1)
|
|
{
|
|
elog(DEBUG, "ExecGetIndexKeyInfo: %s",
|
|
"all index key attribute numbers are zero!");
|
|
(*attsOutP) = NULL;
|
|
return;
|
|
}
|
|
|
|
/* ----------------
|
|
* allocate and fill in array of key attribute numbers
|
|
* ----------------
|
|
*/
|
|
CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext);
|
|
|
|
attKeys = (AttrNumber *) palloc(numKeys * sizeof(AttrNumber));
|
|
|
|
for (i = 0; i < numKeys; i++)
|
|
attKeys[i] = indexTuple->indkey[i];
|
|
|
|
/* ----------------
|
|
* return array to caller.
|
|
* ----------------
|
|
*/
|
|
(*attsOutP) = attKeys;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecOpenIndices
|
|
*
|
|
* Here we scan the pg_index relation to find indices
|
|
* associated with a given heap relation oid. Since we
|
|
* don't know in advance how many indices we have, we
|
|
* form lists containing the information we need from
|
|
* pg_index and then process these lists.
|
|
*
|
|
* Note: much of this code duplicates effort done by
|
|
* the IndexCatalogInformation function in plancat.c
|
|
* because IndexCatalogInformation is poorly written.
|
|
*
|
|
* It would be much better if the functionality provided
|
|
* by this function and IndexCatalogInformation was
|
|
* in the form of a small set of orthogonal routines..
|
|
* If you are trying to understand this, I suggest you
|
|
* look at the code to IndexCatalogInformation and
|
|
* FormIndexTuple.. -cim 9/27/89
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecOpenIndices(Oid resultRelationOid,
|
|
RelationInfo *resultRelationInfo)
|
|
{
|
|
Relation indexRd;
|
|
HeapScanDesc indexSd;
|
|
ScanKeyData key;
|
|
HeapTuple tuple;
|
|
Form_pg_index indexStruct;
|
|
Oid indexOid;
|
|
List *oidList;
|
|
List *nkeyList;
|
|
List *keyList;
|
|
List *fiList;
|
|
char *predString;
|
|
List *predList;
|
|
List *indexoid;
|
|
List *numkeys;
|
|
List *indexkeys;
|
|
List *indexfuncs;
|
|
List *indexpreds;
|
|
int len;
|
|
|
|
RelationPtr relationDescs;
|
|
IndexInfo **indexInfoArray;
|
|
FuncIndexInfoPtr fInfoP;
|
|
int numKeyAtts;
|
|
AttrNumber *indexKeyAtts;
|
|
PredInfo *predicate;
|
|
int i;
|
|
|
|
/* ----------------
|
|
* open pg_index
|
|
* ----------------
|
|
*/
|
|
indexRd = heap_openr(IndexRelationName, AccessShareLock);
|
|
|
|
/* ----------------
|
|
* form a scan key
|
|
* ----------------
|
|
*/
|
|
ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid,
|
|
F_OIDEQ,
|
|
ObjectIdGetDatum(resultRelationOid));
|
|
|
|
/* ----------------
|
|
* scan the index relation, looking for indices for our
|
|
* result relation..
|
|
* ----------------
|
|
*/
|
|
indexSd = heap_beginscan(indexRd, /* scan desc */
|
|
false, /* scan backward flag */
|
|
SnapshotNow, /* NOW snapshot */
|
|
1, /* number scan keys */
|
|
&key); /* scan keys */
|
|
|
|
oidList = NIL;
|
|
nkeyList = NIL;
|
|
keyList = NIL;
|
|
fiList = NIL;
|
|
predList = NIL;
|
|
|
|
while (HeapTupleIsValid(tuple = heap_getnext(indexSd, 0)))
|
|
{
|
|
|
|
/* ----------------
|
|
* For each index relation we find, extract the information
|
|
* we need and store it in a list..
|
|
*
|
|
* first get the oid of the index relation from the tuple
|
|
* ----------------
|
|
*/
|
|
indexStruct = (Form_pg_index) GETSTRUCT(tuple);
|
|
indexOid = indexStruct->indexrelid;
|
|
|
|
/* ----------------
|
|
* allocate space for functional index information.
|
|
* ----------------
|
|
*/
|
|
fInfoP = (FuncIndexInfoPtr) palloc(sizeof(*fInfoP));
|
|
|
|
/* ----------------
|
|
* next get the index key information from the tuple
|
|
* ----------------
|
|
*/
|
|
ExecGetIndexKeyInfo(indexStruct,
|
|
&numKeyAtts,
|
|
&indexKeyAtts,
|
|
fInfoP);
|
|
|
|
/* ----------------
|
|
* next get the index predicate from the tuple
|
|
* ----------------
|
|
*/
|
|
if (VARSIZE(&indexStruct->indpred) != 0)
|
|
{
|
|
predString = fmgr(F_TEXTOUT, &indexStruct->indpred);
|
|
predicate = (PredInfo *) stringToNode(predString);
|
|
pfree(predString);
|
|
}
|
|
else
|
|
predicate = NULL;
|
|
|
|
/* ----------------
|
|
* save the index information into lists
|
|
* ----------------
|
|
*/
|
|
oidList = lconsi(indexOid, oidList);
|
|
nkeyList = lconsi(numKeyAtts, nkeyList);
|
|
keyList = lcons(indexKeyAtts, keyList);
|
|
fiList = lcons(fInfoP, fiList);
|
|
predList = lcons(predicate, predList);
|
|
}
|
|
|
|
/* ----------------
|
|
* we have the info we need so close the pg_index relation..
|
|
* ----------------
|
|
*/
|
|
heap_endscan(indexSd);
|
|
heap_close(indexRd, AccessShareLock);
|
|
|
|
/* ----------------
|
|
* Now that we've collected the index information into three
|
|
* lists, we open the index relations and store the descriptors
|
|
* and the key information into arrays.
|
|
* ----------------
|
|
*/
|
|
len = length(oidList);
|
|
if (len > 0)
|
|
{
|
|
/* ----------------
|
|
* allocate space for relation descs
|
|
* ----------------
|
|
*/
|
|
CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
|
|
relationDescs = (RelationPtr)
|
|
palloc(len * sizeof(Relation));
|
|
|
|
/* ----------------
|
|
* initialize index info array
|
|
* ----------------
|
|
*/
|
|
CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
|
|
indexInfoArray = (IndexInfo **)
|
|
palloc(len * sizeof(IndexInfo *));
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
IndexInfo *ii = makeNode(IndexInfo);
|
|
|
|
ii->ii_NumKeyAttributes = 0;
|
|
ii->ii_KeyAttributeNumbers = (AttrNumber *) NULL;
|
|
ii->ii_FuncIndexInfo = (FuncIndexInfoPtr) NULL;
|
|
ii->ii_Predicate = NULL;
|
|
indexInfoArray[i] = ii;
|
|
}
|
|
|
|
/* ----------------
|
|
* attempt to open each of the indices. If we succeed,
|
|
* then store the index relation descriptor into the
|
|
* relation descriptor array.
|
|
* ----------------
|
|
*/
|
|
i = 0;
|
|
foreach(indexoid, oidList)
|
|
{
|
|
Relation indexDesc;
|
|
|
|
indexOid = lfirsti(indexoid);
|
|
indexDesc = index_open(indexOid);
|
|
if (indexDesc != NULL)
|
|
{
|
|
relationDescs[i++] = indexDesc;
|
|
|
|
/*
|
|
* Hack for not btree and hash indices: they use relation
|
|
* level exclusive locking on update (i.e. - they are
|
|
* not ready for MVCC) and so we have to exclusively lock
|
|
* indices here to prevent deadlocks if we will scan them
|
|
* - index_beginscan places AccessShareLock, indices
|
|
* update methods don't use locks at all. We release this
|
|
* lock in ExecCloseIndices. Note, that hashes use page
|
|
* level locking - i.e. are not deadlock-free, - let's
|
|
* them be on their way -:)) vadim 03-12-1998
|
|
*/
|
|
if (indexDesc->rd_rel->relam != BTREE_AM_OID &&
|
|
indexDesc->rd_rel->relam != HASH_AM_OID)
|
|
LockRelation(indexDesc, AccessExclusiveLock);
|
|
}
|
|
}
|
|
|
|
/* ----------------
|
|
* store the relation descriptor array and number of
|
|
* descs into the result relation info.
|
|
* ----------------
|
|
*/
|
|
resultRelationInfo->ri_NumIndices = i;
|
|
resultRelationInfo->ri_IndexRelationDescs = relationDescs;
|
|
|
|
/* ----------------
|
|
* store the index key information collected in our
|
|
* lists into the index info array
|
|
* ----------------
|
|
*/
|
|
i = 0;
|
|
foreach(numkeys, nkeyList)
|
|
{
|
|
numKeyAtts = lfirsti(numkeys);
|
|
indexInfoArray[i++]->ii_NumKeyAttributes = numKeyAtts;
|
|
}
|
|
|
|
i = 0;
|
|
foreach(indexkeys, keyList)
|
|
{
|
|
indexKeyAtts = (AttrNumber *) lfirst(indexkeys);
|
|
indexInfoArray[i++]->ii_KeyAttributeNumbers = indexKeyAtts;
|
|
}
|
|
|
|
i = 0;
|
|
foreach(indexfuncs, fiList)
|
|
{
|
|
FuncIndexInfoPtr fiP = (FuncIndexInfoPtr) lfirst(indexfuncs);
|
|
|
|
indexInfoArray[i++]->ii_FuncIndexInfo = fiP;
|
|
}
|
|
|
|
i = 0;
|
|
foreach(indexpreds, predList)
|
|
indexInfoArray[i++]->ii_Predicate = lfirst(indexpreds);
|
|
/* ----------------
|
|
* store the index info array into relation info
|
|
* ----------------
|
|
*/
|
|
resultRelationInfo->ri_IndexRelationInfo = indexInfoArray;
|
|
}
|
|
|
|
/* ----------------
|
|
* All done, resultRelationInfo now contains complete information
|
|
* on the indices associated with the result relation.
|
|
* ----------------
|
|
*/
|
|
|
|
/* should free oidList, nkeyList and keyList here */
|
|
/* OK - let's do it -jolly */
|
|
freeList(oidList);
|
|
freeList(nkeyList);
|
|
freeList(keyList);
|
|
freeList(fiList);
|
|
freeList(predList);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecCloseIndices
|
|
*
|
|
* Close the index relations stored in resultRelationInfo
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecCloseIndices(RelationInfo *resultRelationInfo)
|
|
{
|
|
int i;
|
|
int numIndices;
|
|
RelationPtr relationDescs;
|
|
|
|
numIndices = resultRelationInfo->ri_NumIndices;
|
|
relationDescs = resultRelationInfo->ri_IndexRelationDescs;
|
|
|
|
for (i = 0; i < numIndices; i++)
|
|
{
|
|
if (relationDescs[i] == NULL)
|
|
continue;
|
|
|
|
/*
|
|
* See notes in ExecOpenIndices.
|
|
*/
|
|
if (relationDescs[i]->rd_rel->relam != BTREE_AM_OID &&
|
|
relationDescs[i]->rd_rel->relam != HASH_AM_OID)
|
|
UnlockRelation(relationDescs[i], AccessExclusiveLock);
|
|
|
|
index_close(relationDescs[i]);
|
|
}
|
|
|
|
/*
|
|
* XXX should free indexInfo array here too.
|
|
*/
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecFormIndexTuple
|
|
*
|
|
* Most of this code is cannabilized from DefaultBuild().
|
|
* As said in the comments for ExecOpenIndices, most of
|
|
* this functionality should be rearranged into a proper
|
|
* set of routines..
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
#ifdef NOT_USED
|
|
IndexTuple
|
|
ExecFormIndexTuple(HeapTuple heapTuple,
|
|
Relation heapRelation,
|
|
Relation indexRelation,
|
|
IndexInfo *indexInfo)
|
|
{
|
|
IndexTuple indexTuple;
|
|
TupleDesc heapDescriptor;
|
|
TupleDesc indexDescriptor;
|
|
Datum *datum;
|
|
char *nulls;
|
|
|
|
int numberOfAttributes;
|
|
AttrNumber *keyAttributeNumbers;
|
|
FuncIndexInfoPtr fInfoP;
|
|
|
|
/* ----------------
|
|
* get information from index info structure
|
|
* ----------------
|
|
*/
|
|
numberOfAttributes = indexInfo->ii_NumKeyAttributes;
|
|
keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers;
|
|
fInfoP = indexInfo->ii_FuncIndexInfo;
|
|
|
|
/* ----------------
|
|
* datum and null are arrays in which we collect the index attributes
|
|
* when forming a new index tuple.
|
|
* ----------------
|
|
*/
|
|
CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext);
|
|
datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
|
|
nulls = (char *) palloc(numberOfAttributes * sizeof *nulls);
|
|
|
|
/* ----------------
|
|
* get the tuple descriptors from the relations so we know
|
|
* how to form the index tuples..
|
|
* ----------------
|
|
*/
|
|
heapDescriptor = RelationGetDescr(heapRelation);
|
|
indexDescriptor = RelationGetDescr(indexRelation);
|
|
|
|
/* ----------------
|
|
* FormIndexDatum fills in its datum and null parameters
|
|
* with attribute information taken from the given heap tuple.
|
|
* ----------------
|
|
*/
|
|
FormIndexDatum(numberOfAttributes, /* num attributes */
|
|
keyAttributeNumbers, /* array of att nums to extract */
|
|
heapTuple, /* tuple from base relation */
|
|
heapDescriptor, /* heap tuple's descriptor */
|
|
datum, /* return: array of attributes */
|
|
nulls, /* return: array of char's */
|
|
fInfoP); /* functional index information */
|
|
|
|
indexTuple = index_formtuple(indexDescriptor,
|
|
datum,
|
|
nulls);
|
|
|
|
/* ----------------
|
|
* free temporary arrays
|
|
*
|
|
* XXX should store these in the IndexInfo instead of allocating
|
|
* and freeing on every insertion, but efficency here is not
|
|
* that important and FormIndexTuple is wasteful anyways..
|
|
* -cim 9/27/89
|
|
* ----------------
|
|
*/
|
|
pfree(nulls);
|
|
pfree(datum);
|
|
|
|
return indexTuple;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInsertIndexTuples
|
|
*
|
|
* This routine takes care of inserting index tuples
|
|
* into all the relations indexing the result relation
|
|
* when a heap tuple is inserted into the result relation.
|
|
* Much of this code should be moved into the genam
|
|
* stuff as it only exists here because the genam stuff
|
|
* doesn't provide the functionality needed by the
|
|
* executor.. -cim 9/27/89
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecInsertIndexTuples(TupleTableSlot *slot,
|
|
ItemPointer tupleid,
|
|
EState *estate,
|
|
bool is_update)
|
|
{
|
|
HeapTuple heapTuple;
|
|
RelationInfo *resultRelationInfo;
|
|
int i;
|
|
int numIndices;
|
|
RelationPtr relationDescs;
|
|
Relation heapRelation;
|
|
IndexInfo **indexInfoArray;
|
|
IndexInfo *indexInfo;
|
|
Node *predicate;
|
|
ExprContext *econtext;
|
|
InsertIndexResult result;
|
|
int numberOfAttributes;
|
|
AttrNumber *keyAttributeNumbers;
|
|
FuncIndexInfoPtr fInfoP;
|
|
TupleDesc heapDescriptor;
|
|
Datum *datum;
|
|
char *nulls;
|
|
|
|
heapTuple = slot->val;
|
|
|
|
/* ----------------
|
|
* get information from the result relation info structure.
|
|
* ----------------
|
|
*/
|
|
resultRelationInfo = estate->es_result_relation_info;
|
|
numIndices = resultRelationInfo->ri_NumIndices;
|
|
relationDescs = resultRelationInfo->ri_IndexRelationDescs;
|
|
indexInfoArray = resultRelationInfo->ri_IndexRelationInfo;
|
|
heapRelation = resultRelationInfo->ri_RelationDesc;
|
|
|
|
/* ----------------
|
|
* for each index, form and insert the index tuple
|
|
* ----------------
|
|
*/
|
|
econtext = NULL;
|
|
for (i = 0; i < numIndices; i++)
|
|
{
|
|
if (relationDescs[i] == NULL)
|
|
continue;
|
|
|
|
indexInfo = indexInfoArray[i];
|
|
predicate = indexInfo->ii_Predicate;
|
|
if (predicate != NULL)
|
|
{
|
|
if (econtext == NULL)
|
|
econtext = makeNode(ExprContext);
|
|
econtext->ecxt_scantuple = slot;
|
|
|
|
/* Skip this index-update if the predicate isn't satisfied */
|
|
if (! ExecQual((List *) predicate, econtext, false))
|
|
continue;
|
|
}
|
|
|
|
/* ----------------
|
|
* get information from index info structure
|
|
* ----------------
|
|
*/
|
|
numberOfAttributes = indexInfo->ii_NumKeyAttributes;
|
|
keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers;
|
|
fInfoP = indexInfo->ii_FuncIndexInfo;
|
|
datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
|
|
nulls = (char *) palloc(numberOfAttributes * sizeof *nulls);
|
|
heapDescriptor = (TupleDesc) RelationGetDescr(heapRelation);
|
|
|
|
FormIndexDatum(numberOfAttributes, /* num attributes */
|
|
keyAttributeNumbers, /* array of att nums to
|
|
* extract */
|
|
heapTuple, /* tuple from base relation */
|
|
heapDescriptor, /* heap tuple's descriptor */
|
|
datum, /* return: array of attributes */
|
|
nulls, /* return: array of char's */
|
|
fInfoP); /* functional index information */
|
|
|
|
|
|
result = index_insert(relationDescs[i], /* index relation */
|
|
datum, /* array of heaptuple Datums */
|
|
nulls, /* info on nulls */
|
|
&(heapTuple->t_self), /* tid of heap tuple */
|
|
heapRelation);
|
|
|
|
/* ----------------
|
|
* keep track of index inserts for debugging
|
|
* ----------------
|
|
*/
|
|
IncrIndexInserted();
|
|
|
|
/* ----------------
|
|
* free index tuple after insertion
|
|
* ----------------
|
|
*/
|
|
if (result)
|
|
pfree(result);
|
|
}
|
|
if (econtext != NULL)
|
|
pfree(econtext);
|
|
}
|
|
|
|
void
|
|
SetChangedParamList(Plan *node, List *newchg)
|
|
{
|
|
List *nl;
|
|
|
|
foreach(nl, newchg)
|
|
{
|
|
int paramId = lfirsti(nl);
|
|
|
|
/* if this node doesn't depend on a param ... */
|
|
if (!intMember(paramId, node->extParam) &&
|
|
!intMember(paramId, node->locParam))
|
|
continue;
|
|
/* if this param is already in list of changed ones ... */
|
|
if (intMember(paramId, node->chgParam))
|
|
continue;
|
|
/* else - add this param to the list */
|
|
node->chgParam = lappendi(node->chgParam, paramId);
|
|
}
|
|
|
|
}
|