greenplumn predtest_valueset 源码

  • 2022-08-18
  • 浏览 (220)

greenplumn predtest_valueset 代码

文件路径:/src/backend/optimizer/util/predtest_valueset.c

/*-------------------------------------------------------------------------
 *
 * predtest_valueset.c
 *	  Routines to prove possible values of variable based on predicates.
 *
 * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/util/predtest_valueset.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/hash.h"
#include "catalog/pg_type.h"
#include "optimizer/clauses.h"
#include "optimizer/predtest_valueset.h"
#include "utils/lsyscache.h"
#include "nodes/makefuncs.h"
#include "utils/datum.h"


#define INT16MAX (32767)
#define INT16MIN (-32768)
#define INT32MAX (2147483647)
#define INT32MIN (-2147483648)

static HTAB *CreateNodeSetHashTable();
static void AddValue(PossibleValueSet *pvs, Const *valueToCopy);
static void RemoveValue(PossibleValueSet *pvs, Const *value);
static bool ContainsValue(PossibleValueSet *pvs, Const *value);

static bool TryProcessOpExprForPossibleValues(OpExpr *expr, Node *variable, PossibleValueSet *resultOut);
static bool TryProcessNullTestForPossibleValues(NullTest *expr, Node *variable, PossibleValueSet *resultOut);

typedef struct ConstHashValue
{
	Const * c;
} ConstHashValue;

/*
 * Compute a hash value for Const.
 *
 * We use equal() to check for equality, which just comparse the raw bytes.
 * Therefore, we don't need datatype-aware hashing either, we just hash the
 * raw bytes.
 */
static uint32
ConstHashTableHash(const void *keyPtr, Size keysize)
{
	Const	   *c = *((Const **) keyPtr);

	if (c->constisnull)
		return 0;
	else if (c->constbyval)
	{
		/*
		 * Like in datumIsEqual, we use all bits of the Datum, even if the
		 * length is smaller. This assumes that the datatype is consistent
		 * about how it fills the extra bits.
		 */
		return hash_any((unsigned char *) &c->constvalue, sizeof(Datum));
	}
	else
	{
		Size		realSize;

		realSize = datumGetSize(c->constvalue, c->constbyval, c->constlen);

		return hash_any((unsigned char *) DatumGetPointer(c->constvalue), realSize);
	}
}

static int
ConstHashTableMatch(const void*keyPtr1, const void *keyPtr2, Size keysize)
{
	Node	   *left = *((Node **) keyPtr1);
	Node	   *right = *((Node **) keyPtr2);

	return equal(left, right) ? 0 : 1;
}

/**
 * returns a hashtable that can be used to map from a node to itself
 */
static HTAB*
CreateNodeSetHashTable(MemoryContext memoryContext)
{
	HASHCTL	hash_ctl;

	MemSet(&hash_ctl, 0, sizeof(hash_ctl));

	hash_ctl.keysize = sizeof(Const **);
	hash_ctl.entrysize = sizeof(ConstHashValue);
	hash_ctl.hash = ConstHashTableHash;
	hash_ctl.match = ConstHashTableMatch;
	hash_ctl.hcxt = memoryContext;

	return hash_create("ConstantSet", 16, &hash_ctl,
					   HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);
}

/**
 * basic operation on PossibleValueSet:  initialize to "any value possible"
 */
void
InitPossibleValueSetData(PossibleValueSet *pvs)
{
	pvs->memoryContext = NULL;
	pvs->set = NULL;
	pvs->isAnyValuePossible = true;
}

/**
 * Take the values from the given PossibleValueSet and return them as an allocated array.
 *
 * @param pvs the set to turn into an array
 * @param numValuesOut receives the length of the returned array
 * @return the array of Node objects
 */
