blob: c9ccf6751562319e2b2ffcd6b71cfbb44bc5ad76 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "postgres.h"
#include "utils/builtins.h"
#include "execVQual.h"
#include "cdb/cdbhash.h"
static bool
ExecVTargetList(List *targetlist,
ExprContext *econtext,
TupleTableSlot *slot,
ExprDoneCond *itemIsDone,
ExprDoneCond *isDone);
/*
* ExecVariableList
* Evaluates a simple-Variable-list projection.
*
* Results are stored into the passed values and isnull arrays.
*/
static void
ExecVecVariableList(ProjectionInfo *projInfo,
Datum values)
{
ExprContext *econtext = projInfo->pi_exprContext;
int *varSlotOffsets = projInfo->pi_varSlotOffsets;
int *varNumbers = projInfo->pi_varNumbers;
TupleBatch tb = (TupleBatch) DatumGetPointer(values);
int i;
tb->ncols = list_length(projInfo->pi_targetlist);
/*
* Assign to result by direct extraction of fields from source slots ... a
* mite ugly, but fast ...
*/
for (i = list_length(projInfo->pi_targetlist) - 1; i >= 0; i--)
{
char *slotptr = ((char *) econtext) + varSlotOffsets[i];
TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
int varNumber = varNumbers[i] - 1;
tb->datagroup[i] = ((TupleBatch)varSlot->PRIVATE_tb)->datagroup[varNumber];
}
}
TupleTableSlot *
ExecVProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
{
TupleTableSlot *slot;
Assert(projInfo != NULL);
/*
* get the projection info we want
*/
slot = projInfo->pi_slot;
/*
* Clear any former contents of the result slot. This makes it safe for
* us to use the slot's Datum/isnull arrays as workspace. (Also, we can
* return the slot as-is if we decide no rows can be projected.)
*/
ExecClearTuple(slot);
/*
* form a new result tuple (if possible); if successful, mark the result
* slot as containing a valid virtual tuple
*/
if (projInfo->pi_isVarList)
{
/* simple Var list: this always succeeds with one result row */
if (isDone)
*isDone = ExprSingleResult;
ExecVecVariableList(projInfo,slot->PRIVATE_tb);
ExecStoreVirtualTuple(slot);
}
else
{
if (ExecVTargetList(projInfo->pi_targetlist,
projInfo->pi_exprContext,
slot,
(ExprDoneCond *) projInfo->pi_itemIsDone,
isDone))
ExecStoreVirtualTuple(slot);
}
return slot;
}
/*
* VirtualNodeProc
* return value indicate whether has a tuple data fill in slot->PRIVATE_tts_values slot
* This function will be invoked in V->N process.
*/
bool
VirtualNodeProc(TupleTableSlot *slot){
if(TupIsNull(slot) )
return false;
TupleBatch tb = (TupleBatch)DatumGetPointer(slot->PRIVATE_tb);
ExecClearTuple(slot);
while (tb->skip[tb->iter] && tb->iter < tb->nrows)
tb->iter++;
if(tb->iter == tb->nrows)
return false;
for(int i = 0;i < tb->ncols;i ++)
{
vtype *vt = tb->datagroup[i];
slot->PRIVATE_tts_values[i] = vt->values[tb->iter];
slot->PRIVATE_tts_isnull[i] = vt->isnull[tb->iter];
}
tb->iter ++;
ExecStoreVirtualTuple(slot);
return true;
}
/*
* get values from vectorized tuple slot
* copy from src/backend/executor/execQual.c
*/
static Datum
VExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
Var *variable = (Var *) exprstate->expr;
TupleTableSlot *slot;
AttrNumber attnum;
TupleBatch tb;
if (isDone)
*isDone = ExprSingleResult;
Assert(econtext->ecxt_scantuple != NULL || econtext->ecxt_innertuple != NULL || econtext->ecxt_outertuple != NULL);
/*
* Get the input slot and attribute number we want
*
* The asserts check that references to system attributes only appear at
* the level of a relation scan; at higher levels, system attributes must
* be treated as ordinary variables (since we no longer have access to the
* original tuple).
*/
attnum = variable->varattno;
switch (variable->varno)
{
case INNER: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple;
Assert(attnum > 0);
break;
case OUTER: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple;
Assert(attnum > 0);
break;
default: /* get the tuple from the relation being
* scanned */
slot = econtext->ecxt_scantuple;
break;
}
/* isNull is a single value, it can not be used when data is vectorized */
*isNull = false;
/* Fetch the value from the slot */
Assert(NULL != slot);
tb = (TupleBatch )slot->PRIVATE_tb;
Assert(NULL != tb);
return PointerGetDatum(tb->datagroup[attnum - 1]);
}
/*
* get values from vectorized tuple slot
* copy from src/backend/executor/execQual.c
*/
static Datum
VExecEvalVar(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
Var *variable = (Var *) exprstate->expr;
TupleTableSlot *slot;
AttrNumber attnum;
if (isDone)
*isDone = ExprSingleResult;
Assert(econtext->ecxt_scantuple != NULL || econtext->ecxt_innertuple != NULL || econtext->ecxt_outertuple != NULL);
/*
* Get the input slot and attribute number we want
*
* The asserts check that references to system attributes only appear at
* the level of a relation scan; at higher levels, system attributes must
* be treated as ordinary variables (since we no longer have access to the
* original tuple).
*/
attnum = variable->varattno;
switch (variable->varno)
{
case INNER: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple;
Assert(attnum > 0);
break;
case OUTER: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple;
Assert(attnum > 0);
break;
default: /* get the tuple from the relation being
* scanned */
slot = econtext->ecxt_scantuple;
break;
}
Assert(NULL != slot);
if (attnum != InvalidAttrNumber)
{
TupleBatch tb;
/*
* Scalar variable case.
*
* If it's a user attribute, check validity (bogus system attnums will
* be caught inside slot_getattr). What we have to check for here
* is the possibility of an attribute having been changed in type
* since the plan tree was created. Ideally the plan would get
* invalidated and not re-used, but until that day arrives, we need
* defenses. Fortunately it's sufficient to check once on the first
* time through.
*
* Note: we allow a reference to a dropped attribute. slot_getattr
* will force a NULL result in such cases.
*
* Note: ideally we'd check typmod as well as typid, but that seems
* impractical at the moment: in many cases the tupdesc will have
* been generated by ExecTypeFromTL(), and that can't guarantee to
* generate an accurate typmod in all cases, because some expression
* node types don't carry typmod.
*/
if (attnum > 0)
{
TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
Form_pg_attribute attr;
if (attnum > slot_tupdesc->natts) /* should never happen */
elog(ERROR, "attribute number %d exceeds number of columns %d",
attnum, slot_tupdesc->natts);
attr = slot_tupdesc->attrs[attnum - 1];
/* can't check type if dropped, since atttypid is probably 0 */
if (!attr->attisdropped)
{
if (variable->vartype != attr->atttypid &&
GetNtype(variable->vartype) != attr->atttypid)
ereport(ERROR,
(errmsg("attribute %d has wrong type", attnum),
errdetail("Table has type %s, but query expects %s.",
format_type_be(attr->atttypid),
format_type_be(variable->vartype))));
}
}
/* Skip the checking on future executions of node */
exprstate->evalfunc = VExecEvalScalarVar;
/* isNull is a single value, it can not be used when data is vectorized */
*isNull = false;
/* Fetch the value from the slot */
tb = (TupleBatch )slot->PRIVATE_tb;
Assert(NULL != tb);
return PointerGetDatum(tb->datagroup[attnum - 1]);
}
else
{
/* NOT support so far */
Assert(false);
}
return PointerGetDatum(NULL);
}
/* ----------------------------------------------------------------
* VExecEvalNot
* VExecEvalOr
* VExecEvalAnd
*
* copy from src/backend/executor/execQual.c
*
* Evaluate boolean expressions, with appropriate short-circuiting.
*
* The query planner reformulates clause expressions in the
* qualification to conjunctive normal form. If we ever get
* an AND to evaluate, we can be sure that it's not a top-level
* clause in the qualification, but appears lower (as a function
* argument, for example), or in the target list. Not that you
* need to know this, mind you...
* ----------------------------------------------------------------
*/
static Datum
VExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
ExprState *clause = linitial(notclause->args);
Datum expr_value;
vbool *ret;
int i;
if (isDone)
*isDone = ExprSingleResult;
expr_value = ExecEvalExpr(clause, econtext, isNull, NULL);
ret = (vbool*)DatumGetPointer(expr_value);
for(i = 0; i < ret->dim; i++)
{
if(!ret->isnull[i])
ret->values[i] = !ret->values[i];
}
/*
* evaluation of 'not' is simple.. expr is false, then return 'true' and
* vice versa.
*/
return PointerGetDatum(ret);
}
/* ----------------------------------------------------------------
* ExecEvalOr
* ----------------------------------------------------------------
*/
static Datum
VExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
List *clauses = orExpr->args;
ListCell *clause;
vbool *res = NULL;
vbool *next = NULL;
bool skip = true;
int i = 0;
if (isDone)
*isDone = ExprSingleResult;
/*
* If any of the clauses is TRUE, the OR result is TRUE regardless of the
* states of the rest of the clauses, so we can stop evaluating and return
* TRUE immediately. If none are TRUE and one or more is NULL, we return
* NULL; otherwise we return FALSE. This makes sense when you interpret
* NULL as "don't know": if we have a TRUE then the OR is TRUE even if we
* aren't sure about some of the other inputs. If all the known inputs are
* FALSE, but we have one or more "don't knows", then we have to report
* that we "don't know" what the OR's result should be --- perhaps one of
* the "don't knows" would have been TRUE if we'd known its value. Only
* when all the inputs are known to be FALSE can we state confidently that
* the OR's result is FALSE.
*/
foreach(clause, clauses)
{
ExprState *clausestate = (ExprState *) lfirst(clause);
Datum clause_value;
/*
* to check if all the values is true, then skip to evaluate some
* expressions
*/
skip = true;
clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL);
if(NULL == res)
{
res = DatumGetPointer(clause_value);
Assert(NULL != res->isnull);
for(i = 0; i < res->dim; i++)
{
if(res->isnull[i] ||
!res->values[i])
{
skip = false;
break;
}
}
}
else
{
next = DatumGetPointer(clause_value);
Assert(NULL != res->isnull && NULL != next->isnull);
for(i = 0; i < res->dim; i++)
{
res->isnull[i] =
(res->isnull[i] || next->isnull[i]);
res->values[i] = (res->values[i] || next->values[i]);
if(skip && (res->isnull[i] || !res->values[i]))
skip = false;
}
}
if(skip)
{
*isNull = false;
return PointerGetDatum(res);
}
}
*isNull = false;
return PointerGetDatum(res);
}
static Datum
VExecEvalAndInternal(List* clauses, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
ListCell *clause;
vbool *res = NULL;
vbool *next = NULL;
bool skip = true;
int i = 0;
if (isDone)
*isDone = ExprSingleResult;
/*
* If any of the clauses is FALSE, the AND result is FALSE regardless of
* the states of the rest of the clauses, so we can stop evaluating and
* return FALSE immediately. If none are FALSE and one or more is NULL,
* we return NULL; otherwise we return TRUE. This makes sense when you
* interpret NULL as "don't know", using the same sort of reasoning as for
* OR, above.
*/
foreach(clause, clauses)
{
ExprState *clausestate = (ExprState *) lfirst(clause);
Datum clause_value;
/*
* to check if all the values is false, then skip to evaluate some
* expressions
*/
skip = true;
clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL);
if(NULL == res)
{
res = DatumGetPointer(clause_value);
Assert(NULL != res->isnull);
for(i = 0; i < res->dim; i++)
{
if(res->isnull[i] || res->values[i])
{
skip = false;
break;
}
}
}
else
{
next = DatumGetPointer(clause_value);
Assert(NULL != res->isnull && NULL != next->isnull);
for(i = 0; i < res->dim; i++)
{
res->isnull[i] =
(res->isnull[i] || next->isnull[i]);
res->values[i] = (res->values[i] && next->values[i]);
if(skip && (res->isnull[i] || res->values[i]))
skip = false;
}
}
if(skip)
{
*isNull = false;
return PointerGetDatum(res);
}
}
*isNull = false;
return PointerGetDatum(res);
}
/* ----------------------------------------------------------------
* ExecEvalAnd
* ----------------------------------------------------------------
*/
static Datum
VExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
return VExecEvalAndInternal(andExpr->args, econtext, isNull, isDone);
}
/*
* Init the vectorized expressions
*/
ExprState *
VExecInitExpr(Expr *node, PlanState *parent)
{
ExprState *state = NULL;
/*
* Because Var is the leaf node of the expression tree, it have to be
* refactored first, otherwise the all call stack should be refactored.
*/
switch (nodeTag(node))
{
case T_Var:
state = (ExprState *) makeNode(ExprState);
state->evalfunc = VExecEvalVar;
break;
case T_BoolExpr:
{
BoolExpr *boolexpr = (BoolExpr *) node;
BoolExprState *bstate = makeNode(BoolExprState);
switch (boolexpr->boolop)
{
case AND_EXPR:
bstate->xprstate.evalfunc = (ExprStateEvalFunc) VExecEvalAnd;
break;
case OR_EXPR:
bstate->xprstate.evalfunc = (ExprStateEvalFunc) VExecEvalOr;
break;
case NOT_EXPR:
bstate->xprstate.evalfunc = (ExprStateEvalFunc) VExecEvalNot;
break;
default:
elog(ERROR, "unrecognized boolop: %d",
(int) boolexpr->boolop);
break;
}
bstate->args = (List *)
ExecInitExpr((Expr *) boolexpr->args, parent);
state = (ExprState *) bstate;
}
break;
/*TODO: More and more expressions should be vectorized */
default:
break;
}
/* Common code for all state-node types */
if(NULL != state)
state->expr = node;
return state;
}
/* ----------------------------------------------------------------
* copy from src/backend/executor/execQual.c
*
* NOTE:resultForNull do not used now, we can process it when call the
* ExecVQual.
* ----------------------------------------------------------------
*/
vbool*
ExecVQual(List *qual, ExprContext *econtext, bool resultForNull)
{
vbool *result;
MemoryContext oldContext;
bool isNull;
/*
* debugging stuff
*/
EV_printf("ExecQual: qual is ");
EV_nodeDisplay(qual);
EV_printf("\n");
/*
* Run in short-lived per-tuple context while computing expressions.
*/
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
result = (vbool*)DatumGetPointer(VExecEvalAndInternal(qual, econtext, &isNull, NULL));
MemoryContextSwitchTo(oldContext);
return result;
}
/*
* copy from src/backend/executor/ExecQual.c
* ExecTargetList
* Evaluates a targetlist with respect to the given
* expression context. Returns TRUE if we were able to create
* a result, FALSE if we have exhausted a set-valued expression.
*
* Results are stored into the passed values and isnull arrays.
* The caller must provide an itemIsDone array that persists across calls.
*
* As with ExecEvalExpr, the caller should pass isDone = NULL if not
* prepared to deal with sets of result tuples. Otherwise, a return
* of *isDone = ExprMultipleResult signifies a set element, and a return
* of *isDone = ExprEndResult signifies end of the set of tuple.
*/
static bool
ExecVTargetList(List *targetlist,
ExprContext *econtext,
TupleTableSlot *slot,
ExprDoneCond *itemIsDone,
ExprDoneCond *isDone)
{
MemoryContext oldContext;
ListCell *tl;
TupleBatch tb;
bool isnull;
Assert(NULL != slot);
tb = (TupleBatch)DatumGetPointer(slot->PRIVATE_tb);
Assert(NULL != tb);
tb->ncols = list_length(targetlist);
/*
* Run in short-lived per-tuple context while computing expressions.
*/
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
/*
* evaluate all the expressions in the target list
*/
if (isDone)
*isDone = ExprSingleResult; /* until proven otherwise */
foreach(tl, targetlist)
{
GenericExprState *gstate = (GenericExprState *) lfirst(tl);
TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
AttrNumber resind = tle->resno - 1;
tb->datagroup[resind] = (vtype*)ExecEvalExpr(gstate->arg,
econtext,
&isnull,
&itemIsDone[resind]);
if (itemIsDone[resind] != ExprSingleResult)
{
/*TODO: DO NOT SUPPORT SO FAR*/
elog(ERROR, "Only support single result so far.");
}
}
/* Report success */
MemoryContextSwitchTo(oldContext);
return true;
}