Github user zhangjackey commented on a diff in the pull request: https://github.com/apache/incubator-hawq/pull/1351#discussion_r181933267 --- Diff: contrib/vexecutor/execVQual.c --- @@ -123,3 +123,431 @@ VirtualNodeProc(ScanState* state,TupleTableSlot *slot){ 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 */ + tb = (TupleBatch )slot->PRIVATE_tb; + + Assert(NULL != tb); + + return PointerGetDatum(tb->datagroup[attnum]); +} + + +/* + * 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; + } + + 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) + 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]); + } + 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->header.dim; i++) + { + if(!ret->header.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; + bool AnyNull; + vbool *res = NULL; + vbool *next = NULL; + bool skip = true; + int i = 0; + + if (isDone) + *isDone = ExprSingleResult; + + AnyNull = false; + + /* + * 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); + for(i = 0; i < res->header.dim; i++) + { + if(res->header.isnull[i] || !res->values[i]) + { + skip = false; + break; + } + } + } + else + { + next = DatumGetPointer(clause_value); + for(i = 0; i < res->header.dim; i++) + { + res->header.isnull[i] = (res->header.isnull[i] || next->header.isnull[i]); + res->values[i] = (res->values[i] || next->values[i]); + if(skip && (res->header.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) +{ + List *clauses = andExpr->args; + ListCell *clause; + bool AnyNull; + vbool *res = NULL; + vbool *next = NULL; + bool skip = true; + int i = 0; + + if (isDone) + *isDone = ExprSingleResult; + + AnyNull = false; + + /* + * 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); + for(i = 0; i < res->header.dim; i++) + { + if(res->header.isnull[i] || res->values[i]) + { + skip = false; + break; + } + } + } + else + { + next = DatumGetPointer(clause_value); + for(i = 0; i < res->header.dim; i++) + { + res->header.isnull[i] = (res->header.isnull[i] ||next->header.isnull[i]); + res->values[i] = (res->values[i] || next->values[i]); + if(skip && (res->header.isnull[i] || res->values[i])) + skip = false; + } + } + + if(skip) + { + *isNull = false; + return PointerGetDatum(res); + } + } + + *isNull = false; + return PointerGetDatum(res); +} + +/* + * Init the vectorized expressions + */ +ExprState * +VExecInitExpr(Expr *node, PlanState *parent) +{ + ExprState *state = NULL; + if(NULL == parent->vectorized) + return 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; + } + --- End diff -- yes, add in next commit.
---