Node **
GetPossibleValuesAsArray(PossibleValueSet *pvs, int *numValuesOut)
{
	HASH_SEQ_STATUS status;
	ConstHashValue *value;
	List	   *list = NIL;
	Node	  **result;
	int			numValues,
				i;
	ListCell   *lc;

	if (pvs->set == NULL)
	{
		*numValuesOut = 0;
		return NULL;
	}

	hash_seq_init(&status, pvs->set);
	while ((value = (ConstHashValue *) hash_seq_search(&status)) != NULL)
	{
		list = lappend(list, copyObject(value->c));
	}

	numValues = list_length(list);
	result = palloc(sizeof(Node *) * numValues);
	foreach_with_count(lc, list, i)
	{
		result[i] = (Node *) lfirst(lc);
	}

	*numValuesOut = numValues;
	return result;
}

/**
 * basic operation on PossibleValueSet:  cleanup
 */
void
DeletePossibleValueSetData(PossibleValueSet *pvs)
{
	if (pvs->set != NULL)
	{
		Assert(pvs->memoryContext != NULL);

		MemoryContextDelete(pvs->memoryContext);
		pvs->memoryContext = NULL;
		pvs->set = NULL;
	}
	pvs->isAnyValuePossible = true;
}

/**
 * basic operation on PossibleValueSet:  add a value to the set field of PossibleValueSet
 *
 * The caller must verify that the valueToCopy is greenplum hashable
 */
static void
AddValue(PossibleValueSet *pvs, Const *valueToCopy)
{
	if (pvs->set == NULL)
	{
		Assert(pvs->memoryContext == NULL);

		pvs->memoryContext = AllocSetContextCreate(CurrentMemoryContext,
												   "PossibleValueSet",
												   ALLOCSET_DEFAULT_MINSIZE,
												   ALLOCSET_DEFAULT_INITSIZE,
												   ALLOCSET_DEFAULT_MAXSIZE);
		pvs->set = CreateNodeSetHashTable(pvs->memoryContext);
	}

	if (!ContainsValue(pvs, valueToCopy))
	{
		bool		found; /* unused but needed in call */
		MemoryContext oldContext = MemoryContextSwitchTo(pvs->memoryContext);

		Const	   *key = copyObject(valueToCopy);
		ConstHashValue *entry = hash_search(pvs->set, &key, HASH_ENTER, &found);

		entry->c = key;

		MemoryContextSwitchTo(oldContext);
	}
}

static void
SetToNoValuesPossible(PossibleValueSet *pvs)
{
	if (pvs->memoryContext)
	{
		MemoryContextDelete(pvs->memoryContext);
	}
	pvs->memoryContext = AllocSetContextCreate(CurrentMemoryContext,
												   "PossibleValueSet",
												   ALLOCSET_DEFAULT_MINSIZE,
												   ALLOCSET_DEFAULT_INITSIZE,
												   ALLOCSET_DEFAULT_MAXSIZE);
	pvs->set = CreateNodeSetHashTable(pvs->memoryContext);
	pvs->isAnyValuePossible = false;
}

/**
 * basic operation on PossibleValueSet:  remove a value from the set field of PossibleValueSet
 */
static void
RemoveValue(PossibleValueSet *pvs, Const *value)
{
	bool		found; /* unused, needed in call */

	Assert( pvs->set != NULL);
	hash_search(pvs->set, &value, HASH_REMOVE, &found);
}

/**
 * basic operation on PossibleValueSet:  determine if a value is contained in the set field of PossibleValueSet
 */
static bool
ContainsValue(PossibleValueSet *pvs, Const *value)
{
	bool		found = false;

	Assert(!pvs->isAnyValuePossible);
	if (pvs->set != NULL)
		hash_search(pvs->set, &value, HASH_FIND, &found);
	return found;
}

/**
 * in-place union operation
 */
void
AddUnmatchingValues( PossibleValueSet *pvs, PossibleValueSet *toCheck )
{
	HASH_SEQ_STATUS status;
	ConstHashValue *value;

	Assert(!pvs->isAnyValuePossible);
	Assert(!toCheck->isAnyValuePossible);

	hash_seq_init(&status, toCheck->set);
	while ((value = (ConstHashValue *) hash_seq_search(&status)) != NULL)
	{
		AddValue(pvs, value->c);
	}
}

