555 lines
13 KiB
C++
555 lines
13 KiB
C++
/* -------------------------------------------------------------------------
|
|
* ndp_check.cpp
|
|
* Routines to check whether to pushdown
|
|
*
|
|
* Portions Copyright (c) 2022 Huawei Technologies Co.,Ltd.
|
|
*
|
|
* IDENTIFICATION
|
|
* contrib/ndpplugin/ndp_check.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
#include "catalog/pg_operator.h"
|
|
#include "utils/builtins.h"
|
|
#include "ndp_check.h"
|
|
|
|
// operations not define in pg_operator.h
|
|
#define BPCHARLEOID 1059
|
|
#define BPCHARGEOID 1061
|
|
#define FLOAT48PLOID 1116
|
|
#define FLOAT48MIOID 1117
|
|
#define FLOAT48MULOID 1119
|
|
#define FLOAT48DIVOID 1118
|
|
#define FLOAT84PLOID 1126
|
|
#define FLOAT84MIOID 1127
|
|
#define FLOAT84MULOID 1129
|
|
#define FLOAT84DIVOID 1128
|
|
#define TEXTNEOID 531
|
|
#define TEXTLEOID 665
|
|
#define TEXTGEOID 667
|
|
|
|
// functions not define in pg_proc.h
|
|
#define INT4BOOLOID 2557
|
|
#define BOOLINT4OID 2558
|
|
|
|
const Oid g_ndp_support_data_type[] = {
|
|
BOOLOID,
|
|
INT1OID,
|
|
INT2OID,
|
|
INT4OID,
|
|
INT8OID,
|
|
FLOAT4OID,
|
|
FLOAT8OID,
|
|
VARCHAROID,
|
|
TEXTOID,
|
|
BPCHAROID,
|
|
BPCHARARRAYOID,
|
|
TIMESTAMPOID,
|
|
NUMERICOID,
|
|
INTERVALOID,
|
|
ARRAYNUMERICOID,
|
|
TEXTARRAYOID
|
|
};
|
|
|
|
inline static bool CheckNdpSupportDataType(Oid oid)
|
|
{
|
|
for (int i = 0; i < (int)(sizeof(g_ndp_support_data_type) / sizeof(Oid)); ++i) {
|
|
if (oid == g_ndp_support_data_type[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const Oid g_ndp_support_op_expr_type[] = {
|
|
INT48EQOID,
|
|
INT48NEOID,
|
|
INT48LTOID,
|
|
INT48GTOID,
|
|
INT48LEOID,
|
|
INT48GEOID,
|
|
INT2EQOID,
|
|
INT2LTOID,
|
|
INT4EQOID,
|
|
INT4LTOID,
|
|
TEXTEQOID,
|
|
INT8EQOID,
|
|
INT8NEOID,
|
|
INT8LTOID,
|
|
INT8GTOID,
|
|
INT8LEOID,
|
|
INT8GEOID,
|
|
INT84EQOID,
|
|
INT84NEOID,
|
|
INT84LTOID,
|
|
INT84GTOID,
|
|
INT84LEOID,
|
|
INT84GEOID,
|
|
INT4MULOID,
|
|
INT4NEOID,
|
|
INT2NEOID,
|
|
INT2GTOID,
|
|
INT4GTOID,
|
|
INT2LEOID,
|
|
INT4LEOID,
|
|
INT2GEOID,
|
|
INT4GEOID,
|
|
INT2MULOID,
|
|
INT2DIVOID,
|
|
INT4DIVOID,
|
|
INT24EQOID,
|
|
INT42EQOID,
|
|
INT24LTOID,
|
|
INT42LTOID,
|
|
INT24GTOID,
|
|
INT42GTOID,
|
|
INT24NEOID,
|
|
INT42NEOID,
|
|
INT24LEOID,
|
|
INT42LEOID,
|
|
INT24GEOID,
|
|
INT42GEOID,
|
|
INT24MULOID,
|
|
INT42MULOID,
|
|
INT24DIVOID,
|
|
INT42DIVOID,
|
|
INT2PLOID,
|
|
INT4PLOID,
|
|
INT24PLOID,
|
|
INT42PLOID,
|
|
INT2MIOID,
|
|
INT4MIOID,
|
|
INT24MIOID,
|
|
INT42MIOID,
|
|
FLOAT4EQOID,
|
|
FLOAT4NEOID,
|
|
FLOAT4LTOID,
|
|
FLOAT4GTOID,
|
|
FLOAT4LEOID,
|
|
FLOAT4GEOID,
|
|
TEXTLTOID,
|
|
TEXTGTOID,
|
|
FLOAT8EQOID,
|
|
FLOAT8NEOID,
|
|
FLOAT8LTOID,
|
|
FLOAT8LEOID,
|
|
FLOAT8GTOID,
|
|
FLOAT8GEOID,
|
|
INT8PLOID,
|
|
INT8MIOID,
|
|
INT8MULOID,
|
|
INT8DIVOID,
|
|
INT84PLOID,
|
|
INT84MIOID,
|
|
INT84MULOID,
|
|
INT84DIVOID,
|
|
INT48PLOID,
|
|
INT48MIOID,
|
|
INT48MULOID,
|
|
INT48DIVOID,
|
|
INT82PLOID,
|
|
INT82MIOID,
|
|
INT82MULOID,
|
|
INT82DIVOID,
|
|
INT28PLOID,
|
|
INT28MIOID,
|
|
INT28MULOID,
|
|
INT28DIVOID,
|
|
BPCHAREQOID,
|
|
BPCHARNEOID,
|
|
BPCHARLTOID,
|
|
BPCHARGTOID,
|
|
FLOAT48EQOID,
|
|
FLOAT48NEOID,
|
|
FLOAT48LTOID,
|
|
FLOAT48GTOID,
|
|
FLOAT48LEOID,
|
|
FLOAT48GEOID,
|
|
FLOAT84EQOID,
|
|
FLOAT84NEOID,
|
|
FLOAT84LTOID,
|
|
FLOAT84GTOID,
|
|
FLOAT84LEOID,
|
|
FLOAT84GEOID,
|
|
NUMERICEQOID,
|
|
NUMERICNEOID,
|
|
NUMERICLTOID,
|
|
NUMERICLEOID,
|
|
NUMERICGTOID,
|
|
NUMERICGEOID,
|
|
NUMERICADDOID,
|
|
NUMERICSUBOID,
|
|
NUMERICMULOID,
|
|
NUMERICDIVOID,
|
|
INT28EQOID,
|
|
INT28NEOID,
|
|
INT28LTOID,
|
|
INT28GTOID,
|
|
INT28LEOID,
|
|
INT28GEOID,
|
|
INT82EQOID,
|
|
INT82NEOID,
|
|
INT82LTOID,
|
|
INT82GTOID,
|
|
INT82LEOID,
|
|
INT82GEOID,
|
|
TIMESTAMPEQOID,
|
|
TIMESTAMPNEOID,
|
|
TIMESTAMPLTOID,
|
|
TIMESTAMPLEOID,
|
|
TIMESTAMPGTOID,
|
|
TIMESTAMPGEOID,
|
|
OID_TEXT_LIKE_OP,
|
|
|
|
// operations not define in pg_operator.h
|
|
BPCHARLEOID,
|
|
BPCHARGEOID,
|
|
FLOAT48PLOID,
|
|
FLOAT48MIOID,
|
|
FLOAT48MULOID,
|
|
FLOAT48DIVOID,
|
|
FLOAT84PLOID,
|
|
FLOAT84MIOID,
|
|
FLOAT84MULOID,
|
|
FLOAT84DIVOID,
|
|
TEXTNEOID,
|
|
TEXTLEOID,
|
|
TEXTGEOID,
|
|
};
|
|
|
|
inline static bool CheckNdpSupportOpExprType(Oid oid)
|
|
{
|
|
for (int i = 0; i < (int)(sizeof(g_ndp_support_op_expr_type)/sizeof(Oid)); ++i) {
|
|
if (oid == g_ndp_support_op_expr_type[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const Oid g_ndp_support_function_type[] = {
|
|
INT4NUMERICFUNCOID,
|
|
TIMESTAMPPARTFUNCOID,
|
|
TEXTSUBSTRINGFUNCOID,
|
|
FLOAT4TOFLOAT8FUNCOID,
|
|
INT4TOFLOAT8FUNCOID,
|
|
INT2TOFLOAT8FUNCOID,
|
|
INT2TOFLOAT4FUNCOID,
|
|
RTRIM1FUNCOID,
|
|
|
|
// functions not define in pg_proc.h
|
|
INT4BOOLOID,
|
|
BOOLINT4OID,
|
|
};
|
|
|
|
inline static bool CheckNdpSupportFunctionType(Oid oid)
|
|
{
|
|
for (int i = 0; i < (int)(sizeof(g_ndp_support_function_type)/sizeof(Oid)); ++i) {
|
|
if (oid == g_ndp_support_function_type[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const Oid g_ndp_support_aggfunc_type[] = {
|
|
2102, /* avg(int2) */
|
|
2101, /* avg(int4) */
|
|
2100, /* avg(int8) */
|
|
2104, /* avg(float4) */
|
|
2105, /* avg(float8) */
|
|
2103, /* avg(numeric) */
|
|
2106, /* avg(interval) */
|
|
2109, /* sum(int2) */
|
|
2108, /* sum(int4) */
|
|
2107, /* sum(int8) */
|
|
2110, /* sum(float4) */
|
|
2111, /* sum(float8) */
|
|
2114, /* sum(numeric) */
|
|
2113, /* sum(interval) */
|
|
2134, /* min(oid) */
|
|
2118, /* max(oid) */
|
|
2133, /* min(int2) */
|
|
2117, /* max(int2) */
|
|
2132, /* min(int4) */
|
|
2116, /* max(int4) */
|
|
2131, /* min(int8) */
|
|
2115, /* max(int8) */
|
|
2135, /* min(float4) */
|
|
2136, /* min(float8) */
|
|
2119, /* max(float4) */
|
|
2120, /* max(float8) */
|
|
2146, /* min(numeric) */
|
|
2130, /* max(numeric) */
|
|
2144, /* min(interval) */
|
|
2128, /* max(interval) */
|
|
2138, /* min(date) */
|
|
2122, /* max(date) */
|
|
2142, /* min(timestamp) */
|
|
2126, /* max(timestamp) */
|
|
2245, /* min(bpchar) */
|
|
2244, /* max(bpchar) */
|
|
2145, /* min(text) */
|
|
2129, /* max(text) */
|
|
2147, /* count(expr) */
|
|
2803, /* count(*) */
|
|
};
|
|
|
|
inline static bool CheckNdpSupportAggFuncType(Oid oid)
|
|
{
|
|
for (int i = 0; i < (int)(sizeof(g_ndp_support_aggfunc_type)/sizeof(Oid)); ++i) {
|
|
if (oid == g_ndp_support_aggfunc_type[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool CheckNdpSupportVar(Var *var)
|
|
{
|
|
if (!CheckNdpSupportDataType(var->vartype)) {
|
|
return false;
|
|
}
|
|
|
|
if (var->vartype == NUMERICOID) {
|
|
unsigned int typemod = (unsigned int)(var->vartypmod - VARHDRSZ);
|
|
if (var->vartypmod != -1) {
|
|
int precision = (typemod >> 16) & 0xffff;
|
|
if (precision <= 0 || precision > 38) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
static bool CheckNdpSupportParam(Param *param)
|
|
{
|
|
if (param->paramkind != PARAM_EXTERN) {
|
|
return false;
|
|
}
|
|
return CheckNdpSupportDataType(param->paramtype);
|
|
}
|
|
|
|
static bool CheckNdpSupportListType(const List* exprs);
|
|
|
|
static bool CheckNdpSupportNodeType(Node* node)
|
|
{
|
|
if (!node) return true;
|
|
|
|
switch (node->type) {
|
|
case T_TargetEntry:
|
|
return CheckNdpSupportNodeType(castNode(Node, (castNode(TargetEntry, node)->expr)));
|
|
case T_Var:
|
|
return CheckNdpSupportVar(castNode(Var, node));
|
|
case T_Param:
|
|
return CheckNdpSupportParam(castNode(Param, node));
|
|
case T_OpExpr: {
|
|
OpExpr* op = castNode(OpExpr, node);
|
|
// check OpExpr::opfuncid in future
|
|
if ((!CheckNdpSupportDataType(op->opresulttype)) || (!CheckNdpSupportOpExprType(op->opno))) {
|
|
return false;
|
|
}
|
|
return CheckNdpSupportListType(op->args);
|
|
} case T_Const:
|
|
return CheckNdpSupportDataType(castNode(Const, node)->consttype);
|
|
case T_RelabelType:
|
|
return CheckNdpSupportDataType(castNode(RelabelType, node)->resulttype);
|
|
case T_Aggref: {
|
|
Aggref* agg = castNode(Aggref, node);
|
|
if ((!CheckNdpSupportDataType(agg->aggtype)) || (!CheckNdpSupportAggFuncType(agg->aggfnoid))) {
|
|
return false;
|
|
}
|
|
if ((!CheckNdpSupportListType(agg->aggorder)) || (!CheckNdpSupportListType(agg->aggdistinct))) {
|
|
return false;
|
|
}
|
|
return CheckNdpSupportListType(agg->args);
|
|
}
|
|
case T_FuncExpr: {
|
|
FuncExpr* func = castNode(FuncExpr, node);
|
|
if ((!CheckNdpSupportDataType(func->funcresulttype)) || (!CheckNdpSupportFunctionType(func->funcid))) {
|
|
return false;
|
|
}
|
|
return CheckNdpSupportListType(func->args);
|
|
}
|
|
case T_BoolExpr: {
|
|
BoolExpr *boolExpr = castNode(BoolExpr, node);
|
|
return CheckNdpSupportListType(boolExpr->args);
|
|
}
|
|
case T_CaseExpr: {
|
|
CaseExpr *caseExpr = castNode(CaseExpr, node);
|
|
if ((!CheckNdpSupportDataType(caseExpr->casetype)) ||
|
|
(!CheckNdpSupportNodeType(castNode(Node, caseExpr->defresult)))) {
|
|
return false;
|
|
}
|
|
return CheckNdpSupportListType(caseExpr->args);
|
|
}
|
|
case T_CaseWhen: {
|
|
CaseWhen *caseWhen = castNode(CaseWhen, node);
|
|
return CheckNdpSupportNodeType(castNode(Node, caseWhen->expr));
|
|
}
|
|
case T_CaseTestExpr: {
|
|
CaseTestExpr *caseTestExpr = castNode(CaseTestExpr, node);
|
|
return CheckNdpSupportDataType(caseTestExpr->typeId);
|
|
}
|
|
case T_ScalarArrayOpExpr: {
|
|
ScalarArrayOpExpr *scalarArrayOpExpr = castNode(ScalarArrayOpExpr, node);
|
|
if (!CheckNdpSupportOpExprType(scalarArrayOpExpr->opno)) {
|
|
return false;
|
|
}
|
|
return CheckNdpSupportListType(scalarArrayOpExpr->args);
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool CheckNdpSupportListType(const List* exprs)
|
|
{
|
|
if (!exprs) return true;
|
|
|
|
foreach_cell (l, exprs) {
|
|
Node* node = (Node*)lfirst(l);
|
|
if (!CheckNdpSupportNodeType(node)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// check Plan, remove stmt if no use in future
|
|
Plan* CheckAndGetNdpPlan(PlannedStmt* stmt, SeqScan* scan, Plan* parent)
|
|
{
|
|
Plan* node = nullptr;
|
|
|
|
// 1. check scan, should check Scan::tableRows or Plan::plan_rows?
|
|
if (scan->plan.exec_type != EXEC_ON_DATANODES
|
|
|| !CheckNdpSupportListType(scan->plan.targetlist)
|
|
|| !CheckNdpSupportListType(scan->plan.qual)
|
|
|| scan->isPartTbl) {
|
|
return nullptr;
|
|
}
|
|
node = (Plan*)scan;
|
|
|
|
// 2. check agg
|
|
if (parent && IsA(parent, Agg)) {
|
|
Agg* agg = (Agg*)parent;
|
|
if (agg->aggstrategy != AGG_SORTED && // don't support distinct sort agg
|
|
agg->groupingSets == nullptr && agg->chain == nullptr && // don't support grouping set
|
|
CheckNdpSupportListType(parent->targetlist) &&
|
|
CheckNdpSupportListType(parent->qual)) {
|
|
node = parent;
|
|
}
|
|
}
|
|
|
|
// 3. not push down if not agg and scan has no filter
|
|
if (node == (Plan*)scan && scan->plan.qual == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
// 4. if seqscan or agg is righttree of nestloop, do not push down
|
|
if (parent && IsA(parent, NestLoop) && parent->righttree == node) {
|
|
return nullptr;
|
|
}
|
|
|
|
// 5. if seqscan is under merge join, do not push down
|
|
if (parent && IsA(parent, MergeJoin)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// 6. if seqscan is under limit, do not push down
|
|
if (parent && IsA(parent, Limit)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static bool CheckNdpSupportHint(HintState* hint)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static bool CheckNdpSupportXact(void)
|
|
{
|
|
if (IsolationIsSerializable()) {
|
|
return false;
|
|
}
|
|
|
|
if (!IsTransactionState() || IsSubTransaction()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool CheckNdpPreloadSupport(const char* libraries)
|
|
{
|
|
char* rawstring = NULL;
|
|
List* elemlist = NULL;
|
|
ListCell* l = NULL;
|
|
|
|
if (libraries == NULL || libraries[0] == '\0') {
|
|
return false; /* nothing to do */
|
|
}
|
|
|
|
/* Need a modifiable copy of string */
|
|
rawstring = pstrdup(libraries);
|
|
/* Parse string into list of identifiers */
|
|
if (!SplitIdentifierString(rawstring, ',', &elemlist)) {
|
|
/* syntax error in list */
|
|
pfree(rawstring);
|
|
list_free(elemlist);
|
|
ereport(LOG, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid syntax in parameter shared_preload_libraries")));
|
|
return false;
|
|
}
|
|
|
|
foreach (l, elemlist) {
|
|
char* tok = (char*)lfirst(l);
|
|
char* filename = NULL;
|
|
|
|
filename = pstrdup(tok);
|
|
if (strcmp(filename, "ndpplugin") == 0) {
|
|
pfree(filename);
|
|
pfree(rawstring);
|
|
list_free(elemlist);
|
|
return true;
|
|
} else {
|
|
pfree(filename);
|
|
}
|
|
}
|
|
|
|
pfree(rawstring);
|
|
list_free(elemlist);
|
|
ereport(LOG, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("ndpplugin is not preloaded")));
|
|
return false;
|
|
}
|
|
|
|
// check Query/PlanedStmt/conf
|
|
bool CheckNdpSupport(Query* querytree, PlannedStmt *stmt)
|
|
{
|
|
if (!u_sess->ndp_cxt.enable_ndp) {
|
|
return false;
|
|
}
|
|
|
|
/* only plain relations are supported */
|
|
if (!stmt || stmt->commandType != CMD_SELECT) {
|
|
return false;
|
|
}
|
|
|
|
if (!CheckNdpSupportXact()) {
|
|
return false;
|
|
}
|
|
|
|
if (!CheckNdpSupportHint(querytree->hintState)) {
|
|
return false;
|
|
}
|
|
|
|
auto libraries = g_instance.attr.attr_common.shared_preload_libraries_string;
|
|
if (stmt->num_streams > 0 && !CheckNdpPreloadSupport(libraries)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|