blob: 7ef36c35bc63baf34f5016b8531d4fd20659203d [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 "access/htup.h"
#include "catalog/catquery.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "cdb/cdbappendonlyam.h"
#include "cdb/cdbllize.h"
#include "parser/parse_oper.h"
#include "nodes/makefuncs.h"
#include "nodes/primnodes.h"
#include "nodes/plannodes.h"
#include "nodes/relation.h"
#include "optimizer/walkers.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "vcheck.h"
#include "vexecutor.h"
typedef struct VecTypeHashEntry
{
Oid src;
Oid dest;
}VecTypeHashEntry;
/* Map between the vectorized types and non-vectorized types */
static HTAB *hashMapN2V = NULL;
/* Map between the vectorized types and non-vectorized types */
static HTAB *hashMapV2N = NULL;
/*
* We check the expressions tree recursively becuase the args can be a sub expression,
* we must check the return type of sub expression to fit the parent expressions.
* so the retType in Vectorized is a temporary values, after we check on expression,
* we set the retType of this expression, and transfer this value to his parent.
*/
typedef struct VectorizedContext
{
plan_tree_base_prefix base; /* Required prefix for plan_tree_walker/mutator */
Oid retType;
bool replace;
}VectorizedContext;
static Oid get_aggregate_oid(const char *aggname, Oid oidType);
static char *getProcNameOnlyFromOid(Oid object_oid);
// copyed from src/backend/utils/cache/lsyscache.c
// Get oid of aggregate with given name and argument type
static Oid
get_aggregate_oid(const char *aggname, Oid oidType)
{
HeapTuple htup = NULL;
// lookup pg_proc for functions with the given name and arg type
cqContext *pcqCtx = caql_beginscan(
NULL,
cql("SELECT * FROM pg_proc "
" WHERE proname = :1",
PointerGetDatum((char *) aggname)));
Oid oidResult = InvalidOid;
while (HeapTupleIsValid(htup = caql_getnext(pcqCtx)))
{
Oid oidProc = HeapTupleGetOid(htup);
Form_pg_proc proctuple = (Form_pg_proc) GETSTRUCT(htup);
// skip functions with the wrong number of type of arguments
if (0 != proctuple->pronargs && InvalidOid != oidType)
{
if (1 != proctuple->pronargs || oidType != proctuple->proargtypes.values[0])
{
continue;
}
}
else if((0 != proctuple->pronargs && InvalidOid == oidType) ||
(0 == proctuple->pronargs && InvalidOid != oidType))
continue;
if (caql_getcount(
NULL,
cql("SELECT COUNT(*) FROM pg_aggregate "
" WHERE aggfnoid = :1 ",
ObjectIdGetDatum(oidProc))) > 0)
{
oidResult = oidProc;
break;
}
}
caql_endscan(pcqCtx);
return oidResult;
}
//copied from src/backend/catalog/aclchk.c and we refactor it.
static char *
getProcNameOnlyFromOid(Oid object_oid)
{
StringInfoData tname;
initStringInfo(&tname);
HeapTuple tup;
cqContext *pCtx;
Assert(OidIsValid(object_oid));
pCtx = caql_beginscan(
NULL,
cql("SELECT * FROM pg_proc "
" WHERE oid = :1 ",
ObjectIdGetDatum(object_oid)));
tup = caql_getnext(pCtx);
if (!HeapTupleIsValid(tup))
elog(ERROR, "oid [%u] not found in table pg_class", object_oid);
appendStringInfo(&tname, "%s", NameStr(((Form_pg_proc)GETSTRUCT(tup))->proname));
caql_endscan(pCtx);
return tname.data;
}
/*
* Check all the expressions if they can be vectorized
* NOTE: if an expressions is vectorized, we return false...,because we should check
* all the expressions in the Plan node, if we return true, then the walker will be
* over...
*/
static bool
CheckVectorizedExpression(Node *node, VectorizedContext *ctx)
{
if(NULL == node)
return false;
if(is_plan_node(node))
return false;
//check the type of Var if it can be vectorized
if(IsA(node, Var))
{
Var *var = (Var*)node;
Oid vtype = GetVtype(var->vartype);
if(InvalidOid == vtype)
return true;
ctx->retType = vtype;
if(ctx->replace)
var->vartype = vtype;
return false;
}
//Const treat as can be vectorzied, its return type is non-vectorized type
//because we support the function like this: vtype op(vtype, const);
if(IsA(node, Const))
{
Const *c = (Const*)node;
ctx->retType = c->consttype;
return false;
}
//OpExpr:args, return types should can be vectorized,
//and there must exists an vectorized function to implement the operator
if(IsA(node, OpExpr))
{
OpExpr *op = (OpExpr*)node;
Node *argnode = NULL;
Oid ltype, rtype, rettype;
Form_pg_operator voper;
HeapTuple tuple;
//OpExpr mostly have two args, check the first one
argnode = linitial(op->args);
if(CheckVectorizedExpression(argnode, ctx))
return true;
ltype = ctx->retType;
//check the second one
argnode = lsecond(op->args);
if(CheckVectorizedExpression(argnode, ctx))
return true;
rtype = ctx->retType;
//check the return type
rettype = GetVtype(op->opresulttype);
if(InvalidOid == rettype)
return true;
//get the vectorized operator functions
//NOTE:we have no ParseState now, Give the NULL value is OK but not good...
tuple = oper(NULL, list_make1(makeString(get_opname(op->opno))),
ltype, rtype, true, -1);
if(NULL == tuple)
return true;
voper = (Form_pg_operator)GETSTRUCT(tuple);
if(voper->oprresult != rettype)
{
ReleaseOperator(tuple);
return true;
}
if(ctx->replace)
{
op->opresulttype = rettype;
op->opfuncid = voper->oprcode;
}
ReleaseOperator(tuple);
ctx->retType = rettype;
return false;
}
/* support aggregate functions */
if(IsA(node, Aggref))
{
Aggref *ref = (Aggref*)node;
char *aggname = NULL;
Oid retType;
Oid vaggoid;
if(ref->aggdistinct || NULL != ref->aggorder)
return true;
/* Make sure there is less than one arguments */
if(1 < list_length(ref->args))
return true;
/* check arguments */
if(NULL != ref->args)
{
if(CheckVectorizedExpression(linitial(ref->args), ctx))
return true;
retType = ctx->retType;
}
else
retType = InvalidOid;
/* check the vectorized aggregate functions */
aggname = getProcNameOnlyFromOid(ref->aggfnoid);
if(0 == strcmp(aggname, "count") &&
0 == list_length(ref->args))
aggname = "veccount";
Assert(NULL != aggname);
vaggoid = get_aggregate_oid(aggname, retType);
if(InvalidOid == vaggoid)
return true;
if(ctx->replace)
ref->aggfnoid = vaggoid;
return false;
}
/*
* if there are const in expressions, it may need to convert
* type implicitly by FuncExpr, we only check if the arguments
* of the FuncExpr is constant.
*/
if(IsA(node, FuncExpr))
{
FuncExpr *f = (FuncExpr*)node;
ListCell *l = NULL;
Node* expr = NULL;
if(1 != list_length(f->args))
return true;
expr = (Node*)linitial(f->args);
if(!IsA(expr, Const))
return true;
ctx->retType = f->funcresulttype;
return false;
}
if(IsA(node,NullTest))
return true;
if(IsA(node, RowCompareExpr))
return true;
//now, other nodes treat as can not be vectorized
return plan_tree_walker(node, CheckVectorizedExpression, ctx);;
}
/*
* check an plan node, all the expressions in it should be checked
* set the flag if an plan node can be vectorized
*/
static bool
CheckPlanNodeWalker(PlannerInfo *root, Plan *plan)
{
VectorizedContext ctx;
if(plan->vectorized)
return true;
/* skip to check if there are no corresponding vec-exec-operators*/
if(!HasVecExecOprator(plan))
{
plan->vectorized = false;
return true;
}
/* if sub node can not vectorized, set the parent cannot vectorized too */
if( (NULL != plan->lefttree && !plan->lefttree->vectorized) ||
(NULL != plan->righttree && !plan->righttree->vectorized))
{
plan->vectorized = false;
return true;
}
/* the result of aggregate functions is scalar */
if(IsA(plan, Motion) && IsA((plan->lefttree), Agg))
{
plan->vectorized = false;
return true;
}
/* Don't support SORT Aggregate so far */
if(IsA(plan, Agg) && ((Agg*)plan)->aggstrategy == AGG_SORTED)
return true;
/*
* if there is an const in the projection, it is not supported so far,ugly...
* I suppose that we have to carefully discriminate all the expressions,
* then we can know that which const is in the projection, and which one
* is in the qualification, we can process them differently.
*/
{
List *targetlist = plan->targetlist;
ListCell *c = NULL;
foreach(c, targetlist)
{
TargetEntry *entry = (TargetEntry*)lfirst(c);
if(!IsA(entry->expr,Aggref) && !contain_var_clause((Node*)entry))
return true;
}
}
planner_init_plan_tree_base(&ctx.base, root);
ctx.replace =false;
ctx.retType = InvalidOid;
plan->vectorized = !plan_tree_walker((Node*)plan,
CheckVectorizedExpression,
&ctx);
return true;
}
/*
* check the plan tree
*/
static Plan*
CheckPlanVectorzied(PlannerInfo *root, Plan *plan)
{
if(NULL == plan)
return plan;
CheckPlanVectorzied(root, plan->lefttree);
CheckPlanVectorzied(root, plan->righttree);
CheckPlanNodeWalker(root, plan);
return plan;
}
/*
* Replace the non-vectorirzed type to vectorized type
*/
static bool
ReplacePlanNodeWalker(PlannerInfo *root, Plan *plan)
{
VectorizedContext ctx;
if(!plan->vectorized)
return true;
/* skip to replace if there are no corresponding vec-exec-operators*/
if(!HasVecExecOprator(plan))
{
plan->vectorized = false;
return true;
}
planner_init_plan_tree_base(&ctx.base, root);
ctx.replace = true;
ctx.retType = InvalidOid;
plan_tree_walker((Node*)plan,
CheckVectorizedExpression,
&ctx);
return true;
}
/*
* check the plan tree
*/
static Plan*
ReplacePlanVectorzied(PlannerInfo *root, Plan *plan)
{
if(NULL == plan)
return plan;
ReplacePlanVectorzied(root, plan->lefttree);
ReplacePlanVectorzied(root, plan->righttree);
ReplacePlanNodeWalker(root, plan);
return plan;
}
Plan*
CheckAndReplacePlanVectorized(PlannerInfo *root, Plan *plan)
{
/* the top plan node can not be vectorized so far */
plan->vectorized = false;
plan = CheckPlanVectorzied(root, plan);
plan->vectorized = false;
return ReplacePlanVectorzied(root, plan);
}
/*
* map non-vectorized type to vectorized type.
* To scan the PG_TYPE is inefficient, so we create a hashtable to map
* the vectorized type and non-vectorized types.
*/
Oid GetVtype(Oid ntype)
{
HeapTuple tuple;
cqContext *pcqCtx;
Oid vtype;
VecTypeHashEntry *entry = NULL;
bool found = false;
//construct the hash table
if(NULL == hashMapN2V)
{
HASHCTL hash_ctl;
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Oid);
hash_ctl.entrysize = sizeof(VecTypeHashEntry);
hash_ctl.hash = oid_hash;
hashMapN2V = hash_create("vectorized_v2n", 64/*enough?*/,
&hash_ctl, HASH_ELEM | HASH_FUNCTION);
}
//first, find the vectorized type in hash table
entry = hash_search(hashMapN2V, &ntype, HASH_ENTER, &found);
if(found)
return entry->dest;
Assert(!found);
//Second, if it can not be found in hash table, find in PG_TYPE
pcqCtx = caql_beginscan(NULL,
cql("SELECT * FROM pg_type "
" WHERE typelem = :1 "
" AND typstorage = :2 ",
ObjectIdGetDatum(ntype),
CharGetDatum('e')));
tuple = caql_getnext(pcqCtx);
if(!HeapTupleIsValid(tuple))
{
caql_endscan(pcqCtx);
entry->dest = InvalidOid; /* storage the InvalidOid in hash table*/
return InvalidOid;
}
vtype = HeapTupleGetOid(tuple);
//storage in the hash table
entry->dest = vtype;
caql_endscan(pcqCtx);
return DatumGetObjectId(vtype);
}
/*
* Get the functions for the vectorized types
*/
Oid GetNtype(Oid vtype){
HeapTuple tuple;
bool isNull = true;
cqContext *pcqCtx;
VecTypeHashEntry *entry = NULL;
Oid ntype;
bool found = false;
//construct the hash table
if(NULL == hashMapV2N)
{
HASHCTL hash_ctl;
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Oid);
hash_ctl.entrysize = sizeof(VecTypeHashEntry);
hash_ctl.hash = oid_hash;
hashMapV2N = hash_create("hashvfunc", 64/*enough?*/, &hash_ctl, HASH_ELEM | HASH_FUNCTION);
}
//first, find the vectorized type in hash table
entry = hash_search(hashMapV2N, &vtype, HASH_ENTER, &found);
if(found)
return entry->dest;
Assert(!found);
pcqCtx = caql_beginscan(NULL,
cql("SELECT * FROM pg_type "
" WHERE oid = :1 "
" AND typstorage = :2 ",
ObjectIdGetDatum(vtype),
CharGetDatum('e')));
tuple = caql_getnext(pcqCtx);
if(!HeapTupleIsValid(tuple))
{
caql_endscan(pcqCtx);
return InvalidOid;
}
ntype = caql_getattr(pcqCtx,
Anum_pg_type_typelem,
&isNull);
Assert(!isNull);
/* storage in hash table*/
entry->dest = ntype;
caql_endscan(pcqCtx);
return entry->dest;
}