/**
 * in-place intersection operation
 */
void
RemoveUnmatchingValues(PossibleValueSet *pvs, PossibleValueSet *toCheck)
{
	List	   *toRemove = NIL;
	ListCell   *lc;
	HASH_SEQ_STATUS status;
	ConstHashValue *value;

	Assert(!pvs->isAnyValuePossible);
	Assert(!toCheck->isAnyValuePossible);

	hash_seq_init(&status, pvs->set);
	while ((value = (ConstHashValue*) hash_seq_search(&status)) != NULL)
	{
		if (!ContainsValue(toCheck, value->c))
		{
			toRemove = lappend(toRemove, value->c);
		}
	}

	/* remove after so we don't mod hashtable underneath iteration */
	foreach(lc, toRemove)
	{
		Const	   *value = (Const*) lfirst(lc);

		RemoveValue(pvs, value);
	}
	list_free(toRemove);
}

/**
 * Check to see if the given expression is a valid equality between the listed variable and a constant.
 *
 * @param expr the expression to check for being a valid quality
 * @param variable the variable to look for
 * @param resultOut will be updated with the modified values
 */
bool
TryProcessExprForPossibleValues(Node *expr, Node *variable, PossibleValueSet *resultOut)
{
	if (IsA(expr, OpExpr))
	{
		return TryProcessOpExprForPossibleValues((OpExpr *) expr, variable, resultOut);
	}
	else if (IsA(expr, NullTest))
	{
		return TryProcessNullTestForPossibleValues((NullTest *) expr, variable, resultOut);
	}
	else
		return false;
}

static bool
TryProcessOpExprForPossibleValues(OpExpr *expr, Node *variable, PossibleValueSet *resultOut)
{
	Node	   *leftop,
			   *rightop,
			   *varExpr;
	Const	   *constExpr;
	bool		constOnRight;
	List	   *clause_op_infos;
	ListCell   *lc;

	InitPossibleValueSetData(resultOut);

	leftop = get_leftop((Expr *) expr);
	rightop = get_rightop((Expr *) expr);
	if (!leftop || !rightop)
		return false;

	/* check if one operand is a constant */
	if (IsA(rightop, Const))
	{
		varExpr = leftop;
		constExpr = (Const *) rightop;
		constOnRight = true;
	}
	else if (IsA(leftop, Const))
	{
		constExpr = (Const *) leftop;
		varExpr = rightop;
		constOnRight = false;
	}
	else
	{
		/** not a constant?  Learned nothing */
		return false;
	}

	if (constExpr->constisnull)
	{
		/* null doesn't help us */
		return false;
	}

	if (IsA(varExpr, RelabelType))
	{
		RelabelType *rt = (RelabelType *) varExpr;

		varExpr = (Node *) rt->arg;
	}

	if (!equal(varExpr, variable))
	{
		/**
		 * Not talking about our variable?  Learned nothing
		 */
		return false;
	}

	clause_op_infos = get_op_btree_interpretation(expr->opno);

	/* check if it's equality operation */
	bool		is_equality = false;
	OpBtreeInterpretation *clause_op_info = NULL;

	foreach (lc, clause_op_infos)
	{
		clause_op_info = lfirst(lc);

		if (clause_op_info->strategy == BTEqualStrategyNumber)
		{
			is_equality = true;
			break;
		}
	}

	if (!is_equality)
		return false;

	if (clause_op_info->oplefttype == clause_op_info->oprighttype)
	{
		/*
		 * Both sides have the same datatype. This case is simple, we can
		 * add the constant directly as a possible value.
		 */
		resultOut->isAnyValuePossible = false;
		AddValue(resultOut, constExpr);
		return true;
	}
	else
	{
		/*
		 * Cross-datatype equality. this is more complicated, we need to
		 * construct a const with constant's value, but using the variable
		 * side's datatype. it would be tempting to cast the value, but that
		 * fails e.g. if the constant is an int4, with a large value, and
		 * the variable is int2. there will be no match in that case, but
		 * if we tried to simply cast the value, we'd get an error. So we
		 * have hard-coded knowledge of the few simple cases, namely
		 * differently-sized integer types
		 *
		 * Expand the constant to int64, then assign it back to a Datum
		 * of the variable's datatype, checking for overflow.
		 */
		Oid			const_type;
		Oid			var_type;
		int64		constvalue;

		bool		isOverflow;
		Oid			new_const_type;
		int32		new_const_len;
		Datum		new_const_datum;

		if (constOnRight)
		{
			const_type = clause_op_info->oprighttype;
			var_type = clause_op_info->oplefttype;
		}
		else
		{
			const_type = clause_op_info->oplefttype;
			var_type = clause_op_info->oprighttype;
		}

		switch (const_type)
		{
			case INT2OID:
				constvalue = (int64) DatumGetInt16(constExpr->constvalue);
				break;
			case INT4OID:
				constvalue = (int64) DatumGetInt32(constExpr->constvalue);
				break;
			case INT8OID:
				constvalue = DatumGetInt64(constExpr->constvalue);
				break;
			default:
				return false; /* we only know how to handle integer types */
		}

		/* Assign to the target type, checking for overflow */
		isOverflow = false;
		switch (var_type)
		{
			case INT2OID:
				if (constvalue > INT16MAX || constvalue < INT16MIN)
					isOverflow = true;
				new_const_type = INT2OID;
				new_const_len = sizeof(int16);
				new_const_datum = Int16GetDatum((int16) constvalue);
				break;
			case INT4OID:
				if (constvalue > INT32MAX || constvalue < INT32MIN)
					isOverflow = true;
				new_const_type = INT4OID;
				new_const_len = sizeof(int32);
				new_const_datum = Int32GetDatum((int32) constvalue);
				break;
			case INT8OID:
				new_const_type = INT8OID;
				new_const_len = sizeof(int64);
				new_const_datum = Int64GetDatum(constvalue);
				break;

			default:
				return false; /* we only know how to handle integer types */
		}

		if (isOverflow)
		{
			SetToNoValuesPossible(resultOut);
		}
		else
		{
			/* okay, got a new constant value .. set it and done! */
			Const	   *newConst;

			newConst = makeConst(new_const_type,
								 /* consttypmod */ 0,
								 /* constcollid */ InvalidOid,
								 new_const_len,
								 new_const_datum,
								 /* constisnull */ false,
								 /* constbyval */ true);

			resultOut->isAnyValuePossible = false;
			AddValue(resultOut, newConst);

			pfree(newConst);
		}
		return true;
	}
}

static bool
TryProcessNullTestForPossibleValues(NullTest *expr, Node *variable, PossibleValueSet *resultOut)
{
	Node	   *varExpr;

	InitPossibleValueSetData(resultOut);

	varExpr = (Node *) expr->arg;

	if (IsA(varExpr, RelabelType))
	{
		RelabelType *rt = (RelabelType *) varExpr;

		varExpr = (Node *) rt->arg;
	}

	if (!equal(varExpr, variable))
	{
		/**
		 * Not talking about our variable?  Learned nothing
		 */
		return false;
	}

	if (expr->nulltesttype == IS_NULL)
	{
		resultOut->isAnyValuePossible = false;
		AddValue(resultOut, makeNullConst(exprType(varExpr),
										  -1,
										  exprCollation(varExpr)));
		return true;
	}
	else
	{
		/* can't do anything with IS NOT NULL */
		return false;
	}
}

相关信息

greenplumn 源码目录

相关文章

greenplumn appendinfo 源码

greenplumn clauses 源码

greenplumn inherit 源码

greenplumn joininfo 源码

greenplumn orclauses 源码

greenplumn paramassign 源码

greenplumn pathnode 源码

greenplumn placeholder 源码

greenplumn plancat 源码

greenplumn predtest 源码

0  赞