diff --git a/contrib/postgres_fdw/postgres_fdw.cpp b/contrib/postgres_fdw/postgres_fdw.cpp index 9094c985d784542a469c7316c2c1132ade6c0664..e14e7800b7ad475a8daf37790b61604ccd20201e 100644 --- a/contrib/postgres_fdw/postgres_fdw.cpp +++ b/contrib/postgres_fdw/postgres_fdw.cpp @@ -4137,7 +4137,7 @@ static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node */ Assert(!IsA(expr, RestrictInfo)); rinfo = make_restrictinfo(expr, true, false, false, root->qualSecurityLevel, grouped_rel->relids, - NULL, NULL); + NULL, NULL, false); if (is_foreign_expr(root, grouped_rel, expr)) { fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo); } else { diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 4db5b5ba6180101e6d154780d6586b30649337bf..99a76c05b83dbe3c5ece18839ab28a91ce09812d 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -1911,7 +1911,26 @@ static VecHashJoin* _copyVecHashJoin(const VecHashJoin* from) return newnode; } -static VecAgg* _copyVecAgg(const VecAgg* from) +static VecAsofJoin *_copyVecAsofJoin(const VecAsofJoin *from) +{ + VecAsofJoin *newnode = makeNode(VecAsofJoin); + + /* + * copy node superclass fields + */ + CopyJoinFields((const Join *)from, (Join *)newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(hashclauses); + COPY_NODE_FIELD(mergeclauses); + COPY_SCALAR_FIELD(streamBothSides); + + return newnode; +} + +static VecAgg *_copyVecAgg(const VecAgg *from) { VecAgg* newnode = makeNode(VecAgg); @@ -3492,6 +3511,7 @@ static JoinExpr* _copyJoinExpr(const JoinExpr* from) COPY_SCALAR_FIELD(rtindex); COPY_SCALAR_FIELD(is_straight_join); COPY_SCALAR_FIELD(is_apply_join); + COPY_SCALAR_FIELD(isAsof); return newnode; } @@ -3772,12 +3792,12 @@ static SpecialJoinInfo* _copySpecialJoinInfo(const SpecialJoinInfo* from) static LateralJoinInfo * _copyLateralJoinInfo(const LateralJoinInfo *from) { - LateralJoinInfo *newnode = makeNode(LateralJoinInfo); + LateralJoinInfo *newnode = makeNode(LateralJoinInfo); COPY_BITMAPSET_FIELD(lateral_lhs); COPY_BITMAPSET_FIELD(lateral_rhs); - return newnode; + return newnode; } /* @@ -6662,7 +6682,7 @@ static AlterRlsPolicyStmt* _copyAlterRlsPolicyStmt(const AlterRlsPolicyStmt* fro return newnode; } -static CreateAmStmt* _copyCreateAmStmt(const CreateAmStmt *from) +static CreateAmStmt *_copyCreateAmStmt(const CreateAmStmt *from) { CreateAmStmt *newnode = makeNode(CreateAmStmt); COPY_STRING_FIELD(amname); @@ -8269,6 +8289,10 @@ void* copyObject(const void* from) case T_VecHashJoin: retval = _copyVecHashJoin((VecHashJoin*)from); break; + case T_AsofJoin: + case T_VecAsofJoin: + retval = _copyVecAsofJoin((VecAsofJoin *)from); + break; case T_VecAgg: retval = _copyVecAgg((VecAgg*)from); break; diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index 793e2cae2673c30e3d7e5bdcd32e244a50eef451..eb38f95eac94ede746f63db821702e1a93df9571 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -785,6 +785,7 @@ static bool _equalJoinExpr(const JoinExpr* a, const JoinExpr* b) COMPARE_SCALAR_FIELD(rtindex); COMPARE_SCALAR_FIELD(is_straight_join); COMPARE_SCALAR_FIELD(is_apply_join); + COMPARE_SCALAR_FIELD(isAsof); return true; } diff --git a/src/common/backend/nodes/nodes.cpp b/src/common/backend/nodes/nodes.cpp index f32a2ac9a6468bcf936bd6c4aa31c807c2ed53d3..acf4510540ec0f64fbf17affc8757083cec60f2e 100755 --- a/src/common/backend/nodes/nodes.cpp +++ b/src/common/backend/nodes/nodes.cpp @@ -72,6 +72,7 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, {T_NestLoop, "NestLoop"}, {T_MergeJoin, "MergeJoin"}, {T_HashJoin, "HashJoin"}, + {T_AsofJoin, "AsofJoin"}, {T_Material, "Material"}, {T_Sort, "Sort"}, {T_SortGroup, "SortGroup"}, @@ -270,6 +271,7 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, {T_NestPath, "NestPath"}, {T_MergePath, "MergePath"}, {T_HashPath, "HashPath"}, + {T_AsofPath, "AsofPath"}, {T_TidPath, "TidPath"}, {T_ForeignPath, "ForeignPath"}, {T_ExtensiblePath, "ExtensiblePath"}, @@ -543,6 +545,7 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, {T_VecNestLoop, "VecNestLoop"}, {T_VecMergeJoin, "VecMergeJoin"}, {T_VecHashJoin, "VecHashJoin"}, + {T_VecAsofJoin, "VecAsofJoin"}, {T_VecMaterial, "VecMaterial"}, {T_VecSort, "VecSort"}, {T_VecGroup, "VecGroup"}, @@ -570,6 +573,7 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, {T_RowToVecState, "RowToVecState"}, {T_VecAggState, "VecAggState"}, {T_VecHashJoinState, "VecHashJoinState"}, + {T_VecAsofJoinState, "VecAsofJoinState"}, {T_VecStreamState, "VecStreamState"}, {T_VecSortState, "VecSortState"}, {T_VecForeignScanState, "VecForeignScanState"}, diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index 68c7a34ed2aa06657156088a34e49015bfc916a9..403b414c8f31a83f5f11dced0e60f5e37d341c40 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -579,7 +579,7 @@ static void _outPlannedStmt(StringInfo str, PlannedStmt* node) WRITE_INT_FIELD(gather_count); WRITE_INT_FIELD(num_nodes); - if ((t_thrd.proc->workingVersionNum < 92097 || node->num_streams > 0 || IS_SPQ_RUNNING) && + if ((t_thrd.proc->workingVersionNum < 92097 || node->num_streams > 0 || IS_SPQ_RUNNING) && node->nodesDefinition != NULL) { for (int i = 0; i < node->num_nodes; i++) { /* Write the field name only one time and just append the value of each field */ @@ -1818,6 +1818,17 @@ static void _outVecHashJoin(StringInfo str, VecHashJoin* node) out_mem_info(str, &node->mem_info); } +static void _outVecAsofJoin(StringInfo str, VecAsofJoin* node) +{ + WRITE_NODE_TYPE("VECASOFJOIN"); + + _outJoinPlanInfo(str, (Join*)node); + + WRITE_NODE_FIELD(hashclauses); + WRITE_NODE_FIELD(mergeclauses); + WRITE_BOOL_FIELD(streamBothSides); +} + static void _outVecHashAgg(StringInfo str, VecAgg* node) { int i; @@ -3278,6 +3289,9 @@ static void _outJoinExpr(StringInfo str, JoinExpr* node) if (t_thrd.proc->workingVersionNum >= APPLY_JOIN_VERSION_NUMBER) { WRITE_BOOL_FIELD(is_apply_join); } + if (t_thrd.proc->workingVersionNum >= ASOFJOIN_VERSION_NUM) { + WRITE_BOOL_FIELD(isAsof); + } } static void _outFromExpr(StringInfo str, FromExpr* node) @@ -3582,6 +3596,16 @@ static void _outHashPath(StringInfo str, HashPath* node) WRITE_INT_FIELD(num_batches); } +static void _outAsofPath(StringInfo str, AsofPath* node) +{ + WRITE_NODE_TYPE("ASOFPATH"); + + _outJoinPathInfo(str, (JoinPath*)node); + + WRITE_NODE_FIELD(path_hashclauses); + WRITE_NODE_FIELD(path_mergeclauses); +} + static void _outPlannerGlobal(StringInfo str, PlannerGlobal* node) { WRITE_NODE_TYPE("PLANNERGLOBAL"); @@ -6144,7 +6168,7 @@ static void _outVecLimit(StringInfo str, VecLimit* node) } for (size_t i = 0; i < node->numCols; i++) { - if (node->collations[i] >= FirstBootstrapObjectId + if (node->collations[i] >= FirstBootstrapObjectId && IsStatisfyUpdateCompatibility(node->collations[i])) { appendStringInfo(str, " :collname "); _outToken(str, get_collation_name(node->collations[i])); @@ -6999,6 +7023,9 @@ static void _outNode(StringInfo str, const void* obj) case T_HashPath: _outHashPath(str, (HashPath*)obj); break; + case T_AsofPath: + _outAsofPath(str, (AsofPath*)obj); + break; case T_PlannerGlobal: _outPlannerGlobal(str, (PlannerGlobal*)obj); break; @@ -7382,6 +7409,11 @@ static void _outNode(StringInfo str, const void* obj) _outVecHashJoin(str, (VecHashJoin*)obj); break; + case T_AsofJoin: + case T_VecAsofJoin: + _outVecAsofJoin(str, (VecAsofJoin*)obj); + break; + case T_VecAgg: _outVecHashAgg(str, (VecAgg*)obj); break; diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index 677d25ff1233e6e398cac346efe138358021e443..e4c191fc02e1e2eed49e70455f5a7de5642628a0 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -857,6 +857,22 @@ THR_LOCAL bool skip_read_extern_fields = false; READ_DONE(); \ } while (0) +/* + * function for _readAsofJoin and _readVecAsofJoin. + */ +#define READ_ASOFJOIN_FIELD() \ + do { \ + READ_TEMP_LOCALS(); \ + \ + /* Read Join */ \ + _readJoin(&local_node->join); \ + \ + READ_NODE_FIELD(hashclauses); \ + READ_NODE_FIELD(mergeclauses); \ + READ_BOOL_FIELD(streamBothSides); \ + READ_DONE(); \ + } while (0) + /* * function for _readMergeJoin and _readVecMergeJoin. */ @@ -900,10 +916,10 @@ THR_LOCAL bool skip_read_extern_fields = false; } while (0) static Datum readDatum(bool typbyval); -static Scan* _readScan(Scan* local_node); +static Scan *_readScan(Scan *local_node); extern bool StreamTopConsumerAmI(); -static void read_mem_info(OpMemInfo* local_node); -static void _readCursorData(Cursor_Data* local_node); +static void read_mem_info(OpMemInfo *local_node); +static void _readCursorData(Cursor_Data *local_node); /* * _readBitmapset @@ -3230,6 +3246,9 @@ static JoinExpr* _readJoinExpr(void) IF_EXIST(is_apply_join) { READ_BOOL_FIELD(is_apply_join); } + IF_EXIST(isAsof) { + READ_BOOL_FIELD(isAsof); + } READ_DONE(); } @@ -4784,7 +4803,13 @@ static HashJoin* _readHashJoin(HashJoin* local_node) READ_DONE(); } -static MergeJoin* _readMergeJoin(MergeJoin* local_node) +static AsofJoin *_readAsofJoin(AsofJoin *local_node) +{ + READ_LOCALS_NULL(AsofJoin); + READ_ASOFJOIN_FIELD(); +} + +static MergeJoin *_readMergeJoin(MergeJoin *local_node) { READ_LOCALS_NULL(MergeJoin); READ_MERGEJOIN(); @@ -5646,7 +5671,13 @@ static VecHashJoin* _readVecHashJoin(VecHashJoin* local_node) READ_HASHJOIN_FIELD(); } -static VecSetOp* _readVecSetOp(VecSetOp* local_node) +static VecAsofJoin *_readVecAsofJoin(VecAsofJoin *local_node) +{ + READ_LOCALS_NULL(VecAsofJoin); + READ_ASOFJOIN_FIELD(); +} + +static VecSetOp *_readVecSetOp(VecSetOp *local_node) { READ_LOCALS_NULL(VecSetOp); READ_TEMP_LOCALS(); @@ -6921,6 +6952,8 @@ Node* parseNodeString(void) return_value = _readHash(NULL); } else if (MATCH("HASHJOIN", 8)) { return_value = _readHashJoin(NULL); + } else if (MATCH("ASOFJOIN", 8)) { + return_value = _readAsofJoin(NULL); } else if (MATCH("MERGEJOIN", 9)) { return_value = _readMergeJoin(NULL); } else if (MATCH("REMOTEQUERY", 11)) { @@ -7057,6 +7090,8 @@ Node* parseNodeString(void) return_value = _readVecSubqueryScan(NULL); } else if (MATCH("VECHASHJOIN", 11)) { return_value = _readVecHashJoin(NULL); + } else if (MATCH("VECASOFJOIN", 11)) { + return_value = _readVecAsofJoin(NULL); } else if (MATCH("VECAGG", 6)) { return_value = _readVecAgg(NULL); } else if (MATCH("VECPARTITERATOR", 15)) { diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 8c59ee6a53253cacd54e129af27d18180cbb9bc5..3da9e7eabb853331d40d963787e700f17df571e3 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -559,7 +559,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); target_list insert_column_list set_target_list rename_clause_list rename_clause set_clause_list set_clause multiple_set_clause ctext_expr_list ctext_row def_list tsconf_def_list indirection opt_indirection - reloption_list tblspc_option_list cfoption_list group_clause TriggerFuncArgs + reloption_list tblspc_option_list cfoption_list group_clause TriggerFuncArgs opt_delete_limit opclass_item_list opclass_drop_list opclass_purpose opt_opfamily transaction_mode_list_or_empty OptTableFuncElementList TableFuncElementList opt_type_modifiers @@ -922,7 +922,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); /* ordinary key words in alphabetical order */ /* PGXC - added DISTRIBUTE, DIRECT, COORDINATOR, CLEAN, NODE, BARRIER, SLICE, DATANODE */ %token ABORT_P ABSOLUTE_P ACCESS ACCOUNT ACTION ADD_P ADMIN AFTER - AGGREGATE ALGORITHM ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APP APPEND APPLY ARCHIVE ARRAY AS ASC + AGGREGATE ALGORITHM ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APP APPEND APPLY ARCHIVE ARRAY AS ASC ASOF_P ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUDIT AUTHID AUTHORIZATION AUTOEXTEND AUTOMAPPED AUTO_INCREMENT BACKWARD BARRIER BEFORE BEGIN_NON_ANOYBLOCK BEGIN_P BETWEEN BIGINT BINARY BINARY_DOUBLE BINARY_DOUBLE_INF BINARY_DOUBLE_NAN BINARY_INTEGER BIT BLANKS @@ -1130,7 +1130,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); * They wouldn't be given a precedence at all, were it not that we need * left-associativity among the JOIN rules themselves. */ -%left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL ENCRYPTED APPLY OUTER_P +%left JOIN CROSS LEFT FULL RIGHT ASOF_P INNER_P NATURAL ENCRYPTED APPLY OUTER_P /* kluge to keep xml_whitespace_option from causing shift/reduce conflicts */ %right PRESERVE STRIP_P %token CONSTRUCTOR FINAL MAP MEMBER RESULT SELF STATIC_P UNDER @@ -12084,7 +12084,7 @@ DropSynonymStmt: (errcode(ERRCODE_SYNTAX_ERROR), errmsg("missing or invalid synonym identifier"))); } - + DropSynonymStmt *n = makeNode(DropSynonymStmt); n->isPublic = isPublic; n->synName = synName; @@ -12097,7 +12097,7 @@ DropSynonymStmt: opt_public: IDENT - { + { if (strcasecmp($1, "public") == 0) { $$ = true; } else { @@ -12922,14 +12922,14 @@ DefineStmt: | CREATE TYPE_P any_name as_is OBJECT_P '(' OptTableFuncElementList ')' final_clause { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } if ($7 == NIL) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("no attributes found in object type \"%s\"",strVal(linitial($3))))); + errmsg("no attributes found in object type \"%s\"",strVal(linitial($3))))); } CompositeTypeStmt *n = makeNode(CompositeTypeStmt); base_yy_extra_type *yyextra = pg_yyget_extra(yyscanner); @@ -12948,19 +12948,19 @@ DefineStmt: appendBinaryStringInfo(&typespec, yyextra->core_yy_extra.scanbuf + @6, @8-@6+1); n->typespec = typespec.data; n->typebody = NULL; - $$ = (Node *)n; + $$ = (Node *)n; } | CREATE OR REPLACE TYPE_P any_name as_is OBJECT_P '(' OptTableFuncElementList ')' final_clause { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } if ($9 == NIL) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("no attributes found in object type \"%s\"",strVal(linitial($5))))); + errmsg("no attributes found in object type \"%s\"",strVal(linitial($5))))); } CompositeTypeStmt *n = makeNode(CompositeTypeStmt); base_yy_extra_type *yyextra = pg_yyget_extra(yyscanner); @@ -12979,12 +12979,12 @@ DefineStmt: appendBinaryStringInfo(&typespec, yyextra->core_yy_extra.scanbuf + @8, @10-@8+1); n->typespec = typespec.data; n->typebody = NULL; - $$ = (Node *)n; + $$ = (Node *)n; } | CREATE TYPE_P any_name as_is OBJECT_P '(' TableFuncElementList ',' Method_specList ')' final_clause { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } @@ -13005,12 +13005,12 @@ DefineStmt: appendBinaryStringInfo(&typespec, yyextra->core_yy_extra.scanbuf + @6, @10-@6+1); n->typespec = typespec.data; n->typebody = NULL; - $$ = (Node *)n; + $$ = (Node *)n; } | CREATE OR REPLACE TYPE_P any_name as_is OBJECT_P '(' TableFuncElementList ',' Method_specList ')' final_clause { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } @@ -13031,12 +13031,12 @@ DefineStmt: appendBinaryStringInfo(&typespec, yyextra->core_yy_extra.scanbuf + @8, @12-@8+1); n->typespec = typespec.data; n->typebody = NULL; - $$ = (Node *)n; + $$ = (Node *)n; } | CREATE TYPE_P any_name UNDER any_name '(' TableFuncElementList ')' final_clause { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } @@ -13058,12 +13058,12 @@ DefineStmt: appendBinaryStringInfo(&typespec, yyextra->core_yy_extra.scanbuf + @6, @8-@6+1); n->typespec = typespec.data; n->typebody = NULL; - $$ = (Node *)n; + $$ = (Node *)n; } | CREATE OR REPLACE TYPE_P any_name UNDER any_name '(' TableFuncElementList ')' final_clause { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } @@ -13081,16 +13081,16 @@ DefineStmt: n->final = $11; n->issubtype = true; n->methodlist = NULL; - + appendBinaryStringInfo(&typespec, yyextra->core_yy_extra.scanbuf + @8, @10-@8+1); n->typespec = typespec.data; n->typebody = NULL; - $$ = (Node *)n; + $$ = (Node *)n; } | CREATE TYPE_P any_name UNDER any_name '(' TableFuncElementList ',' Method_specList ')' final_clause { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } @@ -13112,12 +13112,12 @@ DefineStmt: appendBinaryStringInfo(&typespec, yyextra->core_yy_extra.scanbuf + @6, @10-@6+1); n->typespec = typespec.data; n->typebody = NULL; - $$ = (Node *)n; + $$ = (Node *)n; } | CREATE OR REPLACE TYPE_P any_name UNDER any_name '(' TableFuncElementList ',' Method_specList ')' final_clause { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } @@ -13144,7 +13144,7 @@ DefineStmt: | CREATE TYPE_P BODY_P any_name as_is type_body_subprogram { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } @@ -13166,7 +13166,7 @@ DefineStmt: | CREATE OR REPLACE TYPE_P BODY_P any_name as_is type_body_subprogram { if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT) { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CREATE TYPE AS OBJECT is only support in A-format database"))); } @@ -13313,7 +13313,7 @@ element_spec : inheritance_clause MEMBER_FUNCTION func_name_opt_arg object_proc_ { FunctionParameter* firstparam = (FunctionParameter*)linitial($4); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13340,7 +13340,7 @@ element_spec : inheritance_clause MEMBER_FUNCTION func_name_opt_arg object_proc_ { FunctionParameter* firstparam = (FunctionParameter*)linitial($4); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13359,7 +13359,7 @@ element_spec : inheritance_clause MEMBER_FUNCTION func_name_opt_arg object_proc_ n->options = list; n->withClause = NULL; n->isProcedure = false; - $$ = (Node*)n; + $$ = (Node*)n; } | inheritance_clause MEMBER_PROCEDURE func_name_opt_arg object_proc_args { @@ -13367,7 +13367,7 @@ element_spec : inheritance_clause MEMBER_FUNCTION func_name_opt_arg object_proc_ { FunctionParameter* firstparam = (FunctionParameter*)linitial($4); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13393,7 +13393,7 @@ element_spec : inheritance_clause MEMBER_FUNCTION func_name_opt_arg object_proc_ n->options = list; n->withClause = NULL; n->isProcedure = true; - $$ = (Node*)n; + $$ = (Node*)n; } | inheritance_clause STATIC_PROCEDURE func_name_opt_arg proc_args { @@ -13446,7 +13446,7 @@ element_spec : inheritance_clause MEMBER_FUNCTION func_name_opt_arg object_proc_ { FunctionParameter* firstparam = (FunctionParameter*)linitial($5); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13465,17 +13465,17 @@ element_spec : inheritance_clause MEMBER_FUNCTION func_name_opt_arg object_proc_ n->options = list; n->withClause = NULL; n->isProcedure = false; - $$ = (Node*)n; + $$ = (Node*)n; } | inheritance_clause MAP_MEMBER PROCEDURE func_name_opt_arg object_proc_args { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("Only a function can be a MAP,ORDER or CONSTRUCTOR method"))); + errmsg("Only a function can be a MAP,ORDER or CONSTRUCTOR method"))); } | inheritance_clause ORDER MEMBER_PROCEDURE func_name_opt_arg object_proc_args { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Only a function can be a MAP,ORDER or CONSTRUCTOR method"))); } @@ -13485,7 +13485,7 @@ element_spec : inheritance_clause MEMBER_FUNCTION func_name_opt_arg object_proc_ { FunctionParameter* firstparam = (FunctionParameter*)linitial($5); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13556,7 +13556,7 @@ subprogram_decl_in_type : inheritance_clause MEMBER_FUNCTION func_name_opt_arg o { FunctionParameter* firstparam = (FunctionParameter*)linitial($4); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13630,7 +13630,7 @@ subprogram_decl_in_type : inheritance_clause MEMBER_FUNCTION func_name_opt_arg o { FunctionParameter* firstparam = (FunctionParameter*)linitial($4); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13663,7 +13663,7 @@ subprogram_decl_in_type : inheritance_clause MEMBER_FUNCTION func_name_opt_arg o (Node*)list_make1(makeString(funcSource->bodySrc)))); n->options = lappend(n->options, makeDefElem("language", (Node*)makeString("plpgsql"))); - + n->withClause = NIL; u_sess->parser_cxt.isCreateFuncOrProc = false; n->typfunckind = OBJECTTYPE_MEMBER_PROC; @@ -13706,7 +13706,7 @@ subprogram_decl_in_type : inheritance_clause MEMBER_FUNCTION func_name_opt_arg o (Node*)list_make1(makeString(funcSource->bodySrc)))); n->options = lappend(n->options, makeDefElem("language", (Node*)makeString("plpgsql"))); - + n->withClause = NIL; u_sess->parser_cxt.isCreateFuncOrProc = false; n->typfunckind = OBJECTTYPE_STATIC_PROC; @@ -13762,7 +13762,7 @@ map_order_decl_in_type : inheritance_clause MAP_MEMBER FUNCTION func_name_opt_ar { FunctionParameter* firstparam = (FunctionParameter*)linitial($5); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13799,7 +13799,7 @@ map_order_decl_in_type : inheritance_clause MAP_MEMBER FUNCTION func_name_opt_ar u_sess->parser_cxt.isCreateFuncOrProc = true; } subprogram_body { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Only a function can be a MAP,ORDER or CONSTRUCTOR method"))); } @@ -13814,7 +13814,7 @@ map_order_decl_in_type : inheritance_clause MAP_MEMBER FUNCTION func_name_opt_ar { FunctionParameter* firstparam = (FunctionParameter*)linitial($5); if ((NULL != firstparam->name) && (strcmp(firstparam->name,"self") == 0) && !(firstparam->mode == FUNC_PARAM_IN || firstparam->mode == FUNC_PARAM_INOUT)) - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("the SELF parameter can be declared only as IN or as IN OUT"))); } @@ -13842,7 +13842,7 @@ map_order_decl_in_type : inheritance_clause MAP_MEMBER FUNCTION func_name_opt_ar (Node*)makeString("plpgsql"))); n->withClause = NULL; n->isProcedure = false; - $$ = (Node*)n; + $$ = (Node*)n; } | inheritance_clause ORDER MEMBER_PROCEDURE func_name_opt_arg object_proc_args opt_createproc_opt_list as_is { u_sess->parser_cxt.eaten_declare = false; @@ -13851,9 +13851,9 @@ map_order_decl_in_type : inheritance_clause MAP_MEMBER FUNCTION func_name_opt_ar u_sess->parser_cxt.isCreateFuncOrProc = true; } subprogram_body { - ereport(errstate, + ereport(errstate, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("Only a function can be a MAP,ORDER or CONSTRUCTOR method"))); + errmsg("Only a function can be a MAP,ORDER or CONSTRUCTOR method"))); } ; @@ -17382,7 +17382,7 @@ CreateProcedureStmt: n->definer = $3; if (n->replace && NULL != n->definer) { parser_yyerror("not support DEFINER function"); - } + } n->funcname = $5; n->parameters = $6; n->returnType = NULL; @@ -24912,7 +24912,7 @@ select_no_parens: | with_clause select_clause { insertSelectOptions((SelectStmt *) $2, NULL, NIL, - NULL, + NULL, $1, yyscanner); $$ = $2; @@ -24920,7 +24920,7 @@ select_no_parens: | with_clause select_clause sort_clause { insertSelectOptions((SelectStmt *) $2, $3, NIL, - NULL, + NULL, $1, yyscanner); $$ = $2; @@ -25407,7 +25407,7 @@ sortby: a_expr USING qual_all_Op opt_nulls_order select_limit: limit_clause offset_clause - { + { FetchLimit *limitClause = $1; if (limitClause) { limitClause->limitOffset = $2; @@ -25415,7 +25415,7 @@ select_limit: $$ = limitClause; } | offset_clause limit_clause - { + { FetchLimit *limitClause = $2; if (limitClause) { limitClause->limitOffset = $1; @@ -25424,14 +25424,14 @@ select_limit: } | limit_clause { $$ = $1; } | limit_offcnt_clause - { + { FetchLimit *limitClause = (FetchLimit *)palloc0(sizeof(FetchLimit)); limitClause->limitOffset = (Node *)list_nth($1, 0); limitClause->limitCount = (Node *)list_nth($1, 1); $$ = limitClause; } | offset_clause - { + { FetchLimit *limitClause = (FetchLimit *)palloc0(sizeof(FetchLimit)); limitClause->limitOffset = $1; $$ = limitClause; @@ -25949,7 +25949,7 @@ table_ref_for_no_table_function: relation_expr %prec UMINUS $1->indexhints = $3; $$ = (Node *) $1; } - + | LATERAL_EXPR func_table alias_clause { RangeFunction *n = makeNode(RangeFunction); @@ -26308,6 +26308,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = FALSE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $4; n->usingClause = NIL; @@ -26319,6 +26320,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = $2; n->isNatural = FALSE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $4; if ($5 != NULL && IsA($5, List)) @@ -26333,6 +26335,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = FALSE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $3; if ($4 != NULL && IsA($4, List)) @@ -26341,11 +26344,28 @@ joined_table: n->quals = $4; /* ON clause */ $$ = n; } + + | table_ref ASOF_P JOIN table_ref join_qual + { + /* letting join_type reduce to empty doesn't work */ + JoinExpr *n = makeNode(JoinExpr); + n->jointype = JOIN_INNER; + n->isNatural = FALSE; + n->isAsof = TRUE; + n->larg = $1; + n->rarg = $4; + if ($4 != NULL && IsA($5, List)) + n->usingClause = (List *) $5; /* USING clause */ + else + n->quals = $5; /* ON clause */ + $$ = n; + } | table_ref NATURAL join_type JOIN table_ref { JoinExpr *n = makeNode(JoinExpr); n->jointype = $3; n->isNatural = TRUE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $5; n->usingClause = NIL; /* figure out which columns later... */ @@ -26358,6 +26378,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = TRUE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $4; n->usingClause = NIL; /* figure out which columns later... */ @@ -29235,7 +29256,7 @@ func_application_special: func_name '(' ')' n->args = lappend(n->args, makeNullAConst(-1)); // nls param constraints is NULL n->args = lappend(n->args, makeNullAConst(-1)); - + n->agg_order = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; @@ -29256,8 +29277,8 @@ func_application_special: func_name '(' ')' FuncCall *n = makeNode(FuncCall); n->funcname = $1; - // args: - // input_expr, default_val, + // args: + // input_expr, default_val, // is DEFAULT gramy, default expr is column ref, // fmt constraints, nls param constraints n->args = lappend($3, $5); @@ -29269,7 +29290,7 @@ func_application_special: func_name '(' ')' n->args = lappend(n->args, $9); // nls param constraints is NULL n->args = lappend(n->args, makeNullAConst(-1)); - + n->agg_order = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; @@ -30181,8 +30202,8 @@ keep_clause: n->rank_first = true; n->keep_order =$5; n->location = @1; - - $$ = n; + + $$ = n; } | KEEP '(' DENSE_RANK LAST_P sort_clause ')' { @@ -30194,12 +30215,12 @@ keep_clause: n->rank_first = false; n->keep_order =$5; n->location = @1; - - $$ = n; + + $$ = n; } | /*EMPTY*/ - { - $$ = NULL; + { + $$ = NULL; } ; @@ -31585,6 +31606,7 @@ unreserved_keyword: | APP | APPEND | ARCHIVE + | ASOF_P | ASSERTION | ASSIGNMENT | AT @@ -33770,7 +33792,7 @@ makeCallFuncStmt(List* funcname,List* parameters, bool is_call) { has_overload_func = true; if (IsPackageFunction(funcname) == false && IsPackageSchemaOid(SchemaNameGetSchemaOid(schemaname, true)) == false && - (schemaname == NULL || strncmp(schemaname, "gms_lob", strlen("gms_lob")))) + (schemaname == NULL || strncmp(schemaname, "gms_lob", strlen("gms_lob")))) { const char* message = "function isn't exclusive "; InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); diff --git a/src/common/backend/utils/adt/ruleutils.cpp b/src/common/backend/utils/adt/ruleutils.cpp index 2defb6b78742cb360f22f97ef1fdb87a907af825..e8c2d995e857a03c411a4963357eb27284d7b3b6 100644 --- a/src/common/backend/utils/adt/ruleutils.cpp +++ b/src/common/backend/utils/adt/ruleutils.cpp @@ -4853,7 +4853,7 @@ char* pg_get_functiondef_worker(Oid funcid, int* headerlines) appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(", quote_qualified_identifier(nsp, pkgname, name)); } else if (typname != NULL) { - appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(", + appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(", quote_qualified_identifier(nsp, typname, name)); } else if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) { appendStringInfo(&buf, "CREATE DEFINER = %s FUNCTION %s(", @@ -11382,7 +11382,7 @@ static void get_windowfunc_expr(WindowFunc* wfunc, deparse_context* context) } funcname = generate_function_name(wfunc->winfnoid, nargs, argnames, argtypes, false, NULL); - + if (pg_strcasecmp(funcname,"\"nth_value\"") == 0) { funcname = "nth_value"; } @@ -12247,6 +12247,8 @@ static void get_from_clause_item(Node* jtnode, Query* query, deparse_context* co case JOIN_INNER: if (j->quals) { appendContextKeyword(context, " JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); + } else if (j->isAsof) { + appendContextKeyword(context, " ASOF JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); } else { if (j->is_apply_join) { appendContextKeyword(context, " CROSS APPLY ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 1); diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index ea3004f4c151c2904579082a4c82b02528bfcfcc..cb2f916924d4a45e3f07aae80636da225b50d251 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -76,7 +76,7 @@ bool will_shutdown = false; * NEXT | 93000 | ? | ? * ********************************************/ -const uint32 GRAND_VERSION_NUM = 93027; +const uint32 GRAND_VERSION_NUM = 93028; /******************************************** * 2.VERSION NUM FOR EACH FEATURE @@ -84,6 +84,7 @@ const uint32 GRAND_VERSION_NUM = 93027; ********************************************/ const uint32 HASH_SAOP_VERSION_NUMBER = 93027; const uint32 CAST_FUNC_VERSION_NUMBER = 93026; +const uint32 ASOFJOIN_VERSION_NUM = 93028; const uint32 IGNORE_NULLS_VERSION_NUMBER = 93025; const uint32 DATAVEC_VERSION_NUMBER = 93019; const uint32 HTAP_VERSION_NUMBER = 93015; diff --git a/src/common/interfaces/libpq/frontend_parser/gram.y b/src/common/interfaces/libpq/frontend_parser/gram.y index e9e2523249a90b4a1e1877457403693fd1a5e1ae..773a4c7f9e478a5054e83af1669bb993eea97f38 100755 --- a/src/common/interfaces/libpq/frontend_parser/gram.y +++ b/src/common/interfaces/libpq/frontend_parser/gram.y @@ -526,7 +526,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; /* ordinary key words in alphabetical order */ /* PGXC - added DISTRIBUTE, DIRECT, COORDINATOR, CLEAN, NODE, BARRIER */ %token ABORT_P ABSOLUTE_P ACCESS ACCOUNT ACTION ADD_P ADMIN AFTER - AGGREGATE ALGORITHM ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APP APPEND APPLY ARCHIVE ARRAY AS ASC + AGGREGATE ALGORITHM ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APP APPEND APPLY ARCHIVE ARRAY AS ASC ASOF_P ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUDIT AUDIT_POLICY AUTHID AUTHORIZATION AUTOEXTEND AUTOMAPPED AUTO_INCREMENT BACKWARD BARRIER BEFORE BEGIN_NON_ANOYBLOCK BEGIN_P BETWEEN BIGINT BINARY BINARY_DOUBLE BINARY_DOUBLE_INF BINARY_DOUBLE_NAN BINARY_INTEGER BIT BLANKS BLOB_P BLOCKCHAIN BODY_P BOGUS @@ -597,7 +597,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; RANDOMIZED RANGE RATIO RAW READ REAL REASSIGN REBUILD RECHECK RECURSIVE RECYCLEBIN REDISANYVALUE REF REFERENCES REFRESH REINDEX REJECT_P RELATIVE_P RELEASE RELOPTIONS REMOTE_P REMOVE RENAME REPEAT REPEATABLE REPLACE REPLICA - RESET RESIZE RESOURCE RESPECT_P RESTART RESTRICT RETURN RETURNED_SQLSTATE RETURNING RETURNS REUSE REVOKE RIGHT ROLE ROLES ROLLBACK ROLLUP ROTATE + RESET RESIZE RESOURCE RESPECT_P RESTART RESTRICT RETURN RETURNED_SQLSTATE RETURNING RETURNS REUSE REVOKE RIGHT ROLE ROLES ROLLBACK ROLLUP ROTATE ROTATION ROW ROW_COUNT ROWNUM ROWS ROWTYPE_P RULE SAMPLE SAVEPOINT SCHEDULE SCHEMA SCHEMA_NAME SCROLL SEARCH SECOND_P SECURITY SELECT SEPARATOR_P SEQUENCE SEQUENCES @@ -708,7 +708,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; * They wouldn't be given a precedence at all, were it not that we need * left-associativity among the JOIN rules themselves. */ -%left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL ENCRYPTED +%left JOIN CROSS LEFT FULL RIGHT ASOF_P INNER_P NATURAL ENCRYPTED /* kluge to keep xml_whitespace_option from causing shift/reduce conflicts */ %right PRESERVE STRIP_P %token CONSTRUCTOR FINAL MAP MEMBER RESULT SELF STATIC_P UNDER @@ -7781,7 +7781,7 @@ sortby: a_expr USING qual_all_Op opt_nulls_order select_limit: limit_clause offset_clause - { + { FetchLimit *limitClause = $1; if (limitClause) { limitClause->limitOffset = $2; @@ -7789,7 +7789,7 @@ select_limit: $$ = limitClause; } | offset_clause limit_clause - { + { FetchLimit *limitClause = $2; if (limitClause) { limitClause->limitOffset = $1; @@ -7798,7 +7798,7 @@ select_limit: } | limit_clause { $$ = $1; } | offset_clause - { + { FetchLimit *limitClause = (FetchLimit *)feparser_malloc0(sizeof(FetchLimit)); limitClause->limitOffset = $1; $$ = limitClause; @@ -8289,6 +8289,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = FALSE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $4; n->usingClause = NIL; @@ -8300,6 +8301,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = $2; n->isNatural = FALSE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $4; if ($5 != NULL && IsA($5, List)) @@ -8314,6 +8316,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = FALSE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $3; if ($4 != NULL && IsA($4, List)) @@ -8327,6 +8330,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = $3; n->isNatural = TRUE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $5; n->usingClause = NIL; /* figure out which columns later... */ @@ -8339,6 +8343,7 @@ joined_table: JoinExpr *n = makeNode(JoinExpr); n->jointype = JOIN_INNER; n->isNatural = TRUE; + n->isAsof = FALSE; n->larg = $1; n->rarg = $4; n->usingClause = NIL; /* figure out which columns later... */ @@ -11790,6 +11795,7 @@ unreserved_keyword: | APP | APPEND | APPLY + | ASOF_P | ASSERTION | ASSIGNMENT | AT diff --git a/src/gausskernel/cbb/workload/memctl.cpp b/src/gausskernel/cbb/workload/memctl.cpp index 49e9eba81eb655762da476768513620fb9b7dbae..4ef500d3dc9b7bc8475c5039526cc90941abf9cb 100644 --- a/src/gausskernel/cbb/workload/memctl.cpp +++ b/src/gausskernel/cbb/workload/memctl.cpp @@ -343,6 +343,8 @@ static bool IsMemoryIntensiveOperator(Node* node) case T_Sort: case T_HashJoin: case T_VecHashJoin: + case T_AsofJoin: + case T_VecAsofJoin: case T_VecSort: return true; /* only hashagg setop is memory intensive */ diff --git a/src/gausskernel/optimizer/commands/explain.cpp b/src/gausskernel/optimizer/commands/explain.cpp index 18906f6779bc1655117729da378b5351bc046a61..b6c1fe54af7df2c0a28207a7a09f93b0acd73696 100755 --- a/src/gausskernel/optimizer/commands/explain.cpp +++ b/src/gausskernel/optimizer/commands/explain.cpp @@ -2299,6 +2299,8 @@ static void ExplainNode( case T_VecNestLoop: case T_VecMergeJoin: case T_MergeJoin: + case T_AsofJoin: + case T_VecAsofJoin: case T_HashJoin: case T_VecHashJoin: { const char* jointype = NULL; @@ -2639,6 +2641,7 @@ static void ExplainNode( switch (nodeTag(plan)) { case T_NestLoop: case T_MergeJoin: + case T_AsofJoin: case T_HashJoin: if (es->format != EXPLAIN_FORMAT_TEXT || (es->verbose && ((Join *) plan)->inner_unique)) ExplainProperty("Inner Unique", ((Join *) plan)->inner_unique?"true":"false", true, es); @@ -2960,6 +2963,13 @@ static void ExplainNode( } show_vechash_info((VecHashJoinState*)planstate, es); break; + + case T_AsofJoin: + case T_VecAsofJoin: { + show_upper_qual(((VecAsofJoin*)plan)->hashclauses, "Part Cond", planstate, ancestors, es); + show_upper_qual(((VecAsofJoin*)plan)->join.joinqual, "Merge Cond", planstate, ancestors, es); + show_skew_optimization(planstate, es); + } break; case T_VecAgg: case T_Agg: show_groupby_keys((AggState*)planstate, ancestors, es); @@ -3446,6 +3456,8 @@ static void CalculateProcessedRows( case T_VecNestLoop: case T_VecMergeJoin: case T_MergeJoin: + case T_AsofJoin: + case T_VecAsofJoin: case T_HashJoin: case T_VecHashJoin: *processed_rows = (*inner_rows) + (*outter_rows); @@ -3766,6 +3778,8 @@ static void show_skew_optimization(const PlanState* planstate, ExplainState* es) case T_VecNestLoop: case T_HashJoin: case T_VecHashJoin: + case T_AsofJoin: + case T_VecAsofJoin: case T_MergeJoin: case T_VecMergeJoin: skew_opt = ((Join*)planstate->plan)->skewoptimize; @@ -10469,6 +10483,12 @@ void PlanTable::set_plan_table_join_options(Plan* plan, char** options) if (hashjoin->hashclauses == NULL) *options = "CARTESIAN"; } break; + case T_AsofJoin: + case T_VecAsofJoin: { + VecAsofJoin* asofjoin = (VecAsofJoin*)plan; + if (asofjoin->hashclauses == NULL || asofjoin->mergeclauses == NULL) + *options = "CARTESIAN"; + } break; default: break; } diff --git a/src/gausskernel/optimizer/path/allpaths.cpp b/src/gausskernel/optimizer/path/allpaths.cpp index 2c3b23cb43ff3eefcae68064365061b5ee0c751b..9c5de4959d62f0897b035a2d52ce9afbbeb017fc 100755 --- a/src/gausskernel/optimizer/path/allpaths.cpp +++ b/src/gausskernel/optimizer/path/allpaths.cpp @@ -4578,6 +4578,9 @@ static void print_path(PlannerInfo* root, Path* path, int indent) case T_MergeJoin: join = true; break; + case T_AsofJoin: + join = true; + break; case T_HashJoin: join = true; break; diff --git a/src/gausskernel/optimizer/path/costsize.cpp b/src/gausskernel/optimizer/path/costsize.cpp index 9cebf508a9caf5098b14e438e5d1b0e5234fd59b..b5bf5e95392b2a6b70cdac6d419df4886351a17b 100755 --- a/src/gausskernel/optimizer/path/costsize.cpp +++ b/src/gausskernel/optimizer/path/costsize.cpp @@ -4596,6 +4596,342 @@ void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* w } } +/* + * initial_cost_asofjoin + * Preliminary estimate of the cost of a asofjoin path. + * + * This must quickly produce lower-bound estimates of the path's startup and + * total costs. If we are unable to eliminate the proposed path from + * consideration using the lower bounds, final_cost_asofjoin will be called + * to obtain the final estimates. + * + * 'workspace' is to be filled with startup_cost, total_cost, and perhaps + * other data to be used by final_cost_hashjoin + * 'jointype' is the type of join to be performed + * 'hashclauses' is the list of joinclauses to be used as hash clauses + * 'outer_path' is the outer input to the join + * 'inner_path' is the inner input to the join + * 'extra' contains miscellaneous information about the join + */ +void initial_cost_asofjoin(PlannerInfo *root, JoinCostWorkspace *workspace, JoinType jointype, List *hashclauses, + Path *outer_path, Path *inner_path, List *mergeclauses, List *outersortkeys, + List *innersortkeys, JoinPathExtraData *extra, int dop) +{ + const uint8 ASOFJOIN_OP_ARG_NUM = 2; + Assert(dop != 0); + Cost startup_cost = 0; + Cost run_cost = 0; + double outer_path_rows = PATH_LOCAL_ROWS(outer_path) / dop; + double inner_path_rows = PATH_LOCAL_ROWS(inner_path) / dop; + StreamPath *inner_stream = NULL; + StreamPath *outer_stream = NULL; + int num_hashclauses = list_length(hashclauses); + + VariableStatData vardata1; + VariableStatData vardata2; + bool isdefault1 = false; + bool isdefault2 = false; + double inner_distinct_num = 0; + double outer_distinct_num = 0; + bool join_is_reversed = false; + OpExpr *opclause = NULL; + + double outer_rows, inner_rows, outer_skip_rows, inner_skip_rows; + Selectivity outerstartsel, outerendsel, innerstartsel, innerendsel; + Path sort_path; /* dummy for result of cost_sort */ + + errno_t rc = 0; + + rc = memset_s(&workspace->outer_mem_info, sizeof(OpMemInfo), 0, sizeof(OpMemInfo)); + securec_check(rc, "\0", "\0"); + rc = memset_s(&workspace->inner_mem_info, sizeof(OpMemInfo), 0, sizeof(OpMemInfo)); + securec_check(rc, "\0", "\0"); + + RestrictInfo *firstclause = (RestrictInfo *)linitial(hashclauses); + + /* Deconstruct the hash clause */ + if (is_opclause(firstclause->clause)) { + opclause = (OpExpr *)firstclause->clause; + if (list_length(opclause->args) == ASOFJOIN_OP_ARG_NUM) { + get_join_variables(root, opclause->args, extra->sjinfo, &vardata1, &vardata2, &join_is_reversed); + double n1 = get_variable_numdistinct(&vardata1, &isdefault1, true, get_join_ratio(&vardata1, extra->sjinfo), + extra->sjinfo, STATS_TYPE_GLOBAL, true); + double n2 = get_variable_numdistinct(&vardata2, &isdefault2, true, get_join_ratio(&vardata2, extra->sjinfo), + extra->sjinfo, STATS_TYPE_GLOBAL, true); + ReleaseVariableStats(vardata1); + ReleaseVariableStats(vardata2); + + if (join_is_reversed) { + outer_distinct_num = n2; + inner_distinct_num = n1; + } else { + outer_distinct_num = n1; + inner_distinct_num = n2; + } + + if (IsA(inner_path, StreamPath)) { + inner_stream = (StreamPath *)inner_path; + if (inner_stream->type == STREAM_REDISTRIBUTE) { + inner_path_rows /= inner_distinct_num; + } + } + + if (IsA(outer_path, StreamPath)) { + outer_stream = (StreamPath *)outer_path; + if (outer_stream->type == STREAM_REDISTRIBUTE) { + outer_path_rows /= outer_distinct_num; + } + } + } + } + + /* Protect some assumptions below that rowcounts aren't zero or NaN */ + if (outer_path_rows <= 0 || isnan(outer_path_rows)) + outer_path_rows = 1; + if (inner_path_rows <= 0 || isnan(inner_path_rows)) + inner_path_rows = 1; + + /* cost of source data */ + startup_cost += outer_path->startup_cost; + run_cost += outer_path->total_cost - outer_path->startup_cost; + + startup_cost += inner_path->startup_cost; + run_cost += inner_path->total_cost - inner_path->startup_cost; + + ereport(DEBUG2, (errmodule(MOD_OPT_JOIN), + errmsg("Source data cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); + + /* sours + * Cost of computing hash function: must do it once per input tuple. We + * charge one cpu_operator_cost for each column's hash function. + * + * XXX when a hashclause is more complex than a single operator, we really + * should charge the extra eval costs of the left or right side, as + * appropriate, here. This seems more work than it's worth at the moment. + */ + startup_cost += (u_sess->attr.attr_sql.cpu_operator_cost * num_hashclauses) * inner_path_rows; + run_cost += u_sess->attr.attr_sql.cpu_operator_cost * num_hashclauses * outer_path_rows; + + ereport(DEBUG2, (errmodule(MOD_OPT_JOIN), + errmsg("Add asofjoin function cost: startup_cost: %lf, run_cost: %lf", startup_cost, run_cost))); + + /* + * A merge join will stop as soon as it exhausts either input stream + * (unless it's an outer join, in which case the outer side has to be + * scanned all the way anyway). Estimate fraction of the left and right + * inputs that will actually need to be scanned. Likewise, we can + * estimate the number of rows that will be skipped before the first join + * pair is found, which should be factored into startup cost. We use only + * the first (most significant) merge clause for this purpose. Since + * mergejoinscansel() is a fairly expensive computation, we cache the + * results in the merge clause RestrictInfo. + */ + if (mergeclauses != NIL && outersortkeys != NIL && innersortkeys != NIL) { + RestrictInfo *firstclause = (RestrictInfo *)linitial(mergeclauses); + List *opathkeys = NIL; + List *ipathkeys = NIL; + PathKey *opathkey = NULL; + PathKey *ipathkey = NULL; + MergeScanSelCache *cache = NULL; + + /* Get the input pathkeys to determine the sort-order details */ + opathkeys = outersortkeys; + ipathkeys = innersortkeys; + AssertEreport(opathkeys != NIL, MOD_OPT, + "The outer pathkeys is null when determining the cost of performing a mergejoin path."); + AssertEreport(ipathkeys != NIL, MOD_OPT, + "The inner pathkeys is null when determining the cost of performing a mergejoin path."); + + opathkey = (PathKey *)linitial(opathkeys); + ipathkey = (PathKey *)linitial(ipathkeys); + /* debugging check */ + if (!OpFamilyEquals(opathkey->pk_opfamily, ipathkey->pk_opfamily) || + opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation || + opathkey->pk_strategy != ipathkey->pk_strategy || opathkey->pk_nulls_first != ipathkey->pk_nulls_first) + ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE), + errmsg("left and right pathkeys do not match in mergejoin when initlize cost"))); + + /* Get the selectivity with caching */ + cache = cached_scansel(root, firstclause, opathkey); + if (bms_is_subset(firstclause->left_relids, outer_path->parent->relids)) { + /* left side of clause is outer */ + outerstartsel = cache->leftstartsel; + outerendsel = cache->leftendsel; + innerstartsel = cache->rightstartsel; + innerendsel = cache->rightendsel; + } else { + /* left side of clause is inner */ + outerstartsel = cache->rightstartsel; + outerendsel = cache->rightendsel; + innerstartsel = cache->leftstartsel; + innerendsel = cache->leftendsel; + } + + outerstartsel = 0.0; + outerendsel = 1.0; + + /* jointype should not be JOIN_RIGHT_ANTI_FULL, + * because JOIN_RIGHT_ANTI_FULL can not create a mergejoin plan. + */ + AssertEreport(jointype != JOIN_RIGHT_ANTI_FULL, MOD_OPT, + "The mergejoin plan with JOIN_RIGHT_ANTI_FULL is not allowed." + "when determining the cost of performing a mergejoin path."); + } else { + /* cope with clauseless or full mergejoin */ + outerstartsel = innerstartsel = 0.0; + outerendsel = innerendsel = 1.0; + } + + /* + * Convert selectivities to row counts. We force outer_rows and + * inner_rows to be at least 1, but the skip_rows estimates can be zero. + */ + outer_skip_rows = rint(outer_path_rows * outerstartsel); + inner_skip_rows = rint(inner_path_rows * innerstartsel); + outer_rows = clamp_row_est(outer_path_rows * outerendsel); + inner_rows = clamp_row_est(inner_path_rows * innerendsel); + + AssertEreport(outer_skip_rows <= outer_rows, MOD_OPT, + "The estimated skip rows is larger than rounding outer rows which avoid possible divide-by-zero " + "when determining the cost of performing a mergejoin path."); + AssertEreport(inner_skip_rows <= inner_rows, MOD_OPT, + "The estimated skip rows is larger than rounding inner rows which avoid possible divide-by-zero" + "when determining the cost of performing a mergejoin path."); + + /* + * Readjust scan selectivities to account for above rounding. This is + * normally an insignificant effect, but when there are only a few rows in + * the inputs, failing to do this makes for a large percentage error. + */ + outerstartsel = outer_skip_rows / outer_path_rows; + innerstartsel = inner_skip_rows / inner_path_rows; + outerendsel = outer_rows / outer_path_rows; + innerendsel = inner_rows / inner_path_rows; + + AssertEreport( + outerstartsel <= outerendsel, MOD_OPT, + "The selectivities corresponding to estimated skip rows is larger than that of above rounding outer rows" + "when determining the cost of performing a mergejoin path."); + AssertEreport( + innerstartsel <= innerendsel, MOD_OPT, + "The selectivities corresponding to estimated skip rows is larger than that of above rounding inner rows" + "when determining the cost of performing a mergejoin path."); + + /* cost of source data */ + if (outersortkeys) { /* do we need to sort outer? */ + int outer_width = get_path_actual_total_width(outer_path, root->glob->vectorized, OP_SORT); + + cost_sort(&sort_path, outersortkeys, outer_path->total_cost, outer_path_rows, outer_width, 0.0, + u_sess->opt_cxt.op_work_mem, -1.0, root->glob->vectorized, dop, &workspace->outer_mem_info); + startup_cost += sort_path.startup_cost; + startup_cost += (sort_path.total_cost - sort_path.startup_cost) * outerstartsel * outer_distinct_num; + run_cost += + (sort_path.total_cost - sort_path.startup_cost) * (outerendsel - outerstartsel) * outer_distinct_num; + } else { + startup_cost += outer_path->startup_cost; + startup_cost += (outer_path->total_cost - outer_path->startup_cost) * outerstartsel * outer_distinct_num; + run_cost += + (outer_path->total_cost - outer_path->startup_cost) * (outerendsel - outerstartsel) * outer_distinct_num; + } + + if (innersortkeys) { /* do we need to sort inner? */ + int inner_width = get_path_actual_total_width(inner_path, root->glob->vectorized, OP_SORT); + + cost_sort(&sort_path, innersortkeys, inner_path->total_cost, inner_path_rows, inner_width, 0.0, + u_sess->opt_cxt.op_work_mem, -1.0, root->glob->vectorized, dop, &workspace->inner_mem_info); + startup_cost += sort_path.startup_cost; + startup_cost += (sort_path.total_cost - sort_path.startup_cost) * innerstartsel * inner_distinct_num; + run_cost = (sort_path.total_cost - sort_path.startup_cost) * (innerendsel - innerstartsel) * inner_distinct_num; + } else { + startup_cost += inner_path->startup_cost; + startup_cost += (inner_path->total_cost - inner_path->startup_cost) * innerstartsel * inner_distinct_num; + run_cost = + (inner_path->total_cost - inner_path->startup_cost) * (innerendsel - innerstartsel) * inner_distinct_num; + } + + /* CPU costs left for later */ + /* Public result fields */ + workspace->startup_cost = startup_cost; + workspace->total_cost = startup_cost + run_cost; + workspace->run_cost = run_cost; + workspace->outer_rows = outer_rows; + workspace->inner_rows = inner_rows; + workspace->outer_skip_rows = outer_skip_rows; + workspace->inner_skip_rows = inner_skip_rows; + workspace->inner_distinct_num = inner_distinct_num; + workspace->outer_distinct_num = outer_distinct_num; + + ereport( + DEBUG1, + (errmodule(MOD_OPT_JOIN), + errmsg("Initial asofjoin cost: startup_cost: %lf, total_cost: %lf, work_mem: %d, esti_work_mem: %d", + workspace->startup_cost, workspace->total_cost, u_sess->opt_cxt.op_work_mem, root->glob->estiopmem))); +} + +/* + * final_cost_asofjoin + * Final estimate of the cost and result size of a asofjoin path. + * + * 'path' is already filled in except for the rows and cost fields and + * skip_mark_restore and materialize_inner + * 'workspace' is the result from initial_cost_asofjoin + * 'extra' contains miscellaneous information about the join + */ +void final_cost_asofjoin(PlannerInfo *root, AsofPath *path, JoinCostWorkspace *workspace, JoinPathExtraData *extra, + int dop) +{ + Assert(dop != 0); + Path *outer_path = path->jpath.outerjoinpath; + Path *inner_path = path->jpath.innerjoinpath; + List *mergeclauses = path->path_mergeclauses; + Cost startup_cost = workspace->startup_cost; + Cost run_cost = workspace->run_cost; + double outer_rows = workspace->outer_rows; + double inner_rows = workspace->inner_rows; + double outer_skip_rows = workspace->outer_skip_rows; + double inner_skip_rows = workspace->inner_skip_rows; + double inner_distinct_num = workspace->inner_distinct_num; + double outer_distinct_num = workspace->outer_distinct_num; + QualCost merge_qual_cost; + + /* Mark the path with the correct row estimate */ + set_rel_path_rows(&path->jpath.path, path->jpath.path.parent, path->jpath.path.param_info); + + /* + * If inner_path or outer_path is EC functioinScan without stream, + * we should set the multiple particularly. + */ + set_joinpath_multiple_for_EC(root, &path->jpath.path, outer_path, inner_path); + + cost_qual_eval(&merge_qual_cost, mergeclauses, root); + + /* + * For each tuple that gets through the mergejoin proper, we charge + * cpu_tuple_cost plus the cost of evaluating additional restriction + * clauses that are to be applied at the join. (This is pessimistic since + * not all of the quals may get evaluated at each tuple.) + * + * Note: we could adjust for SEMI/ANTI joins skipping some qual + * evaluations here, but it's probably not worth the trouble. + */ + startup_cost += merge_qual_cost.startup; + startup_cost += + merge_qual_cost.per_tuple * (outer_skip_rows * outer_distinct_num + inner_skip_rows * inner_distinct_num); + run_cost += merge_qual_cost.per_tuple * ((outer_rows - outer_skip_rows) * outer_distinct_num + + (inner_rows - inner_skip_rows) * inner_distinct_num); + + copy_mem_info(&path->outer_mem_info, &workspace->outer_mem_info); + copy_mem_info(&path->inner_mem_info, &workspace->inner_mem_info); + + path->jpath.path.startup_cost = startup_cost / dop; + path->jpath.path.total_cost = (startup_cost + run_cost) / dop; + path->jpath.path.stream_cost = outer_path->stream_cost; + + ereport(DEBUG2, (errmodule(MOD_OPT_JOIN), + errmsg("final cost asof join: stream_cost: %lf, startup_cost: %lf, total_cost: %lf", + path->jpath.path.stream_cost, path->jpath.path.startup_cost, path->jpath.path.total_cost))); +} + /* * cost_subplan * Figure the costs for a SubPlan (or initplan). diff --git a/src/gausskernel/optimizer/path/equivclass.cpp b/src/gausskernel/optimizer/path/equivclass.cpp index 9d57b912a29876b73189795bcadd4c555813f2ee..5d131bf6821c5d55a0b8a6cfe818019e9e226432 100644 --- a/src/gausskernel/optimizer/path/equivclass.cpp +++ b/src/gausskernel/optimizer/path/equivclass.cpp @@ -1092,13 +1092,22 @@ List* generate_join_implied_equalities_for_ecs( foreach (lc, eclasses) { EquivalenceClass* ec = (EquivalenceClass*)lfirst(lc); List* sublist = NIL; + bool checkAsof = false; + ListCell *jr = NULL; + foreach (jr, ec->ec_sources) { + RestrictInfo *ri = (RestrictInfo *)lfirst(jr); + if (ri->is_asof) { + checkAsof = true; + break; + } + } /* ECs containing consts do not need any further enforcement */ #ifdef STREAMPLAN - if (!IS_STREAM_PLAN && ec->ec_has_const) + if (!IS_STREAM_PLAN && ec->ec_has_const && !checkAsof) continue; #else - if (ec->ec_has_const) + if (ec->ec_has_const && !checkAsof) continue; #endif @@ -1136,6 +1145,17 @@ List* generate_join_implied_equalities_normal( List* inner_members = NIL; ListCell* lc1 = NULL; bool has_const = false; + bool check_asof = false; + ListCell *jr = NULL; + + foreach (jr, ec->ec_sources) { + RestrictInfo *ri = (RestrictInfo *)lfirst(jr); + if (ri->is_asof) { + check_asof = true; + break; + } + } + /* * First, scan the EC to identify member values that are computable at the @@ -1150,7 +1170,7 @@ List* generate_join_implied_equalities_normal( EquivalenceMember* cur_em = (EquivalenceMember*)lfirst(lc1); #ifdef STREAMPLAN - if (IS_STREAM_PLAN && cur_em->em_is_const) { + if (IS_STREAM_PLAN && cur_em->em_is_const && !check_asof) { has_const = true; continue; } @@ -2331,4 +2351,3 @@ void delete_eq_member(PlannerInfo* root, List* tlist, List* collectiveGroupExpr) list_free_ext(group_expr); } - diff --git a/src/gausskernel/optimizer/path/joinpath.cpp b/src/gausskernel/optimizer/path/joinpath.cpp index 595dd831f7ed45796f9475fd2bbcf22bedeecbfb..8b85cd53ed292d4d1146f6096aafcb305c1c3ec9 100755 --- a/src/gausskernel/optimizer/path/joinpath.cpp +++ b/src/gausskernel/optimizer/path/joinpath.cpp @@ -58,6 +58,9 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, List* restrictlist, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels, Relids extra_lateral_rels); +static void asof_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, + List *restrictlist, JoinType jointype, JoinPathExtraData *extra, + Relids param_source_rels); static List* select_mergejoin_clauses(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, List* restrictlist, JoinType jointype, bool* mergejoin_allowed); static bool checkForPWJ(PlannerInfo* root, Path* outer_path, Path* inner_path, JoinType jointype, List* joinrestrict); @@ -126,6 +129,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou JoinPathExtraData extra; List* mergeclause_list = NIL; bool mergejoin_allowed = true; + bool asofjoin_allowed = false; Relids param_source_rels = NULL; Relids extra_lateral_rels = NULL; ListCell* lc = NULL; @@ -138,6 +142,11 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou ? NIL : find_specific_join_hint(root->parse->hintState, joinrel->relids, NULL, HINT_KEYWORD_HASHJOIN, false); + /* check asof join */ + if (check_asof_join(outerrel, innerrel)) { + asofjoin_allowed = true; + } + /* print relation information in pg_log before looking for path */ debug1_print_outerrel_and_innerrel(root, outerrel, innerrel); @@ -168,7 +177,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou * way of implementing a full outer join, so override enable_mergejoin if * it's a full join. */ - if (u_sess->attr.attr_sql.enable_mergejoin || jointype == JOIN_FULL || mergejoin_hint != NIL) + if ((u_sess->attr.attr_sql.enable_mergejoin || jointype == JOIN_FULL || mergejoin_hint != NIL) && !asofjoin_allowed) mergeclause_list = select_mergejoin_clauses(root, joinrel, outerrel, innerrel, restrictlist, jointype, &mergejoin_allowed); @@ -237,9 +246,9 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou if (ENABLE_SQL_BETA_FEATURE(PARAM_PATH_GEN)) { Relids predpush_all = predpush_candidates_same_level(root); /* - * If the GUC PREDPUSHFORCE is enabled, we just check the table appeared - * in the predpush hint, and add it into param_source_rels. Otherwise, - * we check all rels with param info for each outer table. + * If the GUC PREDPUSHFORCE is enabled, we just check the table appeared + * in the predpush hint, and add it into param_source_rels. Otherwise, + * we check all rels with param info for each outer table. */ if (ENABLE_PRED_PUSH_FORCE(root) && predpush_all != NULL) { total_available = @@ -307,7 +316,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou phinfo->ph_lateral); } } - + /* * Make sure extra_lateral_rels doesn't list anything within the join, and * that it's NULL if empty. (This allows us to use bms_add_members to add @@ -324,7 +333,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou * 1. Consider mergejoin paths where both relations must be explicitly * sorted. Skip this if we can't mergejoin. */ - if (mergejoin_allowed) + if (mergejoin_allowed && !asofjoin_allowed) sort_inner_and_outer( root, joinrel, outerrel, innerrel, restrictlist, mergeclause_list, jointype, &extra, param_source_rels, extra_lateral_rels); @@ -336,7 +345,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou * (That's okay because we know that nestloop can't handle right/full * joins at all, so it wouldn't work in the prohibited cases either.) */ - if (mergejoin_allowed) + if (mergejoin_allowed && !asofjoin_allowed) match_unsorted_outer(root, joinrel, outerrel, @@ -361,7 +370,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou * those made by match_unsorted_outer when add_paths_to_joinrel() is * invoked with the two rels given in the other order. */ - if (mergejoin_allowed) + if (mergejoin_allowed && !asofjoin_allowed) match_unsorted_inner(root, joinrel, outerrel, @@ -380,10 +389,16 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou * before being joined. As above, disregard enable_hashjoin for full * joins, because there may be no other alternative. */ - if (u_sess->attr.attr_sql.enable_hashjoin || jointype == JOIN_FULL || hashjoin_hint != NIL) + if ((u_sess->attr.attr_sql.enable_hashjoin || jointype == JOIN_FULL || hashjoin_hint != NIL) && !asofjoin_allowed) hash_inner_and_outer( root, joinrel, outerrel, innerrel, restrictlist, jointype, &extra, param_source_rels, extra_lateral_rels); + /* + * 5. Consider asof join + */ + if (asofjoin_allowed) { + asof_inner_and_outer(root, joinrel, outerrel, innerrel, restrictlist, jointype, &extra, param_source_rels); + } #ifdef PGXC /* * Can not generate join path. It is not necessary to this branch, otherwise @@ -1005,6 +1020,31 @@ static void TryHashJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy return; } +static void TryAsofJoinPathSingle(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, JoinPathExtraData *extra, + Path *outerPath, Path *innerPath, List *restrictClauses, List *hashclauses, + Relids requiredOuter, List *mergeclauses, List *outersortkeys, List *innersortkeys, + JoinCostWorkspace *workspace) +{ + /* try to use partitionwisejoin if need */ + if (checkForPWJ(root, outerPath, innerPath, jointype, restrictClauses)) { + Path *outerpath = fakePathForPWJ(outerPath); + Path *innerpath = fakePathForPWJ(innerPath); + Path *jpath = NULL; + + jpath = (Path *)create_asofjoin_path(root, joinrel, jointype, workspace, extra, outerpath, innerpath, + restrictClauses, requiredOuter, hashclauses, mergeclauses, outersortkeys, + innersortkeys); + add_path(root, joinrel, buildPartitionWiseJoinPath(jpath, outerPath, innerPath)); + } else { + add_path(root, joinrel, + (Path *)create_asofjoin_path(root, joinrel, jointype, workspace, extra, outerPath, innerPath, + restrictClauses, requiredOuter, hashclauses, mergeclauses, outersortkeys, + innersortkeys)); + } + + return; +} + /* * try_hashjoin_path * Consider a hash join path; if it appears useful, push it into @@ -1498,9 +1538,9 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI if (inner_cheapest_total == NULL) return; - inner_cheapest_total = (Path*)create_unique_path(root, innerrel, inner_cheapest_total, extra->sjinfo); + inner_cheapest_total = (Path *)create_unique_path(root, innerrel, inner_cheapest_total, extra->sjinfo); AssertEreport(inner_cheapest_total != NULL, MOD_OPT_JOIN, "inner cheapest path is NULL"); - } else if (nestjoinOK && inner_cheapest_total != NULL ) { + } else if (nestjoinOK && inner_cheapest_total != NULL) { /* * Consider materializing the cheapest inner path, unless * enable_material is off or the path in question materializes its @@ -2865,3 +2905,369 @@ static bool* calculate_join_georgraphy(PlannerInfo* root, RelOptInfo* outerrel, return join_used; } +/* + * try_asofjoin_path + * Consider a asof join path; if it appears useful, push it into + * the joinrel's pathlist via add_path(). + */ +static void try_asofjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, + JoinPathExtraData* extra, Relids param_source_rels, Path* outer_path, + Path* inner_path, List* restrict_clauses, List* hashclauses, List* mergeclauses, List* outersortkeys, List* innersortkeys) +{ + bool execOnCoords = false; + Relids required_outer; + JoinCostWorkspace workspace; + + /* only work on CN Gather mode */ + if (IS_STREAM_PLAN && !IsSameJoinExecType(root, outer_path, inner_path)) { + return; + } + + /* + * Check to see if proposed path is still parameterized, and reject if the + * parameterization wouldn't be sensible. + */ + required_outer = calc_non_nestloop_required_outer(outer_path, inner_path); + if (required_outer && !bms_overlap(required_outer, param_source_rels)) { + /* Waste no memory when we reject a path here */ + bms_free_ext(required_outer); + return; + } + + /* + * See comments in try_nestloop_path(). Also note that asofjoin paths + * never have any output pathkeys, per comments in create_asofjoin_path. + * Note: smp does not support parameterized paths. + */ + int max_dop = ((required_outer != NULL) || (u_sess->opt_cxt.query_dop <= 4 && u_sess->opt_cxt.max_query_dop >= 0)) + ? 1 + : u_sess->opt_cxt.query_dop; + initial_cost_asofjoin( + root, &workspace, jointype, hashclauses, outer_path, inner_path, mergeclauses, outersortkeys, innersortkeys, extra, max_dop); + + if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, NIL, required_outer)) { +#ifdef STREAMPLAN + /* check exec type of inner and outer path before generate join path. */ + if (IS_STREAM_PLAN && CheckJoinExecType(root, outer_path, inner_path)) { + execOnCoords = true; + } + + if (IS_STREAM_PLAN && !execOnCoords) { + AsofJoinPathGen* hjpgen = New(CurrentMemoryContext) AsofJoinPathGen(root, + joinrel, + jointype, + save_jointype, + extra, + outer_path, + inner_path, + restrict_clauses, + required_outer, + hashclauses, + mergeclauses, + outersortkeys, + innersortkeys); + + if (!canSeparateComputeAndStorageGroupForDelete(root)) { + RangeTblEntry* rte = + rt_fetch(linitial_int(root->parse->resultRelations), root->parse->rtable); /* target udi relation */ + + Distribution* distribution = ng_get_baserel_data_distribution(rte->relid, rte->relkind); + + JoinCostWorkspace cur_workspace; + copy_JoinCostWorkspace(&cur_workspace, &workspace); + + hjpgen->addAsofJoinPath(&cur_workspace, distribution, 1); + if (u_sess->opt_cxt.query_dop > 1) + hjpgen->addAsofJoinPath(&cur_workspace, distribution, u_sess->opt_cxt.query_dop); + } else { +#ifdef ENABLE_MULTIPLE_NODES + /* + * We choose candidate distribution list here with heuristic method, + * (1) for un-correlated query block, we will get a list of candidate Distribution + * (2) for correlated query block, we should shuffle them (inner and outer) to a correlated subplan node + * group + */ + List* candidate_distribution_list = + ng_get_join_candidate_distribution_list(outer_path, inner_path, root->is_correlated, + get_join_distribution_perference_type(joinrel, inner_path, outer_path)); + + /* + * For each candidate distribution (node group), we do hash join computing on it, + * if outer or inner is not in candidate node group, we should do shuffle. + */ + ListCell* lc = NULL; + foreach (lc, candidate_distribution_list) { + Distribution* distribution = (Distribution*)lfirst(lc); + JoinCostWorkspace cur_workspace; + copy_JoinCostWorkspace(&cur_workspace, &workspace); + + hjpgen->addAsofJoinPath(&cur_workspace, distribution, 1); + if (u_sess->opt_cxt.query_dop > 1) + hjpgen->addAsofJoinPath(&cur_workspace, distribution, u_sess->opt_cxt.query_dop); + } +#else + JoinCostWorkspace cur_workspace; + copy_JoinCostWorkspace(&cur_workspace, &workspace); + hjpgen->addAsofJoinPath(&cur_workspace, NULL, 1); + if (u_sess->opt_cxt.query_dop > 1) + hjpgen->addAsofJoinPath(&cur_workspace, NULL, u_sess->opt_cxt.query_dop); +#endif + } + + delete hjpgen; + } else +#endif + { + /* try asof join single */ + TryAsofJoinPathSingle(root, + joinrel, + jointype, + extra, + outer_path, + inner_path, + restrict_clauses, + hashclauses, + required_outer, + mergeclauses, + outersortkeys, + innersortkeys, + &workspace); + } + } else { + /* Waste no memory when we reject a path here */ + bms_free_ext(required_outer); + } +} + +/* + * asof_inner_and_outer + * Create asofjoin join paths by explicitly part both the outer and + * inner keys of each available hash clause. + * + * 'joinrel' is the join relation + * 'outerrel' is the outer join relation + * 'innerrel' is the inner join relation + * 'restrictlist' contains all of the RestrictInfo nodes for restriction + * clauses that apply to this join + * 'jointype' is the type of join to do + * 'sjinfo' is extra info about the join for selectivity estimation + * 'semifactors' contains valid data if jointype is SEMI or ANTI + * 'param_source_rels' are OK targets for parameterization of result paths + */ +static void asof_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, + List *restrictlist, JoinType jointype, JoinPathExtraData *extra, + Relids param_source_rels) +{ + const uint8 ASOFJOIN_OP_ARG_NUM = 2; + JoinType save_jointype = jointype; + bool isouterjoin = IS_OUTER_JOIN((uint32)jointype); + List *hashclauses = NIL; + ListCell *l = NULL; + List *all_pathkeys = NIL; + List *outerkeys = NIL; + List *innerkeys = NIL; + List *mergeclauses = NIL; + List *mergeclause_list = NIL; + + /* + * We need to build only one hashclauses list for any given pair of outer + * and inner relations; all of the hashable clauses will be used as keys. + * + * Scan the join's restrictinfo list to find hashjoinable clauses that are + * usable with this pair of sub-relations. + */ + hashclauses = NIL; + OpExpr *opclause = NULL; + foreach (l, restrictlist) { + RestrictInfo *restrictinfo = (RestrictInfo *)lfirst(l); + + opclause = (OpExpr *)restrictinfo->clause; + + if (!restrictinfo->can_join || restrictinfo->hashjoinoperator == InvalidOid) + continue; /* not hashjoinable */ + + /* + * Check if clause has the form "outer op inner" or "inner op outer". + */ + if (!clause_sides_match_join(restrictinfo, outerrel, innerrel)) + continue; /* no good for these input relations */ + + /* + * skip qual that contains sublink + */ + if (contain_subplans((Node *)restrictinfo->clause)) + continue; + + hashclauses = lappend(hashclauses, restrictinfo); + mergeclauses = lappend(mergeclauses, restrictinfo); + } + + mergeclause_list = list_difference(restrictlist, hashclauses); + foreach (l, mergeclause_list) { + RestrictInfo *restrictinfo = (RestrictInfo *)lfirst(l); + Expr *clause = restrictinfo->clause; + Oid eqop; + Node *leftarg = NULL; + + if (restrictinfo->pseudoconstant) + continue; + if (!is_opclause(clause)) + continue; + if (list_length(((OpExpr *)clause)->args) != ASOFJOIN_OP_ARG_NUM) + continue; + /* + * If processing an outer join, only use its own join clauses for + * hashing. For inner joins we need not be so picky. + */ + if (isouterjoin && restrictinfo->is_pushed_down) + continue; + + if (!restrictinfo->can_join) + continue; /* not joinable */ + + get_sort_group_operators(exprType((Node *)clause), false, true, false, NULL, &eqop, NULL, NULL); + + if (!contain_volatile_functions((Node *)clause)) + restrictinfo->mergeopfamilies = get_mergejoin_opfamilies(eqop); + + initialize_mergeclause_eclasses(root, restrictinfo); + mergeclauses = lappend(mergeclauses, restrictinfo); + } + + all_pathkeys = select_outer_pathkeys_for_merge(root, mergeclauses, joinrel); + outerkeys = all_pathkeys; + /* Sort the mergeclauses into the corresponding ordering */ + mergeclauses = find_mergeclauses_for_outer_pathkeys(root, outerkeys, mergeclauses); + /* Build sort pathkeys for the inner side */ + innerkeys = make_inner_pathkeys_for_merge(root, mergeclauses, outerkeys); + + /* If we found any usable hashclauses, make paths */ + if (hashclauses != NULL) { + ListCell *lc1 = NULL; + ListCell *lc2 = NULL; + Path *cheapest_startup_outer = outerrel->cheapest_startup_path; + int i, j; + bool *join_used = NULL; + int num_inner = list_length(innerrel->cheapest_total_path) - 1; + + /* + * We consider both the cheapest-total-cost and cheapest-startup-cost + * outer paths. There's no need to consider any but the + * cheapest-total-cost inner path, however. + */ + join_used = calculate_join_georgraphy(root, outerrel, innerrel, hashclauses); + i = 0; + foreach (lc1, outerrel->cheapest_total_path) { + Path *cheapest_total_outer_orig = (Path *)lfirst(lc1); + Path *cheapest_total_outer = NULL; + j = 0; + foreach (lc2, innerrel->cheapest_total_path) { + Path *cheapest_total_inner = (Path *)lfirst(lc2); + cheapest_total_outer = cheapest_total_outer_orig; + + /* + * If either cheapest-total path is parameterized by the other rel, we + * can't use a hashjoin. (There's no use looking for alternative + * input paths, since these should already be the least-parameterized + * available paths.) + */ + if (PATH_PARAM_BY_REL(cheapest_total_outer, innerrel) || + PATH_PARAM_BY_REL(cheapest_total_inner, outerrel) || + (cheapest_startup_outer != NULL && PATH_PARAM_BY_REL(cheapest_startup_outer, innerrel))) + return; + + /* we always accept join combination if one of path is cheapest path */ + if (cheapest_total_outer != linitial(outerrel->cheapest_total_path) && + cheapest_total_inner != linitial(innerrel->cheapest_total_path)) { + if (!join_used[(i - 1) * num_inner + j - 1]) { + j++; + continue; + } + } + + jointype = save_jointype; + + /* Unique-ify if need be; we ignore parameterized possibilities */ + if (jointype == JOIN_UNIQUE_OUTER) { + cheapest_total_outer = + (Path *)create_unique_path(root, outerrel, cheapest_total_outer, extra->sjinfo); + AssertEreport(cheapest_total_outer != NULL, MOD_OPT_JOIN, "outer cheapest path is NULL"); + jointype = JOIN_INNER; + try_asofjoin_path(root, joinrel, jointype, save_jointype, extra, param_source_rels, + cheapest_total_outer, cheapest_total_inner, restrictlist, hashclauses, + mergeclauses, outerkeys, innerkeys); + /* no possibility of cheap startup here */ + } else if (jointype == JOIN_UNIQUE_INNER) { + cheapest_total_inner = + (Path *)create_unique_path(root, innerrel, cheapest_total_inner, extra->sjinfo); + AssertEreport(cheapest_total_inner != NULL, MOD_OPT_JOIN, "inner cheapest path is NULL"); + jointype = JOIN_INNER; + try_asofjoin_path(root, joinrel, jointype, save_jointype, extra, param_source_rels, + cheapest_total_outer, cheapest_total_inner, restrictlist, hashclauses, + mergeclauses, outerkeys, innerkeys); + if (cheapest_startup_outer != NULL && cheapest_startup_outer != cheapest_total_outer) + try_asofjoin_path(root, joinrel, jointype, save_jointype, extra, param_source_rels, + cheapest_startup_outer, cheapest_total_inner, restrictlist, hashclauses, + mergeclauses, outerkeys, innerkeys); + } else { + /* + * For other jointypes, we consider the following paths: + * 1. cheapest_total_outer and cheapest_total_inner + * 2. cheapest_startup_outer and cheapest_total_inner + * 3. cheapest_parameterized_paths + * There is no use in generating parameterized paths on the basis + * of possibly cheap startup cost, so this is sufficient. + */ + ListCell *llc1 = NULL; + ListCell *llc2 = NULL; + + try_asofjoin_path(root, joinrel, jointype, save_jointype, extra, param_source_rels, + cheapest_total_outer, cheapest_total_inner, restrictlist, hashclauses, + mergeclauses, outerkeys, innerkeys); + + if (cheapest_startup_outer != NULL && cheapest_startup_outer != cheapest_total_outer) + try_asofjoin_path(root, joinrel, jointype, save_jointype, extra, param_source_rels, + cheapest_startup_outer, cheapest_total_inner, restrictlist, hashclauses, + mergeclauses, outerkeys, innerkeys); + + foreach (llc1, outerrel->cheapest_parameterized_paths) { + Path *outerpath = (Path *)lfirst(llc1); + + /* + * We cannot use an outer path that is parameterized by the + * inner rel. + */ + if (PATH_PARAM_BY_REL(outerpath, innerrel)) + continue; + + foreach (llc2, innerrel->cheapest_parameterized_paths) { + Path *innerpath = (Path *)lfirst(llc2); + + /* + * We cannot use an inner path that is parameterized by + * the outer rel, either. + */ + if (PATH_PARAM_BY_REL(innerpath, outerrel)) + continue; + + if (outerpath == cheapest_startup_outer && innerpath == cheapest_total_inner) + continue; /* already tried it */ + + if (outerpath == cheapest_total_outer && innerpath == cheapest_total_inner) + continue; /* already tried it */ + + try_asofjoin_path(root, joinrel, jointype, save_jointype, extra, param_source_rels, + outerpath, innerpath, restrictlist, hashclauses, mergeclauses, outerkeys, + innerkeys); + } + } + } + j++; + } + i++; + } + if (join_used != NULL) + pfree_ext(join_used); + } +} + diff --git a/src/gausskernel/optimizer/path/joinrels.cpp b/src/gausskernel/optimizer/path/joinrels.cpp index 5075d60ba15f73ccdda31e2ec6316b3061f86f6f..8f07fd91f8fade41c490a8c7bfb0f0cc1fc13d55 100755 --- a/src/gausskernel/optimizer/path/joinrels.cpp +++ b/src/gausskernel/optimizer/path/joinrels.cpp @@ -540,6 +540,36 @@ static bool join_is_legal(PlannerInfo* root, RelOptInfo* rel1, RelOptInfo* rel2, return true; } +/* + * check_asof_join + * check is it asof join + */ +bool check_asof_join(RelOptInfo *rel1, RelOptInfo *rel2) +{ + ListCell *jr1 = NULL; + ListCell *jr2 = NULL; + bool check1 = false; + bool check2 = false; + + foreach (jr1, rel1->joininfo) { + RestrictInfo *ri = (RestrictInfo *)lfirst(jr1); + if (ri->is_asof) { + check1 = true; + break; + } + } + + foreach (jr2, rel2->joininfo) { + RestrictInfo *ri = (RestrictInfo *)lfirst(jr2); + if (ri->is_asof) { + check2 = true; + break; + } + } + + return check1 && check2; +} + /* * make_join_rel * Find or create a join RelOptInfo that represents the join of @@ -661,7 +691,9 @@ RelOptInfo* make_join_rel(PlannerInfo* root, RelOptInfo* rel1, RelOptInfo* rel2) if (u_sess->attr.attr_sql.dolphin && sjinfo->is_straight_join) { break; } - add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER, sjinfo, restrictlist); + if (!check_asof_join(rel1, rel2)) { + add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER, sjinfo, restrictlist); + } break; case JOIN_LEFT: case JOIN_LEFT_ANTI_FULL: diff --git a/src/gausskernel/optimizer/path/streampath_base.cpp b/src/gausskernel/optimizer/path/streampath_base.cpp index 5bf2d10f05b66dcd230dacfaaabc42260c479056..f4874815ff985e14aacd2441c342984ba10fafc4 100755 --- a/src/gausskernel/optimizer/path/streampath_base.cpp +++ b/src/gausskernel/optimizer/path/streampath_base.cpp @@ -1144,6 +1144,209 @@ Path* HashJoinPathGen::createHashJoinPath() return (Path*)pathnode; } +/* + * @Description: constructor for AsofJoinPathGen. + * + * @param[IN] root: the plannerInfo for this join. + * @param[IN] joinrel: the join relation. + * @param[IN] jointype: join type. + * @param[IN] save_jointype: save join type. + * @param[IN] sjinfo: extra info about the join for selectivity estimation. + * @param[IN] semifactors: contains valid data if jointype is SEMI or ANTI. + * @param[IN] hashclauses: the RestrictInfo nodes to use as hash clauses + * (this should be a subset of the restrict_clauses list). + * @param[IN] restrictlist: all RestrictInfo nodes to apply at the join. + * @param[IN] inner_path: the inner subpath for join. + * @param[IN] outer_path: the outer subpath for join. + * @param[IN] required_outer: the set of required outer rels. + */ +AsofJoinPathGen::AsofJoinPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, + JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrictlist, + Relids required_outer, List* hashclauses, List* mergeclauses, List* outersortkeys, List* innersortkeys) + : JoinPathGen(root, joinrel, jointype, save_jointype, extra, hashclauses, restrictlist, outer_path, + inner_path, required_outer) +{ + m_joinmethod = T_AsofJoin; + m_mergeClauses = mergeclauses; + m_hashClauses = hashclauses; + m_innerSortKeys = innersortkeys; + m_outerSortKeys = outersortkeys; +} + +/* + * @Description: release memory at deconstruct stage. + */ +AsofJoinPathGen::~AsofJoinPathGen() +{ + m_hashClauses = NIL; + m_innerSortKeys = NIL; + m_mergeClauses = NIL; + m_outerSortKeys = NIL; +} + +/* + * @Description: add asof join path to path list base on the + * target nodegroup and parallel degree. + * + * @param[IN] workspace: work space for join cost. + * @param[IN] targetDistribution: target nodegroup distribution. + * @param[IN] dop: target parallel degree. + * return void + */ +void AsofJoinPathGen::addAsofJoinPath(JoinCostWorkspace* workspace, Distribution* targetDistribution, int dop) +{ + m_dop = dop; + m_workspace = workspace; + m_targetDistribution = targetDistribution; + + initDistribution(); + + if (m_dop > 1) { + /* Add stream info base on join clauses and subpath distributions. */ + addStreamMppInfo(); + } else { + setStreamBaseInfo(STREAM_NONE, STREAM_NONE, NIL, NIL); + } + + /* Add stream info base on parallel degree. */ + addStreamParallelInfo(); + /* Generate asof join paths and add them to path list. */ + addAsofjoinPathToList(); + + /* Reset for next time usage. */ + reset(); +} + +/* + * @Description: create stream path for join, then create asof join path + * and add it to path list. + * + * return void + */ +void AsofJoinPathGen::addAsofjoinPathToList() +{ + ListCell* lc = NULL; + Path* joinpath = NULL; + + if (m_streamInfoList == NIL) + return; + + foreach (lc, m_streamInfoList) { + m_streamInfoPair = (StreamInfoPair*)lfirst(lc); + addJoinStreamPath(); + joinpath = createAsofJoinPath(); + addJoinPath(joinpath); + } +} + +/* + * @Description: create asof join path. + * + * return Path*: + */ +Path* AsofJoinPathGen::createAsofJoinPath() +{ + AsofPath* pathnode = makeNode(AsofPath); + bool try_eq_related_indirectly = false; + + initialCostAsofjoin(); + + pathnode->jpath.path.pathtype = T_AsofJoin; + pathnode->jpath.path.parent = m_rel; + pathnode->jpath.path.pathtarget = m_rel->reltarget; + pathnode->jpath.path.param_info = get_joinrel_parampathinfo( + m_root, m_rel, m_outerStreamPath, m_innerStreamPath, m_extra->sjinfo, m_requiredOuter, &m_joinRestrictinfo); + + /* + * A hashjoin never has pathkeys, since its output ordering is + * unpredictable due to possible batching. XXX If the inner relation is + * small enough, we could instruct the executor that it must not batch, + * and then we could assume that the output inherits the outer relation's + * ordering, which might save a sort step. However there is considerable + * downside if our estimate of the inner relation size is badly off. For + * the moment we don't risk it. (Note also that if we wanted to take this + * seriously, joinpath.c would have to consider many more paths for the + * outer rel than it does now.) + */ + pathnode->jpath.path.pathkeys = NIL; + pathnode->jpath.path.dop = m_dop; + pathnode->jpath.jointype = m_jointype; + pathnode->jpath.outerjoinpath = m_outerStreamPath; + pathnode->jpath.innerjoinpath = m_innerStreamPath; + pathnode->jpath.joinrestrictinfo = m_joinRestrictinfo; + pathnode->jpath.skewoptimize = m_streamInfoPair->skew_optimize; + pathnode->path_hashclauses = m_hashClauses; + pathnode->path_mergeclauses = m_mergeClauses; + pathnode->outersortkeys = m_outerSortKeys; + pathnode->innersortkeys = m_innerSortKeys; + + pathnode->jpath.path.exec_type = SetExectypeForJoinPath(m_innerStreamPath, m_outerStreamPath); + +#ifdef STREAMPLAN + pathnode->jpath.path.locator_type = + locator_type_join(m_innerStreamPath->locator_type, m_outerStreamPath->locator_type); + ProcessRangeListJoinType(&pathnode->jpath.path, m_outerStreamPath, m_innerStreamPath); +#ifdef ENABLE_MULTIPLE_NODES + if (IS_STREAM_PLAN) { + /* add location information for hash join path */ + Distribution* distribution = ng_get_join_distribution(m_outerStreamPath, m_innerStreamPath); + ng_copy_distribution(&pathnode->jpath.path.distribution, distribution); + } +#endif +#endif + + /* final_cost_asofjoin will fill in pathnode->num_batches */ + finalCostAsofjoin(pathnode); + + return (Path*)pathnode; +} + +/* + * @Description: Preliminary estimate of the cost of a hashjoin path. + * + * This must quickly produce lower-bound estimates of the path's startup and + * total costs. If we are unable to eliminate the proposed path from + * consideration using the lower bounds, final_cost_hashjoin will be called + * to obtain the final estimates. + * + * The exact division of labor between this function and final_cost_hashjoin + * is private to them, and represents a tradeoff between speed of the initial + * estimate and getting a tight lower bound. We choose to not examine the + * join quals here (other than by counting the number of hash clauses), + * so we can't do much with CPU costs. We do assume that + * ExecChooseHashTableSize is cheap enough to use here. + * + * return void + */ +void AsofJoinPathGen::initialCostAsofjoin() +{ + initial_cost_asofjoin(m_root, + m_workspace, + m_jointype, + m_hashClauses, + m_outerStreamPath, + m_innerStreamPath, + m_mergeClauses, + m_outerSortKeys, + m_innerSortKeys, + m_extra, + m_dop); +} + +/* + * @Description: Final estimate of the cost and result size of a hashjoin path. + * + * Notice: the numbatches estimate is also saved into 'path' for use later + * + * @param[IN] path: hash join path + * @param[IN] hasalternative: has alternative join. + * return void + */ +void AsofJoinPathGen::finalCostAsofjoin(AsofPath* path) +{ + final_cost_asofjoin(m_root, path, m_workspace, m_extra, path->jpath.path.dop); +} + /* * @Description: Preliminary estimate of the cost of a hashjoin path. * diff --git a/src/gausskernel/optimizer/plan/createplan.cpp b/src/gausskernel/optimizer/plan/createplan.cpp index 7ec60b859efd123dadd9812fb26a288f5bb9272c..25d07ee0d577fa13858a317ca05027a98bd6b259 100755 --- a/src/gausskernel/optimizer/plan/createplan.cpp +++ b/src/gausskernel/optimizer/plan/createplan.cpp @@ -124,6 +124,7 @@ static ForeignScan* create_foreignscan_plan(PlannerInfo* root, ForeignPath* best static NestLoop* create_nestloop_plan(PlannerInfo* root, NestPath* best_path, Plan* outer_plan, Plan* inner_plan); static MergeJoin* create_mergejoin_plan(PlannerInfo* root, MergePath* best_path, Plan* outer_plan, Plan* inner_plan); static HashJoin* create_hashjoin_plan(PlannerInfo* root, HashPath* best_path, Plan* outer_plan, Plan* inner_plan); +static AsofJoin* create_asofjoin_plan(PlannerInfo* root, AsofPath* best_path, Plan* outer_plan, Plan* inner_plan); static Node* replace_nestloop_params(PlannerInfo* root, Node* expr); static Node* replace_nestloop_params_mutator(Node* node, PlannerInfo* root); static void process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params); @@ -180,6 +181,8 @@ static NestLoop* make_nestloop(List* tlist, List* joinclauses, List* otherclause Plan* righttree, JoinType jointype, bool inner_unique); static HashJoin* make_hashjoin(List* tlist, List* joinclauses, List* otherclauses, List* hashclauses, Plan* lefttree, Plan* righttree, JoinType jointype, bool inner_unique); +static AsofJoin* make_asofjoin(List* tlist, List* joinclauses, List* otherclauses, List* hashclauses, + Plan* lefttree, Plan* righttree, JoinType jointype, bool inner_unique); static Hash* make_hash( Plan* lefttree, Oid skewTable, AttrNumber skewColumn, bool skewInherit, Oid skewColType, int32 skewColTypmod); static MergeJoin* make_mergejoin(List* tlist, List* joinclauses, List* otherclauses, List* mergeclauses, @@ -387,6 +390,7 @@ static Plan* create_plan_recurse(PlannerInfo* root, Path* best_path) case T_HashJoin: case T_MergeJoin: case T_NestLoop: + case T_AsofJoin: plan = create_join_plan(root, (JoinPath*)best_path); break; case T_Append: @@ -1216,6 +1220,9 @@ static Plan* create_join_plan(PlannerInfo* root, JoinPath* best_path) plan = (Plan*)create_nestloop_plan(root, (NestPath*)best_path, outer_plan, inner_plan); break; + case T_AsofJoin: + plan = (Plan*)create_asofjoin_plan(root, (AsofPath*)best_path, outer_plan, inner_plan); + break; default: { ereport(ERROR, (errmodule(MOD_OPT), @@ -3509,6 +3516,7 @@ static void ModifyWorktableWtParam(Node* planNode, int oldWtParam, int newWtPara } case T_HashJoin: + case T_AsofJoin: case T_NestLoop: case T_MergeJoin: { Plan* plan = &((Join*)planNode)->plan; @@ -4651,6 +4659,173 @@ static MergeJoin* create_mergejoin_plan(PlannerInfo* root, MergePath* best_path, return join_plan; } +/* + * extract_actual_join_clauses + * + * Extract bare clauses from 'restrictinfo_list', separating those that + * syntactically match the join level from those that were pushed down. + * Pseudoconstant clauses are excluded from the results. + * + * This is only used at outer joins, since for plain joins we don't care + * about pushed-down-ness. + */ +static void extract_actual_asofjoin_clauses(List* restrictinfo_list, List** joinquals, List** otherquals, Relids outerrelids) +{ + ListCell* l = NULL; + + *joinquals = NIL; + *otherquals = NIL; + + foreach (l, restrictinfo_list) { + RestrictInfo* rinfo = (RestrictInfo*)lfirst(l); + + AssertEreport(IsA(rinfo, RestrictInfo), MOD_OPT, ""); + + if (rinfo->is_pushed_down) { + if (!rinfo->pseudoconstant) + *otherquals = lappend(*otherquals, rinfo->clause); + } else { + /* joinquals shouldn't have been marked pseudoconstant */ + AssertEreport(!rinfo->pseudoconstant, MOD_OPT, ""); + + OpExpr* clause = (OpExpr*)rinfo->clause; + + Assert(is_opclause(clause)); + if (bms_is_subset(rinfo->right_relids, outerrelids)) { + /* + * Duplicate just enough of the structure to allow commuting the + * clause without changing the original list. Could use + * copyObject, but a complete deep copy is overkill. + */ + OpExpr* temp = makeNode(OpExpr); + + temp->opno = clause->opno; + temp->opfuncid = InvalidOid; + temp->opresulttype = clause->opresulttype; + temp->opretset = clause->opretset; + temp->opcollid = clause->opcollid; + temp->inputcollid = clause->inputcollid; + temp->args = list_copy(clause->args); + temp->location = clause->location; + /* Commute it --- note this modifies the temp node in-place. */ + CommuteOpExpr(temp); + *joinquals = lappend(*joinquals, temp); + rinfo->outer_is_left = false; + } else { + Assert(bms_is_subset(rinfo->left_relids, outerrelids)); + *joinquals = lappend(*joinquals, clause); + rinfo->outer_is_left = true; + } + } + } +} + +/* + * extract_actual_clauses + * + * Extract bare clauses from 'restrictinfo_list', returning either the + * regular ones or the pseudoconstant ones per 'pseudoconstant'. + */ +static List* extract_actual_asofjoin_clauses(List* restrictinfo_list, bool pseudoconstant, Relids outerrelids) +{ + List* result = NIL; + ListCell* l = NULL; + + foreach (l, restrictinfo_list) { + RestrictInfo* rinfo = (RestrictInfo*)lfirst(l); + + AssertEreport(IsA(rinfo, RestrictInfo), MOD_OPT, ""); + + /* we consider the qual is real if pseudoconstant is true and clause_relids is non-null. */ + if ((rinfo->pseudoconstant == pseudoconstant) && (!pseudoconstant || bms_is_empty(rinfo->clause_relids))) { + + OpExpr* clause = (OpExpr*)rinfo->clause; + + Assert(is_opclause(clause)); + if (bms_is_subset(rinfo->right_relids, outerrelids)) { + /* + * Duplicate just enough of the structure to allow commuting the + * clause without changing the original list. Could use + * copyObject, but a complete deep copy is overkill. + */ + OpExpr* temp = makeNode(OpExpr); + + temp->opno = clause->opno; + temp->opfuncid = InvalidOid; + temp->opresulttype = clause->opresulttype; + temp->opretset = clause->opretset; + temp->opcollid = clause->opcollid; + temp->inputcollid = clause->inputcollid; + temp->args = list_copy(clause->args); + temp->location = clause->location; + /* Commute it --- note this modifies the temp node in-place. */ + CommuteOpExpr(temp); + result = lappend(result, temp); + rinfo->outer_is_left = false; + } else { + Assert(bms_is_subset(rinfo->left_relids, outerrelids)); + result = lappend(result, clause); + rinfo->outer_is_left = true; + } + } + } + return result; +} + +static AsofJoin* create_asofjoin_plan(PlannerInfo* root, AsofPath* best_path, Plan* outer_plan, Plan* inner_plan) +{ + + List* tlist = build_relation_tlist(best_path->jpath.path.parent); + List* joinclauses = NIL; + List* actual_joinclauses = NIL; + List* otherclauses = NIL; + List* hashclauses = NIL; + List* mergeclauses = NIL; + ListCell* lc = NULL; + AsofJoin* join_plan = NULL; + + /* Sort join qual clauses into best execution order */ + joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo); + + /* Get the join qual clauses (in plain expression form) */ + /* Any pseudoconstant clauses are ignored here */ + if (IS_OUTER_JOIN((uint32)(best_path->jpath.jointype))) { + extract_actual_asofjoin_clauses(joinclauses, &joinclauses, &otherclauses, best_path->jpath.outerjoinpath->parent->relids); + } else { + /* We can treat all clauses alike for an inner join */ + joinclauses = extract_actual_asofjoin_clauses(joinclauses, false, best_path->jpath.outerjoinpath->parent->relids); + otherclauses = NIL; + } + + /* + * Remove the hashclauses from the list of join qual clauses, leaving the + * list of quals that must be checked as qpquals. + */ + hashclauses = get_switched_clauses(best_path->path_hashclauses, best_path->jpath.outerjoinpath->parent->relids); + joinclauses = list_difference(joinclauses, hashclauses); + /* + * Replace any outer-relation variables with nestloop params. There + * should not be any in the hashclauses. + */ + if (best_path->jpath.path.param_info) { + joinclauses = (List*)replace_nestloop_params(root, (Node*)joinclauses); + otherclauses = (List*)replace_nestloop_params(root, (Node*)otherclauses); + } + + + join_plan = make_asofjoin(tlist, joinclauses, otherclauses, hashclauses, outer_plan, inner_plan, + best_path->jpath.jointype, best_path->jpath.inner_unique); + + + if (root->join_null_info) + join_plan->join.nulleqqual = make_null_eq_clause(hashclauses, &join_plan->join.joinqual, root->join_null_info); + + /* Set dop from path. */ + join_plan->join.plan.dop = best_path->jpath.path.dop; + + return join_plan; +} + /* * @Description: Find this expr from targetList. * @in expr: Need find expr. @@ -5173,7 +5348,7 @@ static Node* replace_nestloop_params_mutator(Node* node, PlannerInfo* root) * upper-level reference to a lower-level copy of the same PHV. */ PlaceHolderVar *newphv = makeNode(PlaceHolderVar); - + errno_t rc = memcpy_s(newphv, sizeof(PlaceHolderVar), phv, sizeof(PlaceHolderVar)); securec_check_c(rc, "\0", "\0"); newphv->phexpr = (Expr *) @@ -7203,6 +7378,25 @@ static MergeJoin* make_mergejoin(List* tlist, List* joinclauses, List* otherclau return node; } +static AsofJoin* make_asofjoin(List* tlist, List* joinclauses, List* otherclauses, List* hashclauses, + Plan* lefttree, Plan* righttree, JoinType jointype, bool inner_unique) +{ + AsofJoin* node = makeNode(AsofJoin); + Plan* plan = &node->join.plan; + + /* cost should be inserted by caller */ + plan->targetlist = tlist; + plan->qual = otherclauses; + plan->lefttree = lefttree; + plan->righttree = righttree; + node->hashclauses = hashclauses; + node->join.jointype = jointype; + node->join.inner_unique = inner_unique; + node->join.joinqual = joinclauses; + + return node; +} + /* * make_project_set * Build a ProjectSet plan node @@ -8574,7 +8768,7 @@ Limit* make_limit(PlannerInfo* root, Plan* lefttree, Node* limitOffset, Node* li } Limit *make_limit_with_ties(PlannerInfo* root, Plan* lefttree, Query *parse, int64 offset_est, - int64 count_est, bool enable_parallel) + int64 count_est, bool enable_parallel) { Limit *node = make_limit(root, lefttree, parse->limitOffset, parse->limitCount, offset_est, count_est, enable_parallel); AttrNumber *sortColIdx_s = NULL; diff --git a/src/gausskernel/optimizer/plan/initsplan.cpp b/src/gausskernel/optimizer/plan/initsplan.cpp index d9126b9d6b5721057ba0f7de8c45ff15ede917e7..e1e625cc45ba33f01455439ade464a58863b297f 100644 --- a/src/gausskernel/optimizer/plan/initsplan.cpp +++ b/src/gausskernel/optimizer/plan/initsplan.cpp @@ -586,7 +586,7 @@ static void process_security_barrier_quals( qual = (Node*)lfirst(cell2); distribute_qual_to_rels( - root, qual, false, below_outer_join, JOIN_INNER, security_level, qualscope, qualscope, NULL, NULL, NULL); + root, qual, false, below_outer_join, JOIN_INNER, security_level, qualscope, qualscope, NULL, NULL, NULL, false); } /* @@ -919,7 +919,7 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, false, below_outer_join, JOIN_INNER, root->qualSecurityLevel, *qualscope, NULL, NULL, NULL, - NULL); + NULL, FALSE); else *postponed_qual_list = lappend(*postponed_qual_list, pq); } @@ -930,7 +930,7 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, foreach (l, (List*)f->quals) { Node* qual = (Node*)lfirst(l); distribute_qual_to_rels(root, qual, false, below_outer_join, JOIN_INNER, - root->qualSecurityLevel, *qualscope, NULL, NULL, NULL, postponed_qual_list); + root->qualSecurityLevel, *qualscope, NULL, NULL, NULL, postponed_qual_list, FALSE); } } else if (IsA(jtnode, JoinExpr)) { JoinExpr* j = (JoinExpr*)jtnode; @@ -1082,7 +1082,8 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, ojscope, nonnullable_rels, NULL, - postponed_qual_list); + postponed_qual_list, + j->isAsof); } /* Now we can add the SpecialJoinInfo to join_info_list */ @@ -1423,7 +1424,7 @@ static SpecialJoinInfo* make_outerjoininfo( */ void distribute_qual_to_rels(PlannerInfo* root, Node* clause, bool is_deduced, bool below_outer_join, JoinType jointype, Index security_level, Relids qualscope, Relids ojscope, Relids outerjoin_nonnullable, - Relids deduced_nullable_relids, List **postponed_qual_list) + Relids deduced_nullable_relids, List **postponed_qual_list, bool is_asof) { Relids relids; bool is_pushed_down = false; @@ -1652,8 +1653,8 @@ void distribute_qual_to_rels(PlannerInfo* root, Node* clause, bool is_deduced, b security_level, relids, outerjoin_nonnullable, - nullable_relids); - + nullable_relids, + is_asof); /* * If it's a join clause (either naturally, or because delayed by * outer-join rules), add vars used in the clause to targetlists of their @@ -2064,7 +2065,8 @@ void process_implied_equality(PlannerInfo* root, Oid opno, Oid collation, Expr* NULL, NULL, nullable_relids, - NULL); + NULL, + false); } void process_implied_quality(PlannerInfo* root, Node* node, Relids relids, bool below_outer_join) @@ -2101,7 +2103,7 @@ void process_implied_quality(PlannerInfo* root, Node* node, Relids relids, bool return; distribute_qual_to_rels( - root, node, true, below_outer_join, JOIN_INNER, root->qualSecurityLevel, relids, NULL, NULL, NULL, NULL); + root, node, true, below_outer_join, JOIN_INNER, root->qualSecurityLevel, relids, NULL, NULL, NULL, NULL, FALSE); } /* @@ -2145,7 +2147,8 @@ RestrictInfo* build_implied_join_equality( security_level, /* security_level */ qualscope, /* required_relids */ NULL, /* outer_relids */ - nullable_relids); /* nullable_relids */ + nullable_relids,/* nullable_relids */ + false); /* Set mergejoinability/hashjoinability flags */ check_mergejoinable(restrictinfo); diff --git a/src/gausskernel/optimizer/plan/planner.cpp b/src/gausskernel/optimizer/plan/planner.cpp index 35287d2bcb30f12e83f9c785ee499507bdf59875..b0c8675781f117937a1f7a72b325f488fe6215d4 100755 --- a/src/gausskernel/optimizer/plan/planner.cpp +++ b/src/gausskernel/optimizer/plan/planner.cpp @@ -3670,6 +3670,7 @@ static Plan* internal_grouping_planner(PlannerInfo* root, double tuple_fraction) * target list of the query according to the new targetlist * set above. For now do this only for SELECT statements. */ + if (IsA(result_plan, RemoteQuery) && parse->commandType == CMD_SELECT && !permit_gather(root)) { pgxc_rqplan_adjust_tlist( root, (RemoteQuery*)result_plan, ((RemoteQuery*)result_plan)->is_simple ? false : true); @@ -10178,6 +10179,28 @@ static bool vector_engine_walker_internal(Plan* result_plan, bool check_rescan, CostVectorHashJoin((Join*)result_plan, planContext); } break; + case T_AsofJoin: { + + Join* j = (Join*)result_plan; + if (j->jointype != JOIN_INNER) + return true; + + AsofJoin* aj = (AsofJoin*)result_plan; + /* Find unsupport expr in *Hash* clause */ + if (vector_engine_unsupport_expression_walker((Node*)aj->hashclauses, planContext)) + return true; + /* Find unsupport expr in *Join* clause */ + if (vector_engine_unsupport_expression_walker((Node*)aj->join.joinqual, planContext)) + return true; + if (vector_engine_unsupport_expression_walker((Node*)aj->join.nulleqqual, planContext)) + return true; + + if (vector_engine_walker_internal(result_plan->lefttree, check_rescan, planContext)) + return true; + if (vector_engine_walker_internal(result_plan->righttree, check_rescan, planContext)) + return true; + } break; + case T_Append: { Append* append = (Append*)result_plan; ListCell* lc = NULL; @@ -10524,6 +10547,7 @@ Plan* vectorize_plan(Plan* result_plan, bool ignore_remotequery, bool forceVecto } case T_MergeJoin: case T_NestLoop: + case T_AsofJoin: result_plan->lefttree = vectorize_plan(result_plan->lefttree, ignore_remotequery, forceVectorEngine); result_plan->righttree = vectorize_plan(result_plan->righttree, ignore_remotequery, forceVectorEngine); @@ -10586,11 +10610,13 @@ Plan* vectorize_plan(Plan* result_plan, bool ignore_remotequery, bool forceVecto plan = vectorize_plan(plan, ignore_remotequery, forceVectorEngine); lfirst(lc) = plan; if (!IsVecOutput(plan)) { + if (u_sess->attr.attr_sql.enable_force_vector_engine) lfirst(lc) = (Plan*)make_rowtovec(plan); isVec = false; } } + if (isVec == true || u_sess->attr.attr_sql.enable_force_vector_engine) { return build_vector_plan(result_plan); } else { @@ -10674,6 +10700,9 @@ static Plan* build_vector_plan(Plan* plan) case T_MergeJoin: plan->type = T_VecMergeJoin; break; + case T_AsofJoin: + plan->type = T_VecAsofJoin; + break; case T_WindowAgg: plan->type = T_VecWindowAgg; break; diff --git a/src/gausskernel/optimizer/plan/planrecursive_single.cpp b/src/gausskernel/optimizer/plan/planrecursive_single.cpp index 3368841d4ad9503ab7ef96783e699e4e5f9f99d0..9ddcd928965170abe8e273b6889c13618f3c08cd 100644 --- a/src/gausskernel/optimizer/plan/planrecursive_single.cpp +++ b/src/gausskernel/optimizer/plan/planrecursive_single.cpp @@ -297,7 +297,8 @@ void set_recursive_cteplan_ref(Plan* node, RecursiveRefContext* context) /* First, assign join type of nearest node */ if (nodeTag(node) == T_HashJoin || nodeTag(node) == T_VecHashJoin || nodeTag(node) == T_MergeJoin || - nodeTag(node) == T_VecMergeJoin || nodeTag(node) == T_NestLoop || nodeTag(node) == T_VecNestLoop) { + nodeTag(node) == T_VecMergeJoin || nodeTag(node) == T_NestLoop || nodeTag(node) == T_VecNestLoop || + nodeTag(node) == T_AsofJoin || nodeTag(node) == T_VecAsofJoin) { context->join_type = nodeTag(node); } diff --git a/src/gausskernel/optimizer/plan/planrewrite.cpp b/src/gausskernel/optimizer/plan/planrewrite.cpp index f4ae1074b1a6b06249b06859a5f772a1492aa05e..ffa4c5950969d778474f616929ecaf4b5409f8a2 100644 --- a/src/gausskernel/optimizer/plan/planrewrite.cpp +++ b/src/gausskernel/optimizer/plan/planrewrite.cpp @@ -416,6 +416,20 @@ void fix_vars_plannode(PlannerInfo* root, Plan* node) fix_var_expr(root, (Node*)node->var_list); } + break; + case T_AsofJoin: + case T_VecAsofJoin: { + VecAsofJoin* aj = (VecAsofJoin*)node; + + /* Fix Vars in *Hash* clause */ + fix_var_expr(root, (Node*)aj->hashclauses); + + fix_var_expr(root, (Node*)aj->mergeclauses); + /* Fix Vars in *Join* clause */ + fix_var_expr(root, (Node*)aj->join.joinqual); + fix_var_expr(root, (Node*)aj->join.nulleqqual); + fix_var_expr(root, (Node*)node->var_list); + } break; case T_NestLoop: case T_VecNestLoop: { diff --git a/src/gausskernel/optimizer/plan/planstartwith.cpp b/src/gausskernel/optimizer/plan/planstartwith.cpp index f350a7473f13dec79cf765ab6c6a6c5e214a14a1..18f8ef5587544e702e17cce47fdb60bb1c182eed 100644 --- a/src/gausskernel/optimizer/plan/planstartwith.cpp +++ b/src/gausskernel/optimizer/plan/planstartwith.cpp @@ -1965,6 +1965,7 @@ static void BindPlanNodePseudoEntries(PlannerInfo *root, Plan *plan, /* process regular case */ case T_Hash: case T_HashJoin: + case T_AsofJoin: case T_SubqueryScan: case T_CteScan: case T_NestLoop: diff --git a/src/gausskernel/optimizer/plan/setrefs.cpp b/src/gausskernel/optimizer/plan/setrefs.cpp index 2fbc6d5d8364131aa28b07b5747495d2bf5a1b7b..4e00f79ebbfaad932c4fc42d030a74ba6a47bd12 100644 --- a/src/gausskernel/optimizer/plan/setrefs.cpp +++ b/src/gausskernel/optimizer/plan/setrefs.cpp @@ -524,6 +524,8 @@ static Plan* set_plan_refs(PlannerInfo* root, Plan* plan, int rtoffset) case T_VecNestLoop: case T_MergeJoin: case T_VecMergeJoin: + case T_AsofJoin: + case T_VecAsofJoin: case T_HashJoin: case T_VecHashJoin: set_join_references(root, (Join*)plan, rtoffset); @@ -1632,7 +1634,7 @@ static void set_join_references(PlannerInfo* root, Join* join, int rtoffset) MergeJoin* mj = (MergeJoin*)join; mj->mergeclauses = fix_join_expr(root, mj->mergeclauses, outer_itlist, inner_itlist, (Index)0, rtoffset); - } else if (IsA(join, HashJoin) || IsA(join, VecHashJoin)) { + } else if (IsA(join, HashJoin) || IsA(join, VecHashJoin) || IsA(join, AsofJoin) || IsA(join, VecAsofJoin) ) { HashJoin* hj = (HashJoin*)join; hj->hashclauses = fix_join_expr(root, hj->hashclauses, outer_itlist, inner_itlist, (Index)0, rtoffset); diff --git a/src/gausskernel/optimizer/plan/streamplan_utils.cpp b/src/gausskernel/optimizer/plan/streamplan_utils.cpp index d6abea04087b21d43ddba10dd96f9df34650e9a1..7b5cdd77ac65167fc5266f1d2b017b6606970595 100755 --- a/src/gausskernel/optimizer/plan/streamplan_utils.cpp +++ b/src/gausskernel/optimizer/plan/streamplan_utils.cpp @@ -142,6 +142,14 @@ List* check_op_list_template(Plan* result_plan, List* (*check_eval)(Node*)) res_list = list_concat_unique(res_list, check_eval((Node*)splan->join.joinqual)); res_list = list_concat_unique(res_list, check_eval((Node*)splan->hashclauses)); } break; + case T_AsofJoin: + case T_VecAsofJoin: { + VecAsofJoin *splan = (VecAsofJoin *)result_plan; + + res_list = list_concat_unique(res_list, check_eval((Node *)splan->join.joinqual)); + res_list = list_concat_unique(res_list, check_eval((Node *)splan->hashclauses)); + res_list = list_concat_unique(res_list, check_eval((Node *)splan->mergeclauses)); + } break; case T_Limit: case T_VecLimit: { Limit* splan = (Limit*)result_plan; @@ -354,6 +362,7 @@ void stream_path_walker(Path* path, ContainStreamContext* context) case T_NestLoop: case T_HashJoin: + case T_AsofJoin: case T_MergeJoin: { JoinPath* jp = (JoinPath*)path; @@ -1198,6 +1207,7 @@ bool is_local_redistribute_needed(Plan* subplan) break; } case T_HashJoin: + case T_AsofJoin: case T_NestLoop: { /* * If any side need to add local distribute, diff --git a/src/gausskernel/optimizer/plan/subselect.cpp b/src/gausskernel/optimizer/plan/subselect.cpp index 0d0cc9fb225088d950a71989fc614252018233c2..24862b38d2323169b1abb71916ed9631d0821996 100644 --- a/src/gausskernel/optimizer/plan/subselect.cpp +++ b/src/gausskernel/optimizer/plan/subselect.cpp @@ -3254,6 +3254,12 @@ static Bitmapset* finalize_plan(PlannerInfo* root, Plan* plan, Bitmapset* valid_ (void)finalize_primnode((Node*)((Join*)plan)->joinqual, &context); (void)finalize_primnode((Node*)((MergeJoin*)plan)->mergeclauses, &context); break; + + case T_AsofJoin: + (void)finalize_primnode((Node*)((Join*)plan)->joinqual, &context); + (void)finalize_primnode((Node*)((AsofJoin*)plan)->hashclauses, &context); + (void)finalize_primnode((Node*)((AsofJoin*)plan)->mergeclauses, &context); + break; case T_HashJoin: { (void)finalize_primnode((Node*)((Join*)plan)->joinqual, &context); diff --git a/src/gausskernel/optimizer/sqladvisor/sqladvisor_extract.cpp b/src/gausskernel/optimizer/sqladvisor/sqladvisor_extract.cpp index 045d483986f4e7184bbdc4b3ecfc688c3db52822..d608f41103414cc898d166984be547dc3e551bec 100644 --- a/src/gausskernel/optimizer/sqladvisor/sqladvisor_extract.cpp +++ b/src/gausskernel/optimizer/sqladvisor/sqladvisor_extract.cpp @@ -148,6 +148,23 @@ static List* extractNodeVecHashJoin(Plan* plan, List* ancestors, List* rtable, L return resSubplan; } +static List *extractNodeVecAsofJoin(Plan *plan, List *ancestors, List *rtable, List *subplans) +{ + List *resSubplan = NIL; + VecAsofJoin *vecAsofJoin = (VecAsofJoin *)plan; + + extractQual(((AsofJoin *)plan)->mergeclauses, plan, ancestors, rtable, subplans); + extractQual(((AsofJoin *)plan)->hashclauses, plan, ancestors, rtable, subplans); + extractQual(((AsofJoin *)plan)->join.joinqual, plan, ancestors, rtable, subplans); + extractQual(plan->qual, plan, ancestors, rtable, subplans); + resSubplan = extractSubplan((Expr *)vecAsofJoin->join.plan.targetlist, resSubplan, subplans); + resSubplan = extractSubplan((Expr *)vecAsofJoin->join.plan.qual, resSubplan, subplans); + resSubplan = extractSubplan((Expr *)vecAsofJoin->join.joinqual, resSubplan, subplans); + resSubplan = extractSubplan((Expr *)vecAsofJoin->join.nulleqqual, resSubplan, subplans); + resSubplan = extractSubplan((Expr *)vecAsofJoin->hashclauses, resSubplan, subplans); + return resSubplan; +} + static List* extractNodeHashJoin(Plan* plan, List* ancestors, List* rtable, List* subplans) { List* resSubplan = NIL; @@ -573,6 +590,10 @@ void extractNode(Plan* plan, List* ancestors, List* rtable, List* subplans) case T_HashJoin: { resSubplan = extractNodeHashJoin(plan, ancestors, rtable, subplans); } break; + case T_AsofJoin: + case T_VecAsofJoin: { + resSubplan = extractNodeVecAsofJoin(plan, ancestors, rtable, subplans); + } break; case T_VecAgg: { resSubplan = extractNodeVecAgg(plan, ancestors, rtable, subplans); } break; @@ -1158,8 +1179,10 @@ static double getPlanRows(Plan* plan) case T_VecNestLoop: case T_NestLoop: case T_VecMergeJoin: - case T_MergeJoin: + case T_MergeJoin: + case T_AsofJoin: case T_VecHashJoin: + case T_VecAsofJoin: case T_HashJoin: { rowsWeight = outerPlan(plan)->plan_rows + innerPlan(plan)->plan_rows; } break; diff --git a/src/gausskernel/optimizer/util/dataskew.cpp b/src/gausskernel/optimizer/util/dataskew.cpp index 6f7abcf2263ee113b63ab951265d1f588a477b7b..fd7156b9c6332aeb38ccb484baed24fc7673701a 100755 --- a/src/gausskernel/optimizer/util/dataskew.cpp +++ b/src/gausskernel/optimizer/util/dataskew.cpp @@ -1576,6 +1576,9 @@ List* SkewInfo::getJoinClause(Path* jpath) const } else if (T_MergePath == jpath->pathtype) { MergePath* mpath = (MergePath*)jpath; return mpath->path_mergeclauses; + } else if (T_AsofPath == jpath->pathtype) { + AsofPath* mpath = (AsofPath*)jpath; + return mpath->path_hashclauses; } else { NestPath* npath = (NestPath*)jpath; return npath->joinrestrictinfo; diff --git a/src/gausskernel/optimizer/util/joinskewinfo.cpp b/src/gausskernel/optimizer/util/joinskewinfo.cpp index 0ed7090d4e0ae9686204fe3d4426c3119cd208b0..b96715f5093caac378a5d50162321369cc131090 100644 --- a/src/gausskernel/optimizer/util/joinskewinfo.cpp +++ b/src/gausskernel/optimizer/util/joinskewinfo.cpp @@ -974,6 +974,7 @@ void JoinSkewInfo::traverseSubPath(Path* path) switch (path->type) { case T_NestPath: case T_MergePath: + case T_AsofPath: case T_HashPath: { JoinPath* jpath = (JoinPath*)path; @@ -1252,6 +1253,7 @@ bool JoinSkewInfo::checkPathRedundant(List* streamKeys, Path* path) switch (path->pathtype) { case T_NestLoop: case T_MergeJoin: + case T_AsofJoin: case T_HashJoin: { JoinPath* jpath = (JoinPath*)path; @@ -1590,6 +1592,7 @@ void AggSkewInfo::traverseSubPlan(Plan* plan) switch (nodeTag(plan)) { case T_NestLoop: case T_MergeJoin: + case T_AsofJoin: case T_HashJoin: { Join* join = (Join*)plan; if (IS_JOIN_OUTER(join->jointype)) { diff --git a/src/gausskernel/optimizer/util/learn/encoding.cpp b/src/gausskernel/optimizer/util/learn/encoding.cpp index 289a09e8b867d39a6e4c13e21636d053b6df82a9..2f9c25e3b976d84b58a4b79dfa8afc387d6d0885 100644 --- a/src/gausskernel/optimizer/util/learn/encoding.cpp +++ b/src/gausskernel/optimizer/util/learn/encoding.cpp @@ -73,6 +73,8 @@ const OperationInfo G_OPERATION_INFO_TABLE[G_MAX_OPERATION_NUMBER] = { {T_VecMergeJoin, TEXT_OPTNAME_JOIN, TEXT_STRATEGY_JOIN_MERGE}, {T_HashJoin, TEXT_OPTNAME_JOIN, TEXT_STRATEGY_JOIN_HASH}, {T_VecHashJoin, TEXT_OPTNAME_JOIN, TEXT_STRATEGY_JOIN_HASH}, + {T_AsofJoin, TEXT_OPTNAME_JOIN, TEXT_STRATEGY_JOIN_ASOF}, + {T_VecAsofJoin, TEXT_OPTNAME_JOIN, TEXT_STRATEGY_JOIN_ASOF}, {T_CStoreScan, TEXT_OPTNAME_SCAN, TEXT_STRATEGY_SCAN_SEQ}, {T_SeqScan, TEXT_OPTNAME_SCAN, TEXT_STRATEGY_SCAN_SEQ}, #ifdef USE_SPQ @@ -936,6 +938,12 @@ static void GetSpecialPlanOptCondition(PlanState* planstate, StringInfo conditio case T_MergeJoin: GetPlanOptConditionFromQual(((MergeJoin*)plan)->mergeclauses, planstate, condition, maxlen, rtable); break; + case T_AsofJoin: + case T_VecAsofJoin: + GetPlanOptConditionFromQual(((VecAsofJoin*)plan)->hashclauses, planstate, condition, maxlen, rtable); + GetPlanOptConditionFromQual(((VecAsofJoin*)plan)->mergeclauses, planstate, condition, maxlen, rtable); + GetPlanOptConditionFromQual(((VecAsofJoin*)plan)->join.joinqual, planstate, condition, maxlen, rtable); + break; case T_HashJoin: case T_VecHashJoin: GetPlanOptConditionFromQual(((HashJoin*)plan)->hashclauses, planstate, condition, maxlen, rtable); @@ -991,6 +999,8 @@ static void GetPlanOptCondition(PlanState* planstate, StringInfo condition, int case T_NestLoop: case T_VecMergeJoin: case T_MergeJoin: + case T_AsofJoin: + case T_VecAsofJoin: case T_HashJoin: case T_VecHashJoin: case T_BaseResult: diff --git a/src/gausskernel/optimizer/util/optcommon.cpp b/src/gausskernel/optimizer/util/optcommon.cpp index 2aa56444f982a80771918376986d9bca1a21eadd..62616edfda3777dfb2855cc0d3f49180f00ce941 100755 --- a/src/gausskernel/optimizer/util/optcommon.cpp +++ b/src/gausskernel/optimizer/util/optcommon.cpp @@ -114,6 +114,11 @@ void GetPlanNodePlainText( *sname = *pt_operation = "Vector Hash Join"; } break; + case T_AsofJoin: + case T_VecAsofJoin: + *pname = "Vector Asof"; + *sname = *pt_operation = "Vector Asof Join"; + break; case T_SeqScan: *pt_operation = "TABLE ACCESS"; if (!((Scan*)plan)->tablesample) { diff --git a/src/gausskernel/optimizer/util/optimizerdebug.cpp b/src/gausskernel/optimizer/util/optimizerdebug.cpp index 35e3d404d4877478a373c4b7cd73296d99b84aea..b55fdc6deff50c27254d93555e64c3ec9b1985e6 100644 --- a/src/gausskernel/optimizer/util/optimizerdebug.cpp +++ b/src/gausskernel/optimizer/util/optimizerdebug.cpp @@ -192,6 +192,9 @@ static const char* debug1_print_pathtype(Path* path, NodeTag pathtype, Path** su debug1_print_jointype(path, &ptype); *join = true; break; + case T_AsofJoin: + *join = true; + break; #ifdef STREAMPLAN case T_Stream: case T_VecStream: { diff --git a/src/gausskernel/optimizer/util/orclauses.cpp b/src/gausskernel/optimizer/util/orclauses.cpp index 399b032bef8d41e77aac34a53e384a2440bc74d2..be233c5e3d975b25740e5533a7e40a8334cd04bd 100644 --- a/src/gausskernel/optimizer/util/orclauses.cpp +++ b/src/gausskernel/optimizer/util/orclauses.cpp @@ -254,7 +254,7 @@ static void consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel, Expr *orc * Build a RestrictInfo from the new OR clause. We can assume it's valid * as a base restriction clause. */ - or_rinfo = make_restrictinfo(orclause, true, false, false, join_or_rinfo->security_level, NULL, NULL, NULL); + or_rinfo = make_restrictinfo(orclause, true, false, false, join_or_rinfo->security_level, NULL, NULL, NULL, false); /* * Estimate its selectivity. (We could have done this earlier, but doing diff --git a/src/gausskernel/optimizer/util/pathnode.cpp b/src/gausskernel/optimizer/util/pathnode.cpp index 675228c42c0d95cabc71f438a1b8a2c4bfb06bcd..7d91cffba7a16742f83e5bd4c7cf39702d76d0a7 100755 --- a/src/gausskernel/optimizer/util/pathnode.cpp +++ b/src/gausskernel/optimizer/util/pathnode.cpp @@ -229,8 +229,10 @@ PathCostComparison compare_join_single_node_distribution(Path* path1, Path* path return COSTS_DIFFERENT; } - if((path1->pathtype != T_NestLoop && path1->pathtype != T_MergeJoin && path1->pathtype != T_HashJoin) - || (path2->pathtype != T_NestLoop && path2->pathtype != T_MergeJoin && path2->pathtype != T_HashJoin)) { + if((path1->pathtype != T_NestLoop && path1->pathtype != T_MergeJoin + && path1->pathtype != T_HashJoin && path1->pathtype != T_AsofJoin ) + || (path2->pathtype != T_NestLoop && path2->pathtype != T_MergeJoin + && path2->pathtype != T_HashJoin && path2->pathtype != T_AsofJoin)) { return COSTS_DIFFERENT; } @@ -4523,6 +4525,85 @@ HashPath* create_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType return pathnode; } +/* + * create_asofjoin_path + * Creates a pathnode corresponding to a asof join between two relations. + * + * 'joinrel' is the join relation + * 'jointype' is the type of join required + * 'workspace' is the result from initial_cost_hashjoin + * 'extra' contains various information about the join + * 'semifactors' contains valid data if jointype is SEMI or ANTI + * 'outer_path' is the cheapest outer path + * 'inner_path' is the cheapest inner path + * 'restrict_clauses' are the RestrictInfo nodes to apply at the join + * 'required_outer' is the set of required outer rels + * 'hashclauses' are the RestrictInfo nodes to use as hash clauses + * (this should be a subset of the restrict_clauses list) + * 'mergeclauses' are the RestrictInfo nodes to use as merge clauses + * (this should be a subset of the restrict_clauses list) + * 'outersortkeys' are the sort varkeys for the outer relation + * 'innersortkeys' are the sort varkeys for the inner relation + */ +AsofPath* create_asofjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinCostWorkspace* workspace, + JoinPathExtraData *extra, Path* outer_path, Path* inner_path, List* restrict_clauses, Relids required_outer, + List* hashclauses, List* mergeclauses, List* outersortkeys, List* innersortkeys, int dop) +{ + AsofPath* pathnode = makeNode(AsofPath); + bool try_eq_related_indirectly = false; + + pathnode->jpath.path.pathtype = T_AsofJoin; + pathnode->jpath.path.parent = joinrel; + pathnode->jpath.path.pathtarget = joinrel->reltarget; + pathnode->jpath.path.param_info = + get_joinrel_parampathinfo(root, joinrel, outer_path, inner_path, extra->sjinfo, required_outer, &restrict_clauses); + + /* + * A hashjoin never has pathkeys, since its output ordering is + * unpredictable due to possible batching. XXX If the inner relation is + * small enough, we could instruct the executor that it must not batch, + * and then we could assume that the output inherits the outer relation's + * ordering, which might save a sort step. However there is considerable + * downside if our estimate of the inner relation size is badly off. For + * the moment we don't risk it. (Note also that if we wanted to take this + * seriously, joinpath.c would have to consider many more paths for the + * outer rel than it does now.) + */ + pathnode->jpath.path.pathkeys = NIL; + pathnode->jpath.path.dop = dop; + pathnode->jpath.jointype = jointype; + pathnode->jpath.inner_unique = extra->inner_unique; + pathnode->jpath.outerjoinpath = outer_path; + pathnode->jpath.innerjoinpath = inner_path; + pathnode->jpath.joinrestrictinfo = restrict_clauses; + pathnode->path_hashclauses = hashclauses; + pathnode->path_mergeclauses = mergeclauses; + pathnode->outersortkeys = outersortkeys; + pathnode->innersortkeys = innersortkeys; + + pathnode->jpath.path.exec_type = SetExectypeForJoinPath(inner_path, outer_path); + +#ifdef STREAMPLAN + pathnode->jpath.path.locator_type = locator_type_join(inner_path->locator_type, outer_path->locator_type); + ProcessRangeListJoinType(&pathnode->jpath.path, outer_path, inner_path); + + if (IS_STREAM_PLAN) { + /* add location information for asof join path */ + Distribution* distribution = ng_get_join_distribution(outer_path, inner_path); + ng_copy_distribution(&pathnode->jpath.path.distribution, distribution); + } +#endif + + /* final_cost_asofjoin will fill in pathnode->num_batches */ + final_cost_asofjoin(root, + pathnode, + workspace, + extra, + dop); + + return pathnode; +} + /* * create_projection_path * Creates a pathnode that represents performing a projection. @@ -6752,6 +6833,33 @@ static JoinPath* add_join_redistribute_path(PlannerInfo* root, RelOptInfo* joinr required_outer, hashclauses, joinDop); + } else if (nodetag == T_AsofJoin) { + initial_cost_asofjoin(root, + workspace, + jointype, + hashclauses, + stream_path_outer, + stream_path_inner, + restrictlist, + outer_pathkeys, + inner_pathkeys, + &extra, + joinDop); + + joinpath = (JoinPath*)create_asofjoin_path(root, + joinrel, + jointype, + workspace, + &extra, + stream_path_outer, + stream_path_inner, + restrictlist, + required_outer, + hashclauses, + restrictlist, + outer_pathkeys, + inner_pathkeys, + joinDop); } else { initial_cost_nestloop( root, workspace, jointype, stream_path_outer, stream_path_inner, &extra, joinDop); @@ -8387,6 +8495,7 @@ void add_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype list_free_ext(rrinfo_outer); } + /* * @Description: * Add nestloop join path with broadcast. diff --git a/src/gausskernel/optimizer/util/planmem_walker.cpp b/src/gausskernel/optimizer/util/planmem_walker.cpp index fd9f1baba29969f9185b4aedfdd2b12d1fffbf59..34856fb776104e22bfb790b67d33623ffdd61b5f 100644 --- a/src/gausskernel/optimizer/util/planmem_walker.cpp +++ b/src/gausskernel/optimizer/util/planmem_walker.cpp @@ -424,7 +424,15 @@ bool plan_tree_walker(Node* node, MethodWalker walker, void* context) return true; #endif break; - + case T_AsofJoin: + case T_VecAsofJoin: + if (walk_join_node_fields((Join*)node, walker, context)) + return true; + if (p2walker((Node*)((VecAsofJoin*)node)->hashclauses, context)) + return true; + if (p2walker((Node*)((VecAsofJoin*)node)->mergeclauses, context)) + return true; + break; case T_VecToRow: case T_RowToVec: case T_VecMaterial: diff --git a/src/gausskernel/optimizer/util/restrictinfo.cpp b/src/gausskernel/optimizer/util/restrictinfo.cpp index ab7153af049fafb5dded3cbecd3cd02829002801..a769ab94db503dae4feee3d7a876662dd13a22ac 100644 --- a/src/gausskernel/optimizer/util/restrictinfo.cpp +++ b/src/gausskernel/optimizer/util/restrictinfo.cpp @@ -23,9 +23,9 @@ static RestrictInfo* make_restrictinfo_internal(Expr* clause, Expr* orclause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, Index security_level, Relids required_relids, Relids outer_relids, - Relids nullable_relids); + Relids nullable_relids, bool is_asof); static Expr* make_sub_restrictinfos(Expr* clause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, - Index sucurity_level, Relids required_relids, Relids outer_relids, Relids nullable_relids); + Index sucurity_level, Relids required_relids, Relids outer_relids, Relids nullable_relids, bool is_asof); /* * @@ -42,7 +42,7 @@ static Expr* make_sub_restrictinfos(Expr* clause, bool is_pushed_down, bool oute * later. */ RestrictInfo* make_restrictinfo(Expr* clause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, - Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids) + Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids, bool is_asof) { /* * If it's an OR clause, build a modified copy with RestrictInfos inserted @@ -56,7 +56,8 @@ RestrictInfo* make_restrictinfo(Expr* clause, bool is_pushed_down, bool outerjoi security_level, required_relids, outer_relids, - nullable_relids); + nullable_relids, + is_asof); /* Shouldn't be an AND clause, else AND/OR flattening messed up */ AssertEreport(!and_clause((Node*)clause), MOD_OPT, ""); @@ -69,7 +70,8 @@ RestrictInfo* make_restrictinfo(Expr* clause, bool is_pushed_down, bool outerjoi security_level, required_relids, outer_relids, - nullable_relids); + nullable_relids, + is_asof); } /* @@ -188,7 +190,7 @@ List* make_restrictinfo_from_bitmapqual(Path* bitmapqual, bool is_pushed_down, b else { /* Here's the magic part not available to outside callers */ result = list_make1(make_restrictinfo_internal( - make_orclause(withoutris), make_orclause(withris), is_pushed_down, false, false, 0, NULL, NULL, NULL)); + make_orclause(withoutris), make_orclause(withris), is_pushed_down, false, false, 0, NULL, NULL, NULL, false)); } } else if (IsA(bitmapqual, IndexPath)) { IndexPath* ipath = (IndexPath*)bitmapqual; @@ -206,7 +208,7 @@ List* make_restrictinfo_from_bitmapqual(Path* bitmapqual, bool is_pushed_down, b */ if (!predicate_implied_by(list_make1(pred), result)) result = - lappend(result, make_restrictinfo(pred, is_pushed_down, false, false, 0, NULL, NULL, NULL)); + lappend(result, make_restrictinfo(pred, is_pushed_down, false, false, 0, NULL, NULL, NULL, false)); } } } else { @@ -255,7 +257,7 @@ List* make_restrictinfos_from_actual_clauses(PlannerInfo* root, List* clause_lis root->hasPseudoConstantQuals = true; } - rinfo = make_restrictinfo(clause, true, false, pseudoconstant, 0, NULL, NULL, NULL); + rinfo = make_restrictinfo(clause, true, false, pseudoconstant, 0, NULL, NULL, NULL, false); result = lappend(result, rinfo); } return result; @@ -268,7 +270,7 @@ List* make_restrictinfos_from_actual_clauses(PlannerInfo* root, List* clause_lis */ static RestrictInfo* make_restrictinfo_internal(Expr* clause, Expr* orclause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, Index security_level, Relids required_relids, Relids outer_relids, - Relids nullable_relids) + Relids nullable_relids, bool is_asof) { RestrictInfo* restrictinfo = makeNode(RestrictInfo); errno_t rc = EOK; /* Initialize rc to keep compiler slient */ @@ -282,6 +284,7 @@ static RestrictInfo* make_restrictinfo_internal(Expr* clause, Expr* orclause, bo restrictinfo->security_level = security_level; restrictinfo->outer_relids = outer_relids; restrictinfo->nullable_relids = nullable_relids; + restrictinfo->is_asof = is_asof; /* * If it's potentially delayable by lower-level security quals, figure out @@ -380,7 +383,7 @@ static RestrictInfo* make_restrictinfo_internal(Expr* clause, Expr* orclause, bo * contained rels. */ static Expr* make_sub_restrictinfos(Expr* clause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, - Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids) + Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids, bool is_asof) { if (or_clause((Node*)clause)) { List* orlist = NIL; @@ -395,7 +398,8 @@ static Expr* make_sub_restrictinfos(Expr* clause, bool is_pushed_down, bool oute security_level, NULL, outer_relids, - nullable_relids)); + nullable_relids, + is_asof)); return (Expr*)make_restrictinfo_internal(clause, make_orclause(orlist), is_pushed_down, @@ -404,7 +408,8 @@ static Expr* make_sub_restrictinfos(Expr* clause, bool is_pushed_down, bool oute security_level, required_relids, outer_relids, - nullable_relids); + nullable_relids, + is_asof); } else if (and_clause((Node*)clause)) { List* andlist = NIL; ListCell* temp = NULL; @@ -418,7 +423,8 @@ static Expr* make_sub_restrictinfos(Expr* clause, bool is_pushed_down, bool oute security_level, required_relids, outer_relids, - nullable_relids)); + nullable_relids, + is_asof)); return make_andclause(andlist); } else return (Expr*)make_restrictinfo_internal(clause, @@ -429,7 +435,8 @@ static Expr* make_sub_restrictinfos(Expr* clause, bool is_pushed_down, bool oute security_level, required_relids, outer_relids, - nullable_relids); + nullable_relids, + is_asof); } /* diff --git a/src/gausskernel/runtime/executor/execProcnode.cpp b/src/gausskernel/runtime/executor/execProcnode.cpp index f78006546bdc5eab6ae4ea83e4111deda11a6363..1279b94d8dbc55030be8af228990f3690d70d69c 100755 --- a/src/gausskernel/runtime/executor/execProcnode.cpp +++ b/src/gausskernel/runtime/executor/execProcnode.cpp @@ -134,6 +134,7 @@ #include "vecexecutor/vecnodesort.h" #include "vecexecutor/vecmodifytable.h" #include "vecexecutor/vechashjoin.h" +#include "vecexecutor/vecasofjoin.h" #include "vecexecutor/vechashagg.h" #include "vecexecutor/vecpartiterator.h" #include "vecexecutor/vecappend.h" @@ -411,6 +412,9 @@ PlanState* ExecInitNodeByType(Plan* node, EState* estate, int eflags) #endif /* ENABLE_MULTIPLE_NODES */ case T_VecHashJoin: return (PlanState*)ExecInitVecHashJoin((VecHashJoin*)node, estate, eflags); + case T_AsofJoin: + case T_VecAsofJoin: + return (PlanState*)ExecInitVecAsofJoin((VecAsofJoin*)node, estate, eflags); case T_VecAgg: return (PlanState*)ExecInitVecAggregation((VecAgg*)node, estate, eflags); case T_CStoreIndexScan: @@ -1304,6 +1308,10 @@ static void ExecEndNodeByType(PlanState* node) ExecEndVecHashJoin((VecHashJoinState*)node); break; + case T_VecAsofJoinState: + ExecEndVecAsofJoin((VecAsofJoinState*)node); + break; + case T_VecAggState: ExecEndVecAggregation((VecAggState*)node); break; diff --git a/src/gausskernel/runtime/executor/instrument.cpp b/src/gausskernel/runtime/executor/instrument.cpp index eb600018d4db748440e4f73e4868628ff29293fd..979ae1086bfb04dea877cc3eea0ccc3ffe83589a 100644 --- a/src/gausskernel/runtime/executor/instrument.cpp +++ b/src/gausskernel/runtime/executor/instrument.cpp @@ -970,6 +970,10 @@ Instrumentation* ThreadInstrumentation::allocInstrSlot(int plan_node_id, int par pname = "Vector Nest Loop"; plan_type = JOIN_OP; break; + case T_VecAsofJoin: + pname = "Vector Asof Join"; + plan_type = JOIN_OP; + break; case T_MergeJoin: pname = "Merge Join"; /* "Join" gets added by jointype switch */ plan_type = JOIN_OP; diff --git a/src/gausskernel/runtime/vecexecutor/vecexecutor.cpp b/src/gausskernel/runtime/vecexecutor/vecexecutor.cpp index 02740e7c47d81302d0c28fd7ce9f7f8316ef41b2..a628540035bc5fcc07c4760eba27d01262b688c1 100644 --- a/src/gausskernel/runtime/vecexecutor/vecexecutor.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecexecutor.cpp @@ -53,6 +53,7 @@ #include "vecexecutor/vecnodesort.h" #include "vecexecutor/vecmodifytable.h" #include "vecexecutor/vechashjoin.h" +#include "vecexecutor/vecasofjoin.h" #include "vecexecutor/vechashagg.h" #include "vecexecutor/vecpartiterator.h" #include "vecexecutor/vecappend.h" @@ -86,6 +87,7 @@ VectorEngineFunc VectorEngineRunner[] = { reinterpret_cast(ExecRowToVec), reinterpret_cast(ExecVecAggregation), reinterpret_cast(ExecVecHashJoin), + reinterpret_cast(ExecVecAsofJoin), reinterpret_cast(ExecVecStream), reinterpret_cast(ExecVecSort), reinterpret_cast(ExecVecForeignScan), @@ -386,6 +388,10 @@ void ExecEarlyFreeBody(PlanState* node) ExecEarlyFreeVecHashJoin((VecHashJoinState*)node); break; + case T_VecAsofJoinState: + ExecEarlyFreeVecAsofJoin((VecAsofJoinState*)node); + break; + case T_HashJoinState: ExecEarlyFreeHashJoin((HashJoinState*)node); break; diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/Makefile b/src/gausskernel/runtime/vecexecutor/vecnode/Makefile index d0a0afc415c305dd07beac92565548bee1dcac74..2c2adc41b513f92755e474147492a52e9199bc47 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/Makefile +++ b/src/gausskernel/runtime/vecexecutor/vecnode/Makefile @@ -30,7 +30,7 @@ ifneq "$(MAKECMDGOALS)" "clean" endif endif OBJS = vechashtable.o vecagg.o vecplainagg.o vecsortagg.o vechashagg.o vecsort.o vechashjoin.o vecstream.o vectortorow.o \ - veccstore.o veccstoreindexscan.o vecrowtovector.o \ + veccstore.o veccstoreindexscan.o vecrowtovector.o vecasofjoin.o\ vecforeignscan.o vecmodifytable.o vecremotequery.o vecresult.o vecscan.o vecsubqueryscan.o vecpartiterator.o \ vecrescan.o vecappend.o veclimit.o vecconstraints.o vecsetop.o vecgroup.o vecunique.o vecgrpuniq.o vecmaterial.o vecnestloop.o \ vecstore.o vecmergejoin.o vecwindowagg.o veccstoreindexheapscan.o veccstoreindexctidscan.o veccstoreindexand.o veccstoreindexor.o \ diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecasofjoin.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecasofjoin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28de0b983973f5b7bee81a06e2bd9cba4f9bd420 --- /dev/null +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecasofjoin.cpp @@ -0,0 +1,1295 @@ +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * --------------------------------------------------------------------------------------- + * + * vecasofjoin.cpp + * + * + * IDENTIFICATION + * Code/src/gausskernel/runtime/vecexecutor/vecnode/vecasofjoin.cpp + * + * --------------------------------------------------------------------------------------- + */ +#include "codegen/gscodegen.h" +#include "codegen/vecexprcodegen.h" +#include "codegen/vechashjoincodegen.h" + +#include "catalog/pg_type.h" +#include "nodes/plannodes.h" +#include "access/nbtree.h" +#include "vecexecutor/vechashjoin.h" +#include "vecexecutor/vecasofjoin.h" +#include "vecexecutor/vechashtable.h" +#include "vecexecutor/vecexecutor.h" +#include "vecexecutor/vectorbatch.inl" +#include "vectorsonic/vsonichash.h" +#include "postgres.h" +#include "parser/parse_oper.h" +#include "utils/rel_gs.h" +#include "knl/knl_variable.h" +#include "executor/executor.h" +#include "commands/explain.h" +#include "utils/anls_opt.h" +#include "utils/biginteger.h" +#include "utils/builtins.h" +#include "lib/stringinfo.h" +#include "miscadmin.h" +#include "vecexecutor/vecexpression.h" +#include "fmgr.h" +#include "foreign/fdwapi.h" +#include "tcop/utility.h" +#include "utils/lsyscache.h" +#include "utils/typcache.h" +#include "utils/memprot.h" +#ifdef PGXC +#include "catalog/pgxc_node.h" +#include "pgxc/pgxc.h" +#endif + +#define INSTR (m_runtime->js.ps.instrument) + +#define BTLess ((char *)"<") +#define BTLessEqual ((char *)"<=") +#define BTGreaterEqual ((char *)">=") +#define BTGreater ((char *)">") +const uint8 CONSTNUM2 = 2; +const uint8 PARTMAXNUM = 32; + +VecAsofJoinState *ExecInitVecAsofJoin(VecAsofJoin *node, EState *estate, int eflags) +{ + VecAsofJoinState *asof_state = NULL; + Plan *outer_node = NULL; + Plan *inner_node = NULL; + List *lclauses = NIL; + List *rclauses = NIL; + List *lsclauses = NIL; + List *rsclauses = NIL; + List *hoperators = NIL; + ListCell *l = NULL; + ListCell *ls = NULL; + FmgrInfo *eqfunctions = NULL; + ListCell *ho = NULL; + int i; + int key_num; + + /* check for unsupported flags */ + Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); + + asof_state = makeNode(VecAsofJoinState); + asof_state->js.ps.plan = (Plan *)node; + asof_state->js.ps.state = estate; + asof_state->js.ps.vectorized = true; + asof_state->hashTbl = NULL; + asof_state->cmpName = NULL; + + /* + * Miscellaneous initialization + * + * create expression context for node + */ + ExecAssignExprContext(estate, &asof_state->js.ps); + + /* + * initialize child expressions + */ + asof_state->js.ps.targetlist = (List *)ExecInitVecExpr((Expr *)node->join.plan.targetlist, (PlanState *)asof_state); + asof_state->js.ps.qual = (List *)ExecInitVecExpr((Expr *)node->join.plan.qual, (PlanState *)asof_state); + asof_state->js.jointype = node->join.jointype; + asof_state->js.joinqual = (List *)ExecInitVecExpr((Expr *)node->join.joinqual, (PlanState *)asof_state); + asof_state->js.nulleqqual = (List *)ExecInitVecExpr((Expr *)node->join.nulleqqual, (PlanState *)asof_state); + asof_state->hashclauses = (List *)ExecInitVecExpr((Expr *)node->hashclauses, (PlanState *)asof_state); + + /* + * initialize child nodes + * + * Note: we could suppress the REWIND flag for the inner input, which + * would amount to betting that the hash will be a single batch. Not + * clear if this would be a win or not. + */ + outer_node = outerPlan(node); + inner_node = innerPlan(node); + + outerPlanState(asof_state) = ExecInitNode(outer_node, estate, eflags); + innerPlanState(asof_state) = ExecInitNode(inner_node, estate, eflags); + + /* + * tuple table initialization + */ + ExecInitResultTupleSlot(estate, &asof_state->js.ps); + + /* + * initialize tuple type and projection info + * result tupleSlot only contains virtual tuple, so the default + * tableAm type is set to HEAP. + */ + ExecAssignResultTypeFromTL(&asof_state->js.ps); + + if (asof_state->js.ps.targetlist) { + asof_state->js.ps.ps_ProjInfo = + ExecBuildVecProjectionInfo(asof_state->js.ps.targetlist, node->join.plan.qual, + asof_state->js.ps.ps_ExprContext, asof_state->js.ps.ps_ResultTupleSlot, NULL); + + ExecAssignVectorForExprEval(asof_state->js.ps.ps_ProjInfo->pi_exprContext); + } else { + ExecAssignVectorForExprEval(asof_state->js.ps.ps_ExprContext); + + asof_state->js.ps.ps_ProjInfo = NULL; + } + + lclauses = NIL; + rclauses = NIL; + hoperators = NIL; + foreach (l, asof_state->hashclauses) { + FuncExprState *fstate = (FuncExprState *)lfirst(l); + OpExpr *hclause = NULL; + + Assert(IsA(fstate, FuncExprState)); + hclause = (OpExpr *)fstate->xprstate.expr; + Assert(IsA(hclause, OpExpr)); + lclauses = lappend(lclauses, linitial(fstate->args)); + rclauses = lappend(rclauses, lsecond(fstate->args)); + hoperators = lappend_oid(hoperators, hclause->opno); + } + + lsclauses = NIL; + rsclauses = NIL; + foreach (ls, asof_state->js.joinqual) { + FuncExprState *fstate = (FuncExprState *)lfirst(ls); + OpExpr *hclause = NULL; + + Assert(IsA(fstate, FuncExprState)); + hclause = (OpExpr *)fstate->xprstate.expr; + Assert(IsA(hclause, OpExpr)); + asof_state->cmpName = get_opname(hclause->opno); + lsclauses = lappend(lsclauses, linitial(fstate->args)); + rsclauses = lappend(rsclauses, lsecond(fstate->args)); + break; + } + + key_num = list_length(rclauses); + eqfunctions = (FmgrInfo *)palloc(key_num * sizeof(FmgrInfo)); + i = 0; + foreach (ho, hoperators) { + Oid hashop = lfirst_oid(ho); + Oid eq_function; + + eq_function = get_opcode(hashop); + fmgr_info(eq_function, &eqfunctions[i]); + i++; + } + + asof_state->hj_OuterHashKeys = lclauses; + asof_state->hj_InnerHashKeys = rclauses; + asof_state->hj_HashOperators = hoperators; + asof_state->eqfunctions = eqfunctions; + asof_state->hj_OuterSortKeys = lsclauses; + asof_state->hj_InnerSortKeys = rsclauses; + + return asof_state; +} + +VectorBatch *ExecVecAsofJoin(VecAsofJoinState *node) +{ + int64 rows = 0; + + for (;;) { + switch (node->joinState) { + case ASOF_PART: { + if (node->hashTbl == NULL) + node->hashTbl = New(CurrentMemoryContext) AsofHashJoin(INIT_DATUM_ARRAY_SIZE, node); + + ((AsofHashJoin *)(node->hashTbl))->BuildInner(); + rows = ((AsofHashJoin *)(node->hashTbl))->getRows(); + + /* Early free right tree after hash table built */ + ExecEarlyFree(innerPlanState(node)); + + EARLY_FREE_LOG(elog(LOG, + "Early Free: Hash Table for %sHashJoin" + " is built at node %d, memory used %d MB.", + JOIN_NAME, (node->js.ps.plan)->plan_node_id, getSessionMemoryUsageMB())); + + if (0 == rows) { + // When hash table size is zero, no need to fetch left tree any more and + // should deinit the consumer in left tree earlier. + // + ExecEarlyDeinitConsumer((PlanState *)node); + return NULL; + } + + ((AsofHashJoin *)(node->hashTbl))->BuildOuter(); + + /* Early free left tree after hash table built */ + ExecEarlyFree(outerPlanState(node)); + + EARLY_FREE_LOG(elog(LOG, + "Early Free: Hash Table for %sHashJoin" + " is built at node %d, memory used %d MB.", + JOIN_NAME, (node->js.ps.plan)->plan_node_id, getSessionMemoryUsageMB())); + + node->joinState = ASOF_MERGE; + } break; + + case ASOF_MERGE: { + instr_time start_time; + (void)INSTR_TIME_SET_CURRENT(start_time); + VectorBatch *result = NULL; + + result = ((AsofHashJoin *)(node->hashTbl))->Probe(); + ((AsofHashJoin *)(node->hashTbl))->m_probe_time += elapsed_time(&start_time); + + return result; + } + default: + break; + } + } +} + +void ExecEndVecAsofJoin(VecAsofJoinState *node) +{ + void *tbl = node->hashTbl; + + if (tbl != NULL) { + AsofHashJoin *hj_tbl = (AsofHashJoin *)tbl; + hj_tbl->freeMemoryContext(); + } + + ExecFreeExprContext(&node->js.ps); + ExecEndNode(outerPlanState(node)); + ExecEndNode(innerPlanState(node)); +} + +/* + * @Description: radix hash join constructor. + * In hash join constructor, hashContext manages hash table and partition. + * Other variables are under hash join node context. + */ +AsofHashJoin::AsofHashJoin(int size, VecAsofJoinState *node) + : SonicHash(size), + m_complicatekey(false), + m_runtime(node), + m_matchPartIdx(0), + m_spillCount(0), + m_spillSize(0), + m_partNum(0), + m_probeIdx(0), + m_build_time(0.0), + m_probe_time(0.0) +{ + AddControlMemoryContext(m_runtime->js.ps.instrument, m_memControl.hashContext); + m_compareValue = comparisonValue(node->cmpName); + m_buildOp.tupleDesc = outerPlanState(m_runtime)->ps_ResultTupleSlot->tts_tupleDescriptor; + m_probeOp.tupleDesc = innerPlanState(m_runtime)->ps_ResultTupleSlot->tts_tupleDescriptor; + m_buildSortOp.tupleDesc = m_buildOp.tupleDesc; + m_probeSortOp.tupleDesc = m_probeOp.tupleDesc; + m_buildOp.cols = m_buildOp.tupleDesc->natts; + m_probeOp.cols = m_probeOp.tupleDesc->natts; + + m_buildOp.batch = New(CurrentMemoryContext) VectorBatch(CurrentMemoryContext, m_buildOp.tupleDesc); + m_probeOp.batch = New(CurrentMemoryContext) VectorBatch(CurrentMemoryContext, m_probeOp.tupleDesc); + m_lastBatch = New(CurrentMemoryContext) VectorBatch(CurrentMemoryContext, m_probeOp.tupleDesc); + m_eqfunctions = m_runtime->eqfunctions; + + /* init hash key index */ + m_buildOp.keyNum = list_length(m_runtime->hj_OuterHashKeys); + m_probeOp.keyNum = m_buildOp.keyNum; + m_buildOp.keyIndx = (uint16 *)palloc0(sizeof(uint16) * m_buildOp.keyNum); + m_probeOp.keyIndx = (uint16 *)palloc0(sizeof(uint16) * m_buildOp.keyNum); + m_buildOp.oKeyIndx = (uint16 *)palloc0(sizeof(uint16) * m_buildOp.keyNum); + m_probeOp.oKeyIndx = (uint16 *)palloc0(sizeof(uint16) * m_buildOp.keyNum); + + m_integertype = (bool *)palloc(sizeof(bool) * m_buildOp.keyNum); + for (int i = 0; i < m_buildOp.keyNum; i++) { + m_integertype[i] = true; + } + + setHashIndex(m_buildOp.keyIndx, m_buildOp.oKeyIndx, m_runtime->hj_OuterHashKeys); + setHashIndex(m_probeOp.keyIndx, m_probeOp.oKeyIndx, m_runtime->hj_InnerHashKeys); + /* init sort key index */ + m_buildSortOp.numsortkeys = list_length(m_runtime->hj_OuterSortKeys) + m_buildOp.keyNum; + m_probeSortOp.numsortkeys = m_buildSortOp.numsortkeys; + m_buildSortOp.sortColIdx = (AttrNumber *)palloc0(sizeof(AttrNumber) * m_buildSortOp.numsortkeys); + m_probeSortOp.sortColIdx = (AttrNumber *)palloc0(sizeof(AttrNumber) * m_buildSortOp.numsortkeys); + m_buildSortOp.sortOperators = (Oid *)palloc0(sizeof(Oid) * m_buildSortOp.numsortkeys); + m_probeSortOp.sortOperators = (Oid *)palloc0(sizeof(Oid) * m_buildSortOp.numsortkeys); + m_buildSortOp.collations = (Oid *)palloc0(sizeof(Oid) * m_buildSortOp.numsortkeys); + m_probeSortOp.collations = (Oid *)palloc0(sizeof(Oid) * m_buildSortOp.numsortkeys); + m_buildSortOp.nullsFirst = (bool *)palloc0(sizeof(bool) * m_buildSortOp.numsortkeys); + m_probeSortOp.nullsFirst = (bool *)palloc0(sizeof(bool) * m_buildSortOp.numsortkeys); + + setSortIndex(m_buildSortOp.sortColIdx, m_buildSortOp.collations, m_buildSortOp.nullsFirst, + m_buildSortOp.sortOperators, m_runtime->hj_OuterHashKeys, m_runtime->hj_OuterSortKeys, + BTLessStrategyNumber); + + setSortIndex(m_probeSortOp.sortColIdx, m_probeSortOp.collations, m_probeSortOp.nullsFirst, + m_probeSortOp.sortOperators, m_runtime->hj_InnerHashKeys, m_runtime->hj_InnerSortKeys, + BTLessStrategyNumber); + + initMemoryControl(); + + m_memControl.hashContext = AllocSetContextCreate(CurrentMemoryContext, "AsofHashJoinContext", + ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE, STANDARD_CONTEXT, m_memControl.totalMem); + + /* init hash functions */ + m_buildOp.hashFunc = (hashValFun *)palloc0(sizeof(hashValFun) * m_buildOp.keyNum); + m_buildOp.hashAtomFunc = (hashValFun *)palloc0(sizeof(hashValFun) * m_buildOp.keyNum); + m_probeOp.hashFunc = (hashValFun *)palloc0(sizeof(hashValFun) * m_buildOp.keyNum); + m_probeOp.hashAtomFunc = NULL; + + bindingFp(); + + initHashFmgr(); + + initPartitions(); + + replaceEqFunc(); + + if (HAS_INSTR(&m_runtime->js, true)) { + errno_t ret = memset_s(&(INSTR->sorthashinfo), sizeof(INSTR->sorthashinfo), 0, sizeof(struct SortHashInfo)); + securec_check(ret, "\0", "\0"); + } +} + +/* + * @Description: Binding some execution functions. + */ +void AsofHashJoin::bindingFp() +{ + initHashFunc(m_buildOp.tupleDesc, (void *)m_buildOp.hashFunc, m_buildOp.keyIndx, false); + initHashFunc(m_buildOp.tupleDesc, (void *)m_buildOp.hashAtomFunc, m_buildOp.keyIndx, true); + initHashFunc(m_probeOp.tupleDesc, (void *)m_probeOp.hashFunc, m_probeOp.keyIndx, false); +} + +/* + * @Description: Compute hash key index and check whether the hashkey is simple type. + * @out keyIndx - Record build side hash key attr number. + * @out oKeyIndx - Record build side hash key origin attr number. + * @in hashkeys - keyIndx, oKeyIndx should be allocated by caller. + */ +void AsofHashJoin::setHashIndex(uint16 *keyIndx, uint16 *oKeyIndx, List *hashKeys) +{ + int i = 0; + ListCell *lc = NULL; + ExprState *expr_state = NULL; + Var *variable = NULL; + + foreach (lc, hashKeys) { + expr_state = (ExprState *)lfirst(lc); + if (IsA(expr_state->expr, Var)) { + variable = (Var *)expr_state->expr; + } else if (IsA(expr_state->expr, RelabelType)) { + RelabelType *rel_type = (RelabelType *)expr_state->expr; + + if (IsA(rel_type->arg, Var) && ((Var *)rel_type->arg)->varattno > 0) { + variable = (Var *)((RelabelType *)expr_state->expr)->arg; + } else { + m_complicatekey = true; + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("not support complicate key"))); + break; + } + } else { + m_complicatekey = true; + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("not support complicate key"))); + break; + } + + keyIndx[i] = variable->varattno - 1; + oKeyIndx[i] = variable->varoattno - 1; + m_integertype[i] = (unsigned int)(m_integertype[i]) & (unsigned int)integerType(variable->vartype); + i++; + } +} + +/* + * @Description: Compute sort key index . + * @out keyIndx - Record build side sort key attr number. + * @out keyCollations - Record build side sort key collation . + * @out nullsFirstFlags - Record build side sort null first. + * @out opIndx - Record build side sort key variable. + * @in sortkeys - keyIndx, varIndx should be allocated by caller. + * @in sortStrategy sort direction (ASC or DESC) + */ +void AsofHashJoin::setSortIndex(AttrNumber *keyIndx, Oid *keyCollations, bool *nullsFirstFlags, Oid *opIndx, + List *hashKeys, List *sortKeys, int sortStrategy) +{ + int i = 0; + ListCell *lc = NULL; + ExprState *expr_state = NULL; + Var *variable = NULL; + Var *ovariable = NULL; + Oid sortop; + + foreach (lc, hashKeys) { + expr_state = (ExprState *)lfirst(lc); + if (IsA(expr_state->expr, Var)) { + variable = (Var *)expr_state->expr; + } else if (IsA(expr_state->expr, RelabelType)) { + RelabelType *rel_type = (RelabelType *)expr_state->expr; + + if (IsA(rel_type->arg, Var) && ((Var *)rel_type->arg)->varattno > 0) { + variable = (Var *)((RelabelType *)expr_state->expr)->arg; + } + } else { + Assert(false); + } + + if (sortStrategy == BTLessStrategyNumber) { + get_sort_group_operators(variable->vartype, true, false, false, &sortop, NULL, NULL, NULL); + } else { + get_sort_group_operators(variable->vartype, false, false, true, NULL, NULL, &sortop, NULL); + } + + keyIndx[i] = variable->varattno; + keyCollations[i] = variable->varcollid; + nullsFirstFlags[i] = false; + opIndx[i] = sortop; + + i++; + } + + lc = NULL; + foreach (lc, sortKeys) { + expr_state = (ExprState *)lfirst(lc); + if (IsA(expr_state->expr, Var)) { + variable = (Var *)expr_state->expr; + } else if (IsA(expr_state->expr, RelabelType)) { + RelabelType *rel_type = (RelabelType *)expr_state->expr; + + if (IsA(rel_type->arg, Var) && ((Var *)rel_type->arg)->varattno > 0) { + variable = (Var *)((RelabelType *)expr_state->expr)->arg; + } + } else { + Assert(false); + } + if (sortStrategy == BTLessStrategyNumber) { + get_sort_group_operators(variable->vartype, true, false, false, &sortop, NULL, NULL, NULL); + } else { + get_sort_group_operators(variable->vartype, false, false, true, NULL, NULL, &sortop, NULL); + } + + keyIndx[i] = variable->varattno; + keyCollations[i] = variable->varcollid; + nullsFirstFlags[i] = false; + opIndx[i] = sortop; + + i++; + } +} + +/* + * @Description: Initial memory control information. + */ +void AsofHashJoin::initMemoryControl() +{ + VecHashJoin *node = (VecHashJoin *)m_runtime->js.ps.plan; + + m_memControl.totalMem = SET_NODEMEM(((Plan *)node)->operatorMemKB[0], ((Plan *)node)->dop) * 1024L; + if (((Plan *)node)->operatorMaxMem > 0) { + m_memControl.maxMem = SET_NODEMEM(((Plan *)node)->operatorMaxMem, ((Plan *)node)->dop) * 1024L; + } + + elog(DEBUG2, "AsofHashJoinTbl[%d]: operator memory uses %dKB", ((Plan *)node)->plan_node_id, + (int)(m_memControl.totalMem / 1024L)); +} + +/* + * @Description: Initial hash functions info from m_runtime. + * Should be under hashjoin hashContext. + */ +void AsofHashJoin::initHashFmgr() +{ + ListCell *lc = NULL; + int i = 0; + m_buildOp.hashFmgr = (FmgrInfo *)palloc(sizeof(FmgrInfo) * m_buildOp.keyNum); + m_probeOp.hashFmgr = (FmgrInfo *)palloc(sizeof(FmgrInfo) * m_probeOp.keyNum); + foreach (lc, m_runtime->hj_HashOperators) { + Oid hashop = lfirst_oid(lc); + Oid left_hashfn; + Oid right_hashfn; + + if (!get_op_hash_functions(hashop, &left_hashfn, &right_hashfn)) { + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmodule(MOD_VEC_EXECUTOR), + errmsg("could not find hash function for hash operator %u", hashop))); + } + fmgr_info(left_hashfn, &m_probeOp.hashFmgr[i]); + fmgr_info(right_hashfn, &m_buildOp.hashFmgr[i]); + i++; + } + + if (u_sess->attr.attr_sql.enable_fast_numeric) { + replace_numeric_hash_to_bi(i, m_probeOp.hashFmgr); + replace_numeric_hash_to_bi(i, m_buildOp.hashFmgr); + } +} + +/* + * @Description: Build side main function. + */ +void AsofHashJoin::BuildInner() +{ + PlanState *node = innerPlanState(m_runtime); + VectorBatch *batch = NULL; + instr_time start_time; + + for (;;) { + batch = VectorEngine(node); + if (unlikely(BatchIsNull(batch))) { + + break; + } + (void)INSTR_TIME_SET_CURRENT(start_time); + partSort(batch, false); + m_rows += batch->m_rows; + m_build_time += elapsed_time(&start_time); + } +} + +/* + * @Description: Build side main function. + */ +void AsofHashJoin::BuildOuter() +{ + PlanState *node = outerPlanState(m_runtime); + VectorBatch *batch = NULL; + instr_time start_time; + + for (;;) { + batch = VectorEngine(node); + if (unlikely(BatchIsNull(batch))) { + break; + } + + (void)INSTR_TIME_SET_CURRENT(start_time); + + partSort(batch, true); + + m_rows += batch->m_rows; + m_build_time += elapsed_time(&start_time); + } + + if (HAS_INSTR(&m_runtime->js, true)) { + calcHashContextSize(m_memControl.hashContext, &m_memControl.allocatedMem, &m_memControl.availMem); + INSTR->sysBusy = m_memControl.sysBusy; + INSTR->spreadNum = m_memControl.spreadNum; + INSTR->sorthashinfo.hashbuild_time = m_build_time; + INSTR->sorthashinfo.spaceUsed = m_memControl.allocatedMem - m_memControl.availMem; + } +} + +/* + * @Description: save the data to memory. + * @in batch - Put the data in batch to momory. + */ +void AsofHashJoin::partSort(VectorBatch *batch, bool isBuildOp) +{ + int rows = batch->m_rows; + uint32 *part_idx = NULL; + uint32 *key_idx = NULL; + SonicHashInputOpAttr hashOp = isBuildOp ? m_buildOp : m_probeOp; + SortInputOpAttr sortOp = isBuildOp ? m_buildSortOp : m_probeSortOp; + List *hashKeys = isBuildOp ? m_runtime->hj_OuterHashKeys : m_runtime->hj_InnerHashKeys; + + /* First, calclute the hashVal of each row */ + + hashBatchArray(batch, (void *)hashOp.hashFunc, hashOp.hashFmgr, hashOp.keyIndx, m_hashVal); + + /* Get partition number for hash value. */ + calcPartIdx(m_hashVal, m_partNum, rows); + + part_idx = m_partIdx; + SonicSortPartition *part = NULL; + VecHashJoin *node = (VecHashJoin *)m_runtime->js.ps.plan; + int row_idx = 0; + for (row_idx = 0; row_idx < rows; row_idx++, part_idx++) { + part = isBuildOp ? (SonicSortPartition *)m_innerPartitions[*part_idx] + : (SonicSortPartition *)m_outerPartitions[*part_idx]; + Assert(part != NULL); + if (!part->m_sortState) { + MemoryContext old_cxt = MemoryContextSwitchTo(part->m_context); + part->m_sortState = batchsort_begin_heap( + sortOp.tupleDesc, sortOp.numsortkeys, sortOp.sortColIdx, sortOp.sortOperators, sortOp.collations, + sortOp.nullsFirst, m_memControl.totalMem / (m_partNum * 1024L), false, + m_memControl.maxMem / (m_partNum * 1024L), ((Plan *)node)->dop, SET_DOP(((Plan *)node)->dop)); + + part->m_sortState->jitted_CompareMultiColumn = NULL; + part->m_sortState->jitted_CompareMultiColumn_TOPN = NULL; + + (void)MemoryContextSwitchTo(old_cxt); + } + bool is_null = false; + int i = 0; + for (i = 0; i < hashOp.keyNum; i++) { + if (IS_NULL(batch->m_arr[hashOp.keyIndx[i]].m_flag[row_idx])) { + is_null = true; + } + } + if (!is_null) { + part->m_sortState->sort_putbatch(part->m_sortState, batch, row_idx, row_idx + 1); + part->m_rows++; + if (!isBuildOp) { + m_probeIdx++; + } + /* sql active feature */ + if (part->m_sortState->m_tapeset) { + long currentFileBlocks = LogicalTapeSetBlocks(part->m_sortState->m_tapeset); + int64 spill_size = (int64)(currentFileBlocks - part->m_sortState->m_lastFileBlocks); + m_spillSize += spill_size * (BLCKSZ); + m_spillCount += 1; + pgstat_increase_session_spill_size(spill_size * (BLCKSZ)); + part->m_sortState->m_lastFileBlocks = currentFileBlocks; + if (HAS_INSTR(&m_runtime->js, true)) { + INSTR->sorthashinfo.spill_size += m_spillSize; + if (isBuildOp) { + INSTR->sorthashinfo.spill_innerSize += m_spillSize; + if (!part->m_isSpill) { + INSTR->sorthashinfo.spill_innerPartNum += 1; + part->m_isSpill = true; + } + } else { + INSTR->sorthashinfo.spill_outerSize += m_spillSize; + if (!part->m_isSpill) { + INSTR->sorthashinfo.spill_outerPartNum += 1; + part->m_isSpill = true; + } + } + } + } + } + } +} + +/* + * @Description: Fine one file partition pair can join. + * @return - false when cannot get any pair because all partitions are finished searching. + */ +VectorBatch *AsofHashJoin::Probe() +{ + EState *estate = NULL; + uint32 idx = m_matchPartIdx; /* offset of the partition to load */ + uint32 matchIdx = 0; + + /* + * Search for a valid partition that can be fit in the memory. + * If an inner partition is too large for the current memory, + * repartition the inner partition and the coresponding outer partition, + * then search for the next, until we found a vaild one. + */ + for (; idx < m_partNum; ++idx) { + SonicSortPartition *buildPart = (SonicSortPartition *)m_outerPartitions[idx]; + SonicSortPartition *probePart = (SonicSortPartition *)m_innerPartitions[idx]; + Assert(buildPart != NULL); + Assert(probePart != NULL); + if (probePart->m_rows == 0 || buildPart->m_rows == 0) { + finishJoinPartition(idx); + continue; + } + + if (probePart->m_data == NULL && probePart->m_sortState != NULL) { + MemoryContext old_cxt = MemoryContextSwitchTo(probePart->m_context); + probePart->m_data = New(CurrentMemoryContext) VectorBatch(CurrentMemoryContext, m_buildOp.tupleDesc); + (void)MemoryContextSwitchTo(old_cxt); + + batchsort_performsort(probePart->m_sortState); + if (probePart->m_sortState->m_tapeset) { + long currentFileBlocks = LogicalTapeSetBlocks(probePart->m_sortState->m_tapeset); + int64 spill_size = (int64)(currentFileBlocks - probePart->m_sortState->m_lastFileBlocks); + m_spillSize += spill_size * (BLCKSZ); + m_spillCount += 1; + pgstat_increase_session_spill_size(spill_size * (BLCKSZ)); + probePart->m_sortState->m_lastFileBlocks = currentFileBlocks; + if (HAS_INSTR(&m_runtime->js, true)) { + INSTR->sorthashinfo.spill_size += m_spillSize; + INSTR->sorthashinfo.spill_outerSize += m_spillSize; + } + } + + batchsort_getbatch(probePart->m_sortState, true, probePart->m_data); + probePart->m_fetchCount += probePart->m_data->m_rows; + } + + if (buildPart->m_data == NULL && buildPart->m_sortState != NULL) { + MemoryContext old_cxt = MemoryContextSwitchTo(buildPart->m_context); + buildPart->m_data = New(CurrentMemoryContext) VectorBatch(CurrentMemoryContext, m_probeOp.tupleDesc); + (void)MemoryContextSwitchTo(old_cxt); + + batchsort_performsort(buildPart->m_sortState); + if (buildPart->m_sortState->m_tapeset) { + long currentFileBlocks = LogicalTapeSetBlocks(buildPart->m_sortState->m_tapeset); + int64 spill_size = (int64)(currentFileBlocks - buildPart->m_sortState->m_lastFileBlocks); + m_spillSize += spill_size * (BLCKSZ); + m_spillCount += 1; + pgstat_increase_session_spill_size(spill_size * (BLCKSZ)); + buildPart->m_sortState->m_lastFileBlocks = currentFileBlocks; + if (HAS_INSTR(&m_runtime->js, true)) { + INSTR->sorthashinfo.spill_size += m_spillSize; + INSTR->sorthashinfo.spill_innerSize += m_spillSize; + } + } + + batchsort_getbatch(buildPart->m_sortState, true, buildPart->m_data); + buildPart->m_fetchCount += buildPart->m_data->m_rows; + } + + for (; probePart->m_cmpIdx < probePart->m_rows; ++probePart->m_cmpIdx) { + if (buildPart->m_cmpIdx < buildPart->m_rows) { + // If right > left, then there is no match + if (!CompareSortColumn(buildPart, probePart, false)) { + continue; + } + } + + // Exponential search forward for a non-matching value + + int64 bound = 1; + int64 begin = buildPart->m_cmpIdx; + buildPart->m_cmpIdx = begin + bound; + while (buildPart->m_cmpIdx < buildPart->m_rows) { + if (CompareSortColumn(buildPart, probePart, false)) { + // If right <= left, jump ahead + bound *= CONSTNUM2; + buildPart->m_cmpIdx = begin + bound; + } else { + break; + } + } + + // Binary search for the first non-matching value + int64 first = begin + bound / 2; + int64 last = Min(begin + bound, buildPart->m_rows); + while (first < last) { + const int64 mid = first + (last - first) / 2; + buildPart->m_cmpIdx = mid; + if (CompareSortColumn(buildPart, probePart, false)) { + first = mid + 1; + } else { + last = mid; + } + } + buildPart->m_cmpIdx = --first; + // check is the part key equal ? + if (!CompareSortColumn(buildPart, probePart, true)) { + continue; + } + // copy row to output + CopyRow(buildPart, probePart); + matchIdx++; + if (matchIdx == BatchMaxSize) { + m_buildOp.batch->FixRowCount(BatchMaxSize); + m_probeOp.batch->FixRowCount(BatchMaxSize); + ++probePart->m_cmpIdx; + m_matchPartIdx = idx; + return buildRes(m_probeOp.batch, m_buildOp.batch); + } + } + + finishJoinPartition(idx); + } + m_buildOp.batch->FixRowCount(matchIdx); + m_probeOp.batch->FixRowCount(matchIdx); + m_partNum = 0; + if (m_buildOp.batch->m_rows != 0) { + return buildRes(m_probeOp.batch, m_buildOp.batch); + } else { + return NULL; + } +} + +/* + * Inline-able copy of FunctionCall2Coll() to save some cycles in sorting. + */ +static inline Datum myFunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2) +{ + FunctionCallInfoData fcinfo; + Datum result; + + InitFunctionCallInfoData(fcinfo, flinfo, CONSTNUM2, collation, NULL, NULL); + + fcinfo.arg[0] = arg1; + fcinfo.arg[1] = arg2; + fcinfo.argnull[0] = false; + fcinfo.argnull[1] = false; + + result = FunctionCallInvoke(&fcinfo); + + /* Check for null result, since caller is clearly not expecting one */ + if (fcinfo.isnull) + ereport(ERROR, (errmodule(MOD_EXECUTOR), errcode(ERRCODE_UNEXPECTED_NULL_VALUE), + errmsg("function %u returned NULL", fcinfo.flinfo->fn_oid))); + + return result; +} + +/* + * Apply a sort function (by now converted to fmgr lookup form) + * and return a 3-way comparison result. This takes care of handling + * reverse-sort and NULLs-ordering properly. We assume that DESC and + * NULLS_FIRST options are encoded in sk_flags the same way btree does it. + */ +static inline int32 inlineApplySortFunction(FmgrInfo *sortFunction, uint32 sk_flags, Oid collation, Datum datum1, + bool isNull1, Datum datum2, bool isNull2) +{ + int32 compare = 0; + + if (!isNull1 && !isNull2) { + if (sortFunction->fn_addr == NULL) { + if (datum1 < datum2) + compare = -1; + else if (datum1 == datum2) + compare = 0; + else if (datum1 > datum2) + compare = 1; + } else + compare = DatumGetInt32(myFunctionCall2Coll(sortFunction, collation, datum1, datum2)); + + if (sk_flags & SK_BT_DESC) + compare = -compare; + } else if (isNull1) { + if (isNull2) + compare = 0; /* NULL "=" NULL */ + else if (sk_flags & SK_BT_NULLS_FIRST) + compare = -1; /* NULL "<" NOT_NULL */ + else + compare = 1; /* NULL ">" NOT_NULL */ + } else if (isNull2) { + if (sk_flags & SK_BT_NULLS_FIRST) + compare = 1; /* NOT_NULL ">" NULL */ + else + compare = -1; /* NOT_NULL "<" NULL */ + } + return compare; +} + +/* + * @Description: compare two part by sort key + * @in build - build part. + * @in probe - probe part. + * @in check_part - check part key. + */ +bool AsofHashJoin::CompareSortColumn(SonicSortPartition *build, SonicSortPartition *probe, bool check_part) +{ + Batchsortstate *buildState = build->m_sortState; + Batchsortstate *probeState = probe->m_sortState; + + Assert(buildState->m_nKeys == probeState->m_nKeys); + + VectorBatch *buildData = build->m_data; + VectorBatch *probeData = probe->m_data; + + ScanKey buildScanKey = buildState->m_scanKeys; + ScanKey probeScanKey = probeState->m_scanKeys; + + int64 buildIdx = 0; + int64 probeIdx = 0; + + if (build->m_cmpIdx >= build->m_fetchCount) { + m_lastBatch->Reset(); + m_lastBatch->Copy(buildData); + batchsort_getbatch(buildState, true, buildData); + build->m_fetchCount += buildData->m_rows; + } + + buildIdx = buildData->m_rows - (build->m_fetchCount - build->m_cmpIdx); + if (buildIdx < 0) { + buildData = m_lastBatch; + buildIdx = m_lastBatch->m_rows + buildIdx; + } + + if (probe->m_cmpIdx >= probe->m_fetchCount) { + batchsort_getbatch(probeState, true, probeData); + probe->m_fetchCount += probeData->m_rows; + } + probeIdx = probeData->m_rows - (probe->m_fetchCount - probe->m_cmpIdx); + + Assert(probeIdx >= 0); + Assert(buildIdx >= 0); + int nkey; + int32 compare = 0; + ScalarValue datum1, datum2; + ScalarValue datum1Tmp, datum2Tmp; + bool isnull1 = false; + bool isnull2 = false; + + CHECK_FOR_INTERRUPTS(); + + int buildColIdx; + int probeColIdx; + + for (nkey = 0; nkey < buildState->m_nKeys; ++nkey, ++buildScanKey, ++probeScanKey) { + buildColIdx = buildScanKey->sk_attno - 1; + probeColIdx = probeScanKey->sk_attno - 1; + + Assert(buildColIdx >= 0); + Assert(probeColIdx >= 0); + + Form_pg_attribute buildAttr = &buildState->tupDesc->attrs[buildColIdx]; + Form_pg_attribute probeAttr = &probeState->tupDesc->attrs[probeColIdx]; + + Assert(buildAttr->atttypid == probeAttr->atttypid); + + Oid typeOid = buildAttr->atttypid; + + datum1Tmp = datum1 = buildData->m_arr[buildColIdx].m_vals[buildIdx]; + datum2Tmp = datum2 = probeData->m_arr[probeColIdx].m_vals[probeIdx]; + + isnull1 = IS_NULL(buildData->m_arr[buildColIdx].m_flag[buildIdx]); + isnull2 = IS_NULL(probeData->m_arr[probeColIdx].m_flag[probeIdx]); + + switch (typeOid) { + // extract header + case TIMETZOID: + case INTERVALOID: + case TINTERVALOID: + case NAMEOID: + case MACADDROID: + datum1 = isnull1 == false ? PointerGetDatum((char *)datum1Tmp + VARHDRSZ_SHORT) : 0; + datum2 = isnull2 == false ? PointerGetDatum((char *)datum2Tmp + VARHDRSZ_SHORT) : 0; + break; + case TIDOID: + datum1 = isnull1 == false ? PointerGetDatum(&datum1Tmp) : 0; + datum2 = isnull2 == false ? PointerGetDatum(&datum2Tmp) : 0; + break; + default: + break; + } + + compare = inlineApplySortFunction(&buildScanKey->sk_func, buildScanKey->sk_flags, buildScanKey->sk_collation, + datum1, isnull1, datum2, isnull2); + if (check_part) { + return compare == 0; + } + if (compare != 0) + return compare <= m_compareValue; + } + + return compare <= m_compareValue; +} + +/* + * @Description: compare two part by sort key + * @in build - build part. + * @in probe - probe part. + */ +void AsofHashJoin::CopyRow(SonicSortPartition *build, SonicSortPartition *probe) +{ + VectorBatch *buildData = build->m_data; + VectorBatch *probeData = probe->m_data; + + int64 buildIdx = 0; + int64 probeIdx = 0; + + buildIdx = buildData->m_rows - (build->m_fetchCount - build->m_cmpIdx); + if (buildIdx < 0) { + buildData = m_lastBatch; + buildIdx = m_lastBatch->m_rows + buildIdx; + } + + probeIdx = probeData->m_rows - (probe->m_fetchCount - probe->m_cmpIdx); + + Assert(probeIdx >= 0); + Assert(buildIdx >= 0); + + m_probeOp.batch->Copy(buildData, buildIdx, buildIdx + 1); + m_buildOp.batch->Copy(probeData, probeIdx, probeIdx + 1); +} + +/* + * Description: Delete memory context. + */ +void AsofHashJoin::freeMemoryContext() +{ + if (m_memControl.hashContext != NULL) { + for (int idx = 0; idx < m_partNum; ++idx) { + finishJoinPartition(idx); + } + /* Delete child context for hashContext */ + MemoryContextDelete(m_memControl.hashContext); + m_memControl.hashContext = NULL; + m_innerPartitions = NULL; + m_outerPartitions = NULL; + } + if (m_memControl.tmpContext != NULL) { + /* Delete child context for tmpContext */ + MemoryContextDelete(m_memControl.tmpContext); + m_memControl.tmpContext = NULL; + } +} + +/* + * @Description: Release build and probe partition. + * @in partIdx - Partition index. + */ +void AsofHashJoin::finishJoinPartition(uint32 partIdx) +{ + if (m_innerPartitions && m_innerPartitions[partIdx] != NULL) { + m_innerPartitions[partIdx]->freeResources(); + m_innerPartitions[partIdx] = NULL; + } + + if (m_outerPartitions && m_outerPartitions[partIdx] != NULL) { + m_outerPartitions[partIdx]->freeResources(); + m_outerPartitions[partIdx] = NULL; + } +} + +/* + * @Description: Build output with inBatch and outBatch. + * @in inBatch - Build side batch. + * @in outBatch - Probe side batch. + */ +VectorBatch *AsofHashJoin::buildRes(VectorBatch *inBatch, VectorBatch *outBatch) +{ + ExprContext *econtext = NULL; + VectorBatch *res_batch = NULL; + List *filter_qual = NIL; + ; + ScalarVector *pvector = NULL; + bool has_qual = false; + + DBG_ASSERT(inBatch->m_rows == outBatch->m_rows); + + ResetExprContext(m_runtime->js.ps.ps_ExprContext); + + /* Testing whether ps_ProjInfo is NULL */ + Assert(m_runtime->js.ps.ps_ProjInfo); + econtext = m_runtime->js.ps.ps_ProjInfo->pi_exprContext; + initEcontextBatch(NULL, outBatch, inBatch, NULL); + + if (m_runtime->js.joinqual != NULL && list_length(m_runtime->js.joinqual) >= CONSTNUM2) { + filter_qual = list_delete_first(m_runtime->js.joinqual); + has_qual = true; + econtext->ecxt_scanbatch = inBatch; + pvector = ExecVecQual(filter_qual, econtext, false); + if (pvector == NULL) { + inBatch->Reset(); + outBatch->Reset(); + return NULL; + } + } + + if (m_runtime->js.ps.qual != NULL) { + has_qual = true; + econtext->ecxt_scanbatch = inBatch; + pvector = ExecVecQual(m_runtime->js.ps.qual, econtext, false); + + if (pvector == NULL) { + inBatch->Reset(); + outBatch->Reset(); + return NULL; + } + } + + if (has_qual) { + outBatch->PackT(econtext->ecxt_scanbatch->m_sel); + inBatch->PackT(econtext->ecxt_scanbatch->m_sel); + } + + initEcontextBatch(NULL, outBatch, inBatch, NULL); + res_batch = ExecVecProject(m_runtime->js.ps.ps_ProjInfo); + if (res_batch->m_rows != inBatch->m_rows) { + res_batch->FixRowCount(inBatch->m_rows); + } + + inBatch->Reset(); + outBatch->Reset(); + return res_batch; +} + +/* + * @Description: Calculate memory context size + * @in ctx: memory context to calculate + * @in allocateSize: pointer to put total allocated size including child context + * @in freeSize: pointer to put total free size including child context + */ +void AsofHashJoin::calcHashContextSize(MemoryContext ctx, uint64 *allocateSize, uint64 *freeSize) +{ + AllocSetContext *aset = (AllocSetContext *)ctx; + MemoryContext child; + if (NULL == ctx) { + return; + } + /* calculate MemoryContext Stats */ + *allocateSize += (aset->totalSpace); + *freeSize += (aset->freeSpace); + + /* recursive MemoryContext's child */ + for (child = ctx->firstchild; child != NULL; child = child->nextchild) { + calcHashContextSize(child, allocateSize, freeSize); + } +} + +/* + * @Description: Calculate partition number with planner parameters. + */ +uint32 AsofHashJoin::calcPartitionNum() +{ + VecAsofJoin *node = NULL; + Plan *inner_plan = NULL; + int64 nrows; + int width; + uint32 part_num; + + node = (VecAsofJoin *)m_runtime->js.ps.plan; + inner_plan = innerPlan(node); + nrows = (int64)inner_plan->plan_rows; + width = inner_plan->plan_width; + elog(DEBUG2, "VecAsofJoin: total size: %ldByte, operator size: %ldByte.", (nrows * width), m_memControl.totalMem); + + part_num = getPower2NextNum((nrows * width) / m_memControl.totalMem); + part_num = Max(PARTMAXNUM, part_num); + part_num = Min(part_num, SONIC_PART_MAX_NUM); + + return part_num; +} + +/* + * @Description: Calculate partition index and record them in m_partIdx. + * @in hashVal - hash value to be used. The space should be allocated by caller. + * @in partNum - partition number. + * @in nrows - hash value numbers. + */ +inline void AsofHashJoin::calcPartIdx(uint32 *hashVal, uint32 partNum, int nrows) +{ + Assert(partNum != 0); + for (int i = 0; i < nrows; i++) { + m_partIdx[i] = *hashVal % partNum; + hashVal++; + } +} + +/* + * @Description: Initial partitions. + */ +void AsofHashJoin::initPartitions() +{ + SonicSortPartition *part = NULL; + if (!m_partNum) { + m_partNum = calcPartitionNum(); + } + AutoContextSwitch memSwitch(m_memControl.hashContext); + SonicHashPartition **buildP = (SonicHashPartition **)palloc0(sizeof(SonicHashPartition *) * m_partNum); + SonicHashPartition **probeP = (SonicHashPartition **)palloc0(sizeof(SonicHashPartition *) * m_partNum); + for (uint32 partIdx = 0; partIdx < m_partNum; partIdx++) { + part = New(CurrentMemoryContext) SonicSortPartition( + (char*)"innerPartitionContext", m_complicatekey, m_buildOp.tupleDesc, m_memControl.totalMem); + part->m_data = NULL; + part->m_sortState = NULL; + buildP[partIdx] = part; + part = New(CurrentMemoryContext) SonicSortPartition( + (char*)"outerPartitionContext", m_complicatekey, m_probeOp.tupleDesc, m_memControl.totalMem); + part->m_data = NULL; + part->m_sortState = NULL; + probeP[partIdx] = part; + } + m_outerPartitions = probeP; + m_innerPartitions = buildP; +} + +/* + * @Description: calculate comparison Value + * @in cmpName - cmp operater name + */ +int AsofHashJoin::comparisonValue(char *cmpName) +{ + if (pg_strcasecmp(cmpName, BTGreaterEqual) || pg_strcasecmp(cmpName, BTLessEqual)) { + return 0; + } else if (pg_strcasecmp(cmpName, BTGreater) || pg_strcasecmp(cmpName, BTLess)) { + return -1; + } + Assert(false); + elog(FATAL, "sortStrategy %s NOT IMPLEMENTED", cmpName); +} + +/* + * @Description: Reset resources. + */ +void AsofHashJoin::ResetNecessary() +{ + /* Reset hashContext */ + MemoryContextResetAndDeleteChildren(m_memControl.hashContext); + initPartitions(); + m_runtime->joinState = ASOF_PART; + resetMemoryControl(); + m_rows = 0; + m_matchPartIdx = 0; + m_spillCount = 0; + m_spillSize = 0; + + /* + * If chgParam of subnode is not null then plan will be re-scanned + * by first VectorEngine. + */ + if (m_runtime->js.ps.righttree->chgParam == NULL) { + VecExecReScan(m_runtime->js.ps.righttree); + } +} + +/* + * @Description: reset memory control information. + */ +void AsofHashJoin::resetMemoryControl() +{ + /* do not reset the total memory, since it might be successfully spread in the last partition scan */ + m_memControl.sysBusy = false; + m_memControl.spillToDisk = false; + m_memControl.spillNum = 0; + m_memControl.spreadNum = 0; + m_memControl.availMem = 0; + m_memControl.allocatedMem = 0; + + if (HAS_INSTR(&m_runtime->js, true)) { + errno_t rc = memset_s(&(INSTR->sorthashinfo), sizeof(INSTR->sorthashinfo), 0, sizeof(struct SortHashInfo)); + securec_check(rc, "\0", "\0"); + } +} + +void ExecReScanVecAsofJoin(VecAsofJoinState *node) +{ + AsofHashJoin *hj_tbl = (AsofHashJoin *)node->hashTbl; + if (hj_tbl != NULL) + hj_tbl->ResetNecessary(); + + /* + * if chgParam of subnode is not null then plan will be re-scanned by + * first VectorEngine. + */ + if (node->js.ps.lefttree->chgParam == NULL) + VecExecReScan(node->js.ps.lefttree); +} + +/* + * @Description: Early free the memory for VecHashJoin. + * + * @param[IN] node: vector executor state for HashJoin + * @return: void + */ +void ExecEarlyFreeVecAsofJoin(VecAsofJoinState *node) +{ + PlanState *plan_state = &node->js.ps; + if (plan_state->earlyFreed) + return; + void *tbl = node->hashTbl; + if (tbl != NULL) { + AsofHashJoin *hj_tbl = (AsofHashJoin *)tbl; + hj_tbl->freeMemoryContext(); + } + EARLY_FREE_LOG(elog(LOG, + "Early Free: After early freeing %sHashJoin " + "at node %d, memory used %d MB.", + JOIN_NAME, plan_state->plan->plan_node_id, getSessionMemoryUsageMB())); + plan_state->earlyFreed = true; + ExecEarlyFree(innerPlanState(node)); + ExecEarlyFree(outerPlanState(node)); +} diff --git a/src/gausskernel/runtime/vecexecutor/vecnode/vecrescan.cpp b/src/gausskernel/runtime/vecexecutor/vecnode/vecrescan.cpp index 1d20fdde42921db4625b5a40ee9c84b31d478530..298214214f34768f31f53cfa29ae04ce2c6b3622 100644 --- a/src/gausskernel/runtime/vecexecutor/vecnode/vecrescan.cpp +++ b/src/gausskernel/runtime/vecexecutor/vecnode/vecrescan.cpp @@ -47,6 +47,7 @@ #include "vecexecutor/vecsubqueryscan.h" #include "vecexecutor/vecappend.h" #include "vecexecutor/vechashjoin.h" +#include "vecexecutor/vecasofjoin.h" #include "vecexecutor/vechashagg.h" #include "vecexecutor/vecnodeforeignscan.h" #include "vecexecutor/vecnodecstoreindexheapscan.h" @@ -61,7 +62,6 @@ #include "vecexecutor/vectsstorescan.h" #endif /* ENABLE_MULTIPLE_NODES */ #include "vecexecutor/vecwindowagg.h" - /* * VecExecReScan * Reset a plan node so that its output can be re-scanned. @@ -200,6 +200,9 @@ void VecExecReScan(PlanState* node) case T_VecHashJoinState: ExecReScanVecHashJoin((VecHashJoinState*)node); break; + case T_VecAsofJoinState: + ExecReScanVecAsofJoin((VecAsofJoinState*)node); + break; case T_VecAggState: ExecReScanVecAggregation((VecAggState*)node); break; diff --git a/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonicpartition.cpp b/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonicpartition.cpp index 5ff8d4f2109ee8ad04d00290a579b4ec12dd4578..05c32cab62c9d288e7c1aaeaf7af7d862874a9db 100644 --- a/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonicpartition.cpp +++ b/src/gausskernel/runtime/vecexecutor/vectorsonic/vsonicpartition.cpp @@ -687,3 +687,84 @@ bool SonicHashFilePartition::isValid() } return true; } + +SonicSortPartition::SonicSortPartition(const char *cxtname, bool hasHash, TupleDesc tupleDesc, int64 workMem) + : SonicHashPartition(cxtname, tupleDesc->natts, workMem) +{ + MemoryContext old_ctx = MemoryContextSwitchTo(m_context); + m_data = NULL; + m_sortState = NULL; + m_isSpill = false; + m_hash = NULL; + m_bucket = NULL; + m_hashSize = 0; + m_bucketTypeSize = 0; + m_mask = 0; + m_cmpIdx = 0; + m_fetchCount = 0; + const uint8 HashDataDescNum = 4; + + if (hasHash) { + /* init m_hash to store the hash value */ + DatumDesc *desc = (DatumDesc *)palloc0(sizeof(DatumDesc)); + getDataDesc(desc, HashDataDescNum, NULL, false); + m_hash = New(m_context) SonicIntTemplateDatumArray(m_context, INIT_DATUM_ARRAY_SIZE, false, desc); + } + + (void)MemoryContextSwitchTo(old_ctx); + + m_status = partitionStatusMemory; +} + +/* + * @Description: init m_data according to desc per column + * @return - void. + */ +void SonicSortPartition::init(uint16 colIdx, DatumDesc *desc) +{ + Assert(false); +} + +/* + * @Description: put hash values into SonicIntTemplateDatumArray + * @in hashValue - pointer of hash values + * @in nrows - rows to put + */ +void SonicSortPartition::putHash(uint32 *hashValues, uint64 nrows) +{ + Assert(false); +} + +/* + * @Description: free context, file buffer and close file handler + */ +void SonicSortPartition::freeResources() +{ + if (m_sortState != NULL) { + batchsort_end(m_sortState); + } + if (m_context != NULL) { + /* reset the m_context */ + MemoryContextReset(m_context); + /* Delete child context for m_hashContext */ + MemoryContextDelete(m_context); + m_context = NULL; + } +} + +/* @Description: check if data is valid + * @return - true if every m_data[column] has the same number of rows. + */ +bool SonicSortPartition::isValid() +{ + return true; +} + +/* @Description: set select flag + * @in row number + */ +void SonicSortPartition::SetMatch(int idx) +{ + Assert(idx < m_data->m_rows); + m_data->m_sel[idx] = true; +} diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index d29b45febec1c98e4ab449a57842a86214ecf048..2d5467338cf9979ea5fc43534cb8a81be0f16940 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -165,6 +165,7 @@ extern const uint32 KEEP_FUNC_VERSION_NUMBER; extern const uint32 IGNORE_NULLS_VERSION_NUMBER; extern const uint32 CAST_FUNC_VERSION_NUMBER; extern const uint32 HASH_SAOP_VERSION_NUMBER; +extern const uint32 ASOFJOIN_VERSION_NUM; extern void register_backend_version(uint32 backend_version); extern bool contain_backend_version(uint32 version_number); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index e30f6eba6618520cec6e89febf2a919cc4d6d85f..9057d84dbb7f6f7df6caec330c36f415c52ed349 100755 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -83,6 +83,7 @@ typedef enum NodeTag { T_NestLoop, T_MergeJoin, T_HashJoin, + T_AsofJoin, T_Material, T_Sort, T_SortGroup, @@ -361,6 +362,7 @@ typedef enum NodeTag { T_NestPath, T_MergePath, T_HashPath, + T_AsofPath, T_TidPath, T_ForeignPath, T_ExtensiblePath, @@ -736,6 +738,7 @@ typedef enum NodeTag { T_VecNestLoop, T_VecMergeJoin, T_VecHashJoin, + T_VecAsofJoin, T_VecMaterial, T_VecSort, T_VecGroup, @@ -769,6 +772,7 @@ typedef enum NodeTag { T_RowToVecState, T_VecAggState, T_VecHashJoinState, + T_VecAsofJoinState, T_VecStreamState, T_VecSortState, T_VecForeignScanState, @@ -1148,7 +1152,8 @@ typedef enum JoinType { /* * We might need additional join types someday. */ - JOIN_STRAIGHT + JOIN_STRAIGHT, + JOIN_ASOF_INNER } JoinType; /* diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 995aca568a7f2f1161064430a87fc1a88aaedf44..4ca840f3a0c70af349006b17992c30995e7adf40 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -1174,6 +1174,18 @@ typedef struct HashJoin { #endif } HashJoin; +/* ---------------- + * asof join node + * ---------------- + */ + +typedef struct AsofJoin { + Join join; + List* hashclauses; + List* mergeclauses; /* mergeclauses as expression trees */ + bool streamBothSides; +} AsofJoin; + /* ---------------- * materialization node * ---------------- @@ -1554,6 +1566,7 @@ typedef struct VecPartIterator : public PartIterator { // vector aggregation. typedef struct HashJoin VecHashJoin; +typedef struct AsofJoin VecAsofJoin; typedef struct Agg VecAgg; typedef struct SetOp VecSetOp; typedef struct Unique VecUnique; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index ae397802feb7a3949462783a1459cdc760ac7f87..7262a38ffa26fcfc66be5fe6a9faf77ba1294d18 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1441,6 +1441,7 @@ typedef struct JoinExpr { NodeTag type; JoinType jointype; /* type of join */ bool isNatural; /* Natural join? Will need to shape table */ + bool isAsof; /* Asof join? */ Node* larg; /* left subtree */ Node* rarg; /* right subtree */ List* usingClause; /* USING clause, if any (list of String) */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index f7dcd23f5e7a80c30f4e1d96a7a99b1fc77bca17..9b521f4557b43a527f39df6080ab39b82dbe9a24 100755 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -781,7 +781,7 @@ typedef struct RelOptInfo { struct Path* cheapest_unique_path; List* cheapest_parameterized_paths; - + /* parameterization information needed for both base rels and join rels */ /* (see also lateral_vars and lateral_referencers) */ Relids direct_lateral_relids; /* rels directly laterally referenced */ @@ -1579,6 +1579,24 @@ typedef struct HashPath { double joinRows; } HashPath; +/* + * A asofjoin path has these fields. + * + * The remarks above for mergeclauses apply for hashclauses as well. + * + * Hashjoin does not care what order its inputs appear in, so we have + * no need for sortkeys. + */ +typedef struct AsofPath { + JoinPath jpath; + List *path_hashclauses; /* join clauses used for hashing */ + List *path_mergeclauses; /* join clauses to be used for merge */ + List *outersortkeys; /* keys for explicit sort, if any */ + List *innersortkeys; /* keys for explicit sort, if any */ + OpMemInfo outer_mem_info; /* Mem info for outer explicit sort */ + OpMemInfo inner_mem_info; /* Mem info for inner explicit sort */ +} AsofPath; + #ifdef PGXC /* * A remotequery path represents the queries to be sent to the datanode/s @@ -1842,6 +1860,9 @@ typedef struct RestrictInfo { /* transient workspace for use while considering a specific join path */ bool outer_is_left; /* T = outer var on left, F = on right */ + /* true if clause is asofjoinable, else false: */ + bool is_asof; + /* valid if clause is hashjoinable, else InvalidOid: */ Oid hashjoinoperator; /* copy of clause operator */ @@ -2248,6 +2269,10 @@ typedef struct JoinCostWorkspace { int numbuckets; int numbatches; + /* private for cost_asofjoin code */ + double inner_distinct_num; + double outer_distinct_num; + /* Meminfo for joins */ OpMemInfo outer_mem_info; OpMemInfo inner_mem_info; diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 8173a3eacd91e20da7fc2905226af7976d5312c6..d14474239ac51532063c0873f7697acd1ad82e50 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -135,7 +135,7 @@ extern Expr* evaluate_expr(Expr* expr, Oid result_type, int32 result_typmod, Oid extern bool contain_var_unsubstitutable_functions(Node* clause); extern void distribute_qual_to_rels(PlannerInfo* root, Node* clause, bool is_deduced, bool below_outer_join, JoinType jointype, Index security_level, Relids qualscope, Relids ojscope, Relids outerjoin_nonnullable, - Relids deduced_nullable_relids, List **postponed_qual_list); + Relids deduced_nullable_relids, List **postponed_qual_list, bool is_asof); extern void check_plan_correlation(PlannerInfo* root, Node* expr); extern bool findConstraintByVar(Var* var, Oid relid, constraintType conType); extern bool is_var_node(Node* node); diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index a0aca6b10393931e71432c663993978669e84b21..be15dce063e170666e5345d9653cb953fde0aa92 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -29,7 +29,6 @@ * we are definition that the cost of scanning one tuple is 1/10 times. */ #define COL_TUPLE_COST_MULTIPLIER 10 - /* * Estimate the overhead per hashtable entry at 64 bytes (same as in * planner.c). @@ -137,6 +136,11 @@ extern void initial_cost_hashjoin(PlannerInfo* root, JoinCostWorkspace* workspac Path* outer_path, Path* inner_path, JoinPathExtraData *extra, int dop); extern void final_cost_hashjoin(PlannerInfo* root, HashPath* path, JoinCostWorkspace* workspace, JoinPathExtraData *extra, bool hasalternative, int dop); +extern void initial_cost_asofjoin(PlannerInfo* root, JoinCostWorkspace* workspace, JoinType jointype, List* hashclauses, + Path* outer_path, Path* inner_path, List* mergeclauses, List* outersortkeys, List* innersortkeys, + JoinPathExtraData *extra, int dop); +extern void final_cost_asofjoin(PlannerInfo* root, AsofPath* path, JoinCostWorkspace* workspace, + JoinPathExtraData *extra, int dop); extern void cost_rescan(PlannerInfo* root, Path* path, Cost* rescan_startup_cost, /* output parameters */ Cost* rescan_total_cost, OpMemInfo* mem_info); extern Cost cost_rescan_material(double rows, int width, OpMemInfo* mem_info, bool vectorized, int dop); diff --git a/src/include/optimizer/learn.h b/src/include/optimizer/learn.h index 24f6c50ded8e7c7916d1432af17e6c18f214bdc6..cb99e4a0cea60cca18d1e585d8ed85089a5c268d 100644 --- a/src/include/optimizer/learn.h +++ b/src/include/optimizer/learn.h @@ -103,6 +103,7 @@ #define TEXT_STRATEGY_JOIN_NESTED_LOOP "NESTED_LOOP" #define TEXT_STRATEGY_JOIN_MERGE "MERGE" #define TEXT_STRATEGY_JOIN_HASH "HASH" +#define TEXT_STRATEGY_JOIN_ASOF "ASOF" #define TEXT_STRATEGY_MODIFY_TABLE_INSERT "INSERT" #define TEXT_STRATEGY_MODIFY_TABLE_UPDATE "UPDATE" #define TEXT_STRATEGY_MODIFY_TABLE_DELETE "DELETE" diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index b17342cd56d79647bf960d1bb418d459c1ade244..83449e6285cf72226a4abb153b4a039599027a9d 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -52,7 +52,6 @@ extern Path* find_hinted_path(Path* current_path); extern void add_path(PlannerInfo* root, RelOptInfo* parent_rel, Path* new_path); extern bool add_path_precheck( RelOptInfo* parent_rel, Cost startup_cost, Cost total_cost, List* pathkeys, Relids required_outer); - extern Path* create_seqscan_path(PlannerInfo* root, RelOptInfo* rel, Relids required_outer, int dop = 1); extern Path *create_resultscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer); extern Path* create_cstorescan_path(PlannerInfo* root, RelOptInfo* rel, int dop = 1); @@ -106,6 +105,11 @@ extern HashPath* create_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, Jo JoinCostWorkspace* workspace, JoinPathExtraData* extra, Path* outer_path, Path* inner_path, List* restrict_clauses, Relids required_outer, List* hashclauses, int dop = 1); +extern AsofPath* create_asofjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, + JoinCostWorkspace* workspace, JoinPathExtraData* extra, Path* outer_path, Path* inner_path, + List* restrict_clauses, Relids required_outer, List* hashclauses, List* mergeclauses, + List* outersortkeys, List* innersortkeys, int dop = 1); + extern ProjectionPath *create_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, PathTarget *target); extern Path *apply_projection_to_path(PlannerInfo *root, RelOptInfo *rel, Path *path, PathTarget *target); extern ProjectSetPath *create_set_projection_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, @@ -147,6 +151,7 @@ extern void add_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType JoinCostWorkspace* workspace, SpecialJoinInfo* sjinfo, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys, Relids required_outer, List* mergeclauses, List* outersortkeys, List* innersortkeys, Distribution* target_distribution); + extern bool equal_distributekey(PlannerInfo* root, List* distribute_key1, List* distribute_key2); extern bool judge_node_compatible(PlannerInfo* root, Node* n1, Node* n2); extern List* build_superset_keys_for_rel( @@ -226,5 +231,8 @@ extern bool IsSameJoinExecType(PlannerInfo *root, Path *outer_path, Path *inner_ extern bool is_diskey_and_joinkey_compatible(Node* diskey, Node* joinkey); +extern bool check_asof_join(RelOptInfo* rel1, RelOptInfo* rel2); + + #endif #endif /* PATHNODE_H */ diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h index 94140e7b3c826dc94fd110f0661a9cc9b5cf9135..b774c14b8e74f7bcd77185e8cef46c7877709621 100644 --- a/src/include/optimizer/restrictinfo.h +++ b/src/include/optimizer/restrictinfo.h @@ -17,10 +17,10 @@ #include "nodes/relation.h" /* Convenience macro for the common case of a valid-everywhere qual */ -#define make_simple_restrictinfo(clause) make_restrictinfo(clause, true, false, false, 0, NULL, NULL, NULL) +#define make_simple_restrictinfo(clause) make_restrictinfo(clause, true, false, false, 0, NULL, NULL, NULL, false) extern RestrictInfo* make_restrictinfo(Expr* clause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, - Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids); + Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids, bool is_asof); extern List* make_restrictinfo_from_bitmapqual(Path* bitmapqual, bool is_pushed_down, bool include_predicates); extern List* make_restrictinfos_from_actual_clauses(PlannerInfo* root, List* clause_list); extern bool restriction_is_or_clause(RestrictInfo* restrictinfo); diff --git a/src/include/optimizer/streampath.h b/src/include/optimizer/streampath.h index 6e21d24a39057124c13946d473126f693a743caf..449647a8fc728509f39d93229d1dc31d453f4386 100644 --- a/src/include/optimizer/streampath.h +++ b/src/include/optimizer/streampath.h @@ -385,4 +385,43 @@ private: List* m_outerSortKeys; }; +class AsofJoinPathGen : public JoinPathGen { +public: + AsofJoinPathGen(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, + JoinPathExtraData* extra, Path* outer_path, Path* inner_path, + List* restrictlist, Relids required_outer, List* hashclauses, List* mergeclauses, List* outersortkeys, List* innersortkeys); + + virtual ~AsofJoinPathGen(); + + /* Create Asof join path and add it to path list. */ + void addAsofJoinPath(JoinCostWorkspace* workspace, Distribution* targetDistribution, int dop); + +private: + /* Add asof join path to path list. */ + void addAsofjoinPathToList(); + + /* Initial the cost of asof join. */ + void initialCostAsofjoin(); + + /* Finalize the cost of Asof join. */ + void finalCostAsofjoin(AsofPath* path); + + /* Create asof join path. */ + Path* createAsofJoinPath(); + +private: + /* Hash clauses for asof join. */ + List* m_hashClauses; + + /* merge clauses for asof join. */ + List* m_mergeClauses; + + /* Sort key of inner path. */ + List* m_innerSortKeys; + + /* Sort key of outer path. */ + List* m_outerSortKeys; +}; + + #endif /* STREAMPATH_H */ diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index ef4bfd7cb6da73762111efbd48ba5195d1033529..d0c946958910ced98500b17dcbd4546432d96152 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -52,6 +52,7 @@ PG_KEYWORD("archive", ARCHIVE, UNRESERVED_KEYWORD) PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD) PG_KEYWORD("as", AS, RESERVED_KEYWORD) PG_KEYWORD("asc", ASC, RESERVED_KEYWORD) +PG_KEYWORD("asof", ASOF_P, UNRESERVED_KEYWORD) PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD) PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD) PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD) diff --git a/src/include/vecexecutor/vecasofjoin.h b/src/include/vecexecutor/vecasofjoin.h new file mode 100644 index 0000000000000000000000000000000000000000..92c75c605aeafc1e0e1ee63f3de6ed406db91939 --- /dev/null +++ b/src/include/vecexecutor/vecasofjoin.h @@ -0,0 +1,181 @@ + +/* + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * --------------------------------------------------------------------------------------- + * + * vecasofjoin.h + * Prototypes for vectorized asof join + * + * IDENTIFICATION + * src/include/vecexecutor/vecasofjoin.h + * + * --------------------------------------------------------------------------------------- + */ + +#ifndef VECASOFJOIN_H_ +#define VECASOFJOIN_H_ + +#include "vecexecutor/vecnodes.h" +#include "workload/workload.h" +#include "vectorsonic/vsonichash.h" +#include "vectorsonic/vsonichashjoin.h" +#include "vectorsonic/vsonicpartition.h" + +// asof join runtime state +#define ASOF_PART 0 +#define ASOF_MERGE 1 +#define ASOF_END 2 + +extern VecAsofJoinState *ExecInitVecAsofJoin(VecAsofJoin *node, EState *estate, int eflags); +extern VectorBatch *ExecVecAsofJoin(VecAsofJoinState *node); +extern void ExecEndVecAsofJoin(VecAsofJoinState *node); +extern void ExecReScanVecAsofJoin(VecAsofJoinState *node); +extern void ExecEarlyFreeVecAsofJoin(VecAsofJoinState *node); + +class AsofHashJoin : public SonicHash { +public: + AsofHashJoin(int size, VecAsofJoinState *node); + + ~AsofHashJoin() {}; + + void BuildInner(); + void BuildOuter(); + + VectorBatch *Probe(); + + void ResetNecessary(); + + void freeMemoryContext(); + +private: + /* init functions */ + void setHashIndex(uint16 *keyIndx, uint16 *oKeyIndx, List *hashKeys); + + /* init functions */ + void setSortIndex(AttrNumber *keyIndx, Oid *keyCollations, bool *nullsFirstFlags, Oid *opIndx, List *hashKeys, + List *sortKeys, int sortStrategy); + + void initMemoryControl(); + void initPartitions(); + + void initHashFmgr(); + + int comparisonValue(char *cmpName); + bool CompareSortColumn(SonicSortPartition *build, SonicSortPartition *probe, bool check_part); + void CopyRow(SonicSortPartition *build, SonicSortPartition *probe); + + void bindingFp(); + + /* build side functions to put data */ + void partSort(VectorBatch *batch, bool isBuildOp); + + /* output functions */ + VectorBatch *buildRes(VectorBatch *inBatch, VectorBatch *outBatch); + + void calcHashContextSize(MemoryContext ctx, uint64 *allocateSize, uint64 *freeSize); + + /* Match Functions */ + void initMatchFunc(TupleDesc desc, uint16 keyNum); + + void DispatchKeyInnerFunction(int KeyIndx); + + template + void DispatchKeyOuterFunction(int KeyIndx); + + /* partitions functions */ + uint32 calcPartitionNum(); + + void calcPartIdx(uint32 *hashVal, uint32 partNum, int nrows); + + /* clean functions */ + void finishJoinPartition(uint32 partIdx); + + void resetMemoryControl(); + +public: + double m_build_time; + double m_probe_time; + +private: + typedef struct SortInputOpAttr { + int numsortkeys; + AttrNumber *sortColIdx; + Oid *sortOperators; + Oid *collations; + bool *nullsFirst; + TupleDesc tupleDesc; + + SortInputOpAttr() + { + numsortkeys = 0; + sortColIdx = NULL; + sortOperators = NULL; + collations = NULL; + nullsFirst = NULL; + tupleDesc = NULL; + } + } SortInputOpAttr; + + bool m_complicatekey; + /* last build batch */ + VectorBatch *m_lastBatch; + + /* describe outer operator */ + SonicHashInputOpAttr m_probeOp; + + /* describe inner sort operator */ + SortInputOpAttr m_buildSortOp; + /* describe outer sort operator */ + SortInputOpAttr m_probeSortOp; + /* describe compare flag */ + int m_compareValue; + /* describe spill disk count */ + uint64 m_spillCount; + /* describe spill disk size */ + long m_spillSize; + /* describe whether hash key is integer. */ + bool *m_integertype; + /* runtime state */ + VecAsofJoinState *m_runtime; + /* record partition indexes of data in atom or batch */ + uint32 m_partIdx[2 * BatchMaxSize]; + /* partitions for inner source */ + SonicHashPartition **m_innerPartitions; + /* partitions for outer source */ + SonicHashPartition **m_outerPartitions; + + /* + * number of partitions. + * inner and outer should have the same number of partitions. + */ + uint32 m_partNum; + + /* + * save the match part index + */ + uint32 m_matchPartIdx; + + /* + * the probe row index + */ + uint64 m_probeIdx; + + /* + * the cjVector is only allocated and + * used when m_complicateJoinKey is true + */ + ScalarVector *m_cjVector; +}; + +#endif /* SRC_INCLUDE_VECTORSONIC_VSONICHASHJOIN_H_ */ \ No newline at end of file diff --git a/src/include/vecexecutor/vecnodes.h b/src/include/vecexecutor/vecnodes.h index 04e68283d8459e6966d46d20c104e44b08dc092e..1025b2e890b0c3885e75d1da955017e45f469688 100644 --- a/src/include/vecexecutor/vecnodes.h +++ b/src/include/vecexecutor/vecnodes.h @@ -76,6 +76,38 @@ typedef struct VecHashJoinState : public HashJoinState { char* jitted_buildHashTable_NeedCopy; } VecHashJoinState; + +typedef struct VecAsofJoinState { + JoinState js; /* its first field is NodeTag */ + List* hashclauses; /* list of ExprState nodes */ + List* mergeclauses; /* list of ExprState nodes */ + List* hj_OuterHashKeys; /* list of ExprState nodes */ + List* hj_InnerHashKeys; /* list of ExprState nodes */ + List* hj_OuterSortKeys; /* list of ExprState nodes */ + List* hj_InnerSortKeys; /* list of ExprState nodes */ + List* hj_HashOperators; /* list of operator OIDs */ + int joinState; + + char* cmpName; + + void* hashTbl; + + FmgrInfo* eqfunctions; + + /* + * function pointer to LLVM machine code if hash join qual + * can be LLVM optimiezed. + */ + vecqual_func jitted_joinqual; /* LLVM IR function pointer to point to + * codegened hash->joinqual expr */ + vecqual_func jitted_hashclause; /* LLVM IR function pointer to point to + * codegened hash clause expr */ + + char* jitted_innerjoin; /* jitted inner hash join */ + char* jitted_matchkey; /* jitted matchKey for hash join*/ + +} VecAsofJoinState; + typedef enum VecAggType { AGG_COUNT = 0, AGG_SUM, diff --git a/src/include/vectorsonic/vsonicpartition.h b/src/include/vectorsonic/vsonicpartition.h index 13b63f8b9bd86bedaa8a79273628498d123899b1..80bb68843c40fefb53e2b46e16d4185c0e8d9c11 100644 --- a/src/include/vectorsonic/vsonicpartition.h +++ b/src/include/vectorsonic/vsonicpartition.h @@ -230,4 +230,50 @@ public: inline bool isValid(); }; +class SonicSortPartition : public SonicHashPartition { + +public: + /* where to put data */ + VectorBatch* m_data; + /* For complicate join key */ + SonicDatumArray* m_hash; + /* for sort state */ + Batchsortstate* m_sortState; + + uint8 m_bucketTypeSize; + + char* m_bucket; + + uint32 m_hashSize; + + uint32 m_mask; + /* for the index row of compare */ + int64 m_cmpIdx; + /* for the count of get batch from sort state */ + uint32 m_fetchCount; + + bool m_isSpill; + + +public: + SonicSortPartition(const char* cxtname, bool hasHash, TupleDesc tupleDesc, int64 workMem); + ~SonicSortPartition(){}; + + void init(uint16 colIdx, DatumDesc* desc); + + void freeResources(); + + void SetMatch(int idx); + + void putHash(uint32* hashValues, uint64 nrows); + + inline bool isValid(); + + template + void putVal(ScalarValue* val, uint8* flag, uint16 colIdx) + { + Assert(false); + } +}; + #endif /* SRC_INCLUDE_VECTORSONIC_VSONICPARTITION_H_ */ diff --git a/src/test/regress/expected/vec_asofjoin.out b/src/test/regress/expected/vec_asofjoin.out new file mode 100644 index 0000000000000000000000000000000000000000..54c92a90983beb2124b5cde644ea25f7fc6dd4b3 --- /dev/null +++ b/src/test/regress/expected/vec_asofjoin.out @@ -0,0 +1,157 @@ +/* + * This file is used to test the function of ExecVecAsofJoin() + */ +---- +--- Create Table and Insert Data +---- +CREATE TABLE prices( + ticker char(10), + pa integer, + wh timestamp, + price DECIMAL(10,2))with(orientation=column); +CREATE TABLE holdings( + ticker char(10), + pa integer, + wh timestamp, + shares DECIMAL(10,2))with(orientation=column); + +insert into prices values('APPL', 1, '2001-01-01 00:00:00', 1); +insert into prices values('APPL', 1, '2001-01-01 00:01:00', 2); +insert into prices values('APPL', 1, '2001-01-01 00:02:00', 3); +insert into prices values('MSFT', 2, '2001-01-01 00:00:00', 1); +insert into prices values('MSFT', 2, '2001-01-01 00:01:00', 2); +insert into prices values('MSFT', 2, '2001-01-01 00:02:00', 3); +insert into prices values('GOOG', 1, '2001-01-01 00:00:00', 1); +insert into prices values('GOOG', 1,'2001-01-01 00:01:00', 2); +insert into prices values('GOOG', 1,'2001-01-01 00:02:00', 3); +insert into holdings values('APPL', 1, '2000-12-31 23:59:30', 5.16); +insert into holdings values('APPL', 2, '2001-01-01 00:00:30', 2.94); +insert into holdings values('APPL', 1, '2001-01-01 00:01:30', 24.13); +insert into holdings values('MSFT', 1, '2000-12-31 23:59:30', 9.33); +insert into holdings values('MSFT', 2, '2001-01-01 00:00:30', 23.45); +insert into holdings values('MSFT', 1, '2001-01-01 00:01:30', 10.58); +insert into holdings values('DATA', 1, '2000-12-31 23:59:30', 6.65); +insert into holdings values('DATA', 1, '2001-01-01 00:00:30', 17.95); +insert into holdings values('DATA', 1, '2001-01-01 00:01:30', 18.37); +---- +--- Normal Case +---- +set query_dop = 1; +SELECT h.ticker, h.wh, price * shares AS total +FROM holdings h +ASOF JOIN prices p ON h.ticker = p.ticker AND h.wh >= p.wh order by h.ticker; + ticker | wh | total +------------+--------------------------+--------- + APPL | Mon Jan 01 00:00:30 2001 | 2.9400 + APPL | Mon Jan 01 00:01:30 2001 | 48.2600 + MSFT | Mon Jan 01 00:00:30 2001 | 23.4500 + MSFT | Mon Jan 01 00:01:30 2001 | 21.1600 +(4 rows) + +explain (verbose on, costs off) +SELECT h.ticker, h.wh, price * shares AS total +FROM holdings h +ASOF JOIN prices p ON h.ticker = p.ticker AND h.wh >= p.wh order by h.ticker; + QUERY PLAN +------------------------------------------------------------ + Row Adapter + Output: h.ticker, h.wh, ((p.price * h.shares)) + -> Vector Sort + Output: h.ticker, h.wh, ((p.price * h.shares)) + Sort Key: h.ticker + -> Vector Asof Join + Output: h.ticker, h.wh, (p.price * h.shares) + Part Cond: (h.ticker = p.ticker) + Merge Cond: (h.wh >= p.wh) + -> CStore Scan on public.holdings h + Output: h.ticker, h.wh, h.shares + -> CStore Scan on public.prices p + Output: p.price, p.ticker, p.wh +(13 rows) + +SELECT h.ticker, h.wh +FROM ( + SELECT ticker, + wh, + pa + FROM holdings +) h ASOF JOIN ( + SELECT ticker, + wh, + pa + FROM prices +) p + ON h.ticker = p.ticker AND h.pa = p.pa AND h.wh >= p.wh order by h.ticker; + ticker | wh +------------+-------------------------- + APPL | Mon Jan 01 00:01:30 2001 + MSFT | Mon Jan 01 00:00:30 2001 +(2 rows) + +SELECT h.ticker, h.wh +FROM ( + SELECT ticker, + wh, + pa+1 as pas + FROM holdings +) h ASOF JOIN ( + SELECT ticker, + wh, + pa+1 as pas + FROM prices +) p + ON h.ticker = p.ticker AND h.wh >= p.wh AND h.pas >= p.pas order by h.ticker; + ticker | wh +------------+-------------------------- + APPL | Mon Jan 01 00:00:30 2001 + APPL | Mon Jan 01 00:01:30 2001 + MSFT | Mon Jan 01 00:00:30 2001 +(3 rows) + +-- SMP case +set query_dop = 2; +SELECT h.ticker, h.wh, price * shares AS total +FROM holdings h +ASOF JOIN prices p ON h.ticker = p.ticker AND h.wh >= p.wh order by h.ticker; + ticker | wh | total +------------+--------------------------+--------- + APPL | Mon Jan 01 00:00:30 2001 | 2.9400 + APPL | Mon Jan 01 00:01:30 2001 | 48.2600 + MSFT | Mon Jan 01 00:00:30 2001 | 23.4500 + MSFT | Mon Jan 01 00:01:30 2001 | 21.1600 +(4 rows) + +explain (verbose on, costs off) +SELECT h.ticker, h.wh, price * shares AS total +FROM holdings h +ASOF JOIN prices p ON h.ticker = p.ticker AND h.wh >= p.wh order by h.ticker; + QUERY PLAN +----------------------------------------------------------------------------- + Row Adapter + Output: h.ticker, h.wh, ((p.price * h.shares)) + -> Vector Sort + Output: h.ticker, h.wh, ((p.price * h.shares)) + Sort Key: h.ticker + -> Vector Streaming(type: LOCAL GATHER dop: 1/2) + Output: h.ticker, h.wh, ((p.price * h.shares)) + -> Vector Asof Join + Output: h.ticker, h.wh, (p.price * h.shares) + Part Cond: (h.ticker = p.ticker) + Merge Cond: (h.wh >= p.wh) + -> Vector Streaming(type: LOCAL REDISTRIBUTE dop: 2/1) + Output: h.ticker, h.wh, h.shares + Distribute Key: h.ticker + -> CStore Scan on public.holdings h + Output: h.ticker, h.wh, h.shares + -> Vector Streaming(type: LOCAL REDISTRIBUTE dop: 2/1) + Output: p.price, p.ticker, p.wh + Distribute Key: p.ticker + -> CStore Scan on public.prices p + Output: p.price, p.ticker, p.wh +(21 rows) + +---- +--- Clean table and resource +---- +drop table prices; +drop table holdings; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 80b6b29af1aedca09a60d2fe7f5ef7d5607d8cba..625cbbf79ba92b01bb18d12778542e60770294bf 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -645,7 +645,7 @@ test: vec_prepare_003 vec_nestloop_pre vec_mergejoin_prepare vec_sonic_agg_prepa test: vec_sonic_agg1 vec_sonic_agg2 vec_sonic_agg3 test: vec_result vec_expression1 vec_expression2 vec_expression3 vec_sort vec_nestloop1 vec_limit vec_partition vec_partition_1 vec_mergejoin_1 vec_mergejoin_2 vec_material_001 vec_material_002 vec_stream vec_stream_1 vec_mergejoin_inner vec_mergejoin_left vec_mergejoin_semi vec_mergejoin_anti vec_unsupport_expression test: vec_group vec_unique vec_agg1 vec_agg2 vec_agg3 vec_setop_001 vec_setop_002 vec_setop_003 vec_setop_004 vec_setop_005 hw_vec_constrainst vec_mergejoin_aggregation -test: vec_numeric vec_numeric_1 vec_numeric_2 vec_hashjoin1 vec_hashjoin2 vec_hashjoin3 vec_bitmap_1 vec_bitmap_2 wait_status +test: vec_numeric vec_numeric_1 vec_numeric_2 vec_hashjoin1 vec_hashjoin2 vec_hashjoin3 vec_bitmap_1 vec_bitmap_2 wait_status vec_asofjoin test: vec_numeric_sop_1 vec_numeric_sop_2 vec_numeric_sop_3 vec_numeric_sop_4 vec_numeric_sop_5 hw_vec_int4 hw_vec_int8 hw_vec_float4 hw_vec_float8 test: llvm_vecexpr1 llvm_vecexpr2 llvm_vecexpr3 llvm_vecexpr_td llvm_target_expr llvm_target_expr2 llvm_target_expr3 test: llvm_vecagg llvm_vecagg2 llvm_vecagg3 llvm_vecsort llvm_vecsort2 llvm_vechashjoin llvm_vechashjoin2 diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 069a49bd61c36e99b669b91dfd0cdf17760b7f4d..66fb74c8acfc564b0b863da3b20693c0f7ff03dc 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -613,7 +613,7 @@ test: unsupported_features statistic statistic_2 test: hw_setop_writefile -test: vec_nestloop_pre vec_mergejoin_prepare vec_result vec_limit vec_mergejoin_1 vec_mergejoin_2 vec_stream force_vector_engine force_vector_engine2 +test: vec_nestloop_pre vec_mergejoin_prepare vec_result vec_limit vec_mergejoin_1 vec_mergejoin_2 vec_stream force_vector_engine force_vector_engine2 vec_asofjoin test: vec_mergejoin_inner vec_mergejoin_left vec_mergejoin_semi vec_mergejoin_anti llvm_vecexpr1 llvm_vecexpr2 llvm_vecexpr3 llvm_target_expr llvm_target_expr2 llvm_target_expr3 llvm_vecexpr_td #test: vec_nestloop1 test: vec_mergejoin_aggregation llvm_vecagg llvm_vecagg2 llvm_vecagg3 llvm_vechashjoin vector_subpartition diff --git a/src/test/regress/parallel_schedule0B b/src/test/regress/parallel_schedule0B index f0ef71301241b688f4e3de564501312a430ac368..f53e7ca1b0554a5e47c76e26e2d5887998870c33 100644 --- a/src/test/regress/parallel_schedule0B +++ b/src/test/regress/parallel_schedule0B @@ -158,7 +158,7 @@ test: unsupported_features statistic statistic_2 test: hw_setop_writefile -test: vec_nestloop_pre vec_mergejoin_prepare vec_result vec_limit vec_mergejoin_1 vec_mergejoin_2 vec_stream force_vector_engine force_vector_engine2 +test: vec_nestloop_pre vec_mergejoin_prepare vec_result vec_limit vec_mergejoin_1 vec_mergejoin_2 vec_stream force_vector_engine force_vector_engine2 vec_asofjoin test: vec_mergejoin_inner vec_mergejoin_left vec_mergejoin_semi vec_mergejoin_anti llvm_vecexpr1 llvm_vecexpr2 llvm_vecexpr3 llvm_target_expr llvm_target_expr2 llvm_target_expr3 llvm_vecexpr_td #test: vec_nestloop1 test: vec_mergejoin_aggregation llvm_vecagg llvm_vecagg2 llvm_vecagg3 llvm_vechashjoin vector_subpartition diff --git a/src/test/regress/pg_regress.cpp b/src/test/regress/pg_regress.cpp index c7724dd17ce10b2c2cdaf6775f16496291b1c746..9d31029ac67f06e67ba4865ef2d4f10b0893ed1b 100644 --- a/src/test/regress/pg_regress.cpp +++ b/src/test/regress/pg_regress.cpp @@ -5516,7 +5516,7 @@ static void check_global_variables() } } -#define BASE_PGXC_LIKE_MACRO_NUM 1385 +#define BASE_PGXC_LIKE_MACRO_NUM 1389 static void check_pgxc_like_macros() { #ifdef BUILD_BY_CMAKE diff --git a/src/test/regress/sql/vec_asofjoin.sql b/src/test/regress/sql/vec_asofjoin.sql new file mode 100644 index 0000000000000000000000000000000000000000..fa31b11ea266cc0b8e7c9976740c46ae08251972 --- /dev/null +++ b/src/test/regress/sql/vec_asofjoin.sql @@ -0,0 +1,101 @@ +/* + * This file is used to test the function of ExecVecAsofJoin() + */ +---- +--- Create Table and Insert Data +---- + +CREATE TABLE prices( + ticker char(10), + pa integer, + wh timestamp, + price DECIMAL(10,2))with(orientation=column); +CREATE TABLE holdings( + ticker char(10), + pa integer, + wh timestamp, + shares DECIMAL(10,2))with(orientation=column); + + + +insert into prices values('APPL', 1, '2001-01-01 00:00:00', 1); +insert into prices values('APPL', 1, '2001-01-01 00:01:00', 2); +insert into prices values('APPL', 1, '2001-01-01 00:02:00', 3); +insert into prices values('MSFT', 2, '2001-01-01 00:00:00', 1); +insert into prices values('MSFT', 2, '2001-01-01 00:01:00', 2); +insert into prices values('MSFT', 2, '2001-01-01 00:02:00', 3); +insert into prices values('GOOG', 1, '2001-01-01 00:00:00', 1); +insert into prices values('GOOG', 1,'2001-01-01 00:01:00', 2); +insert into prices values('GOOG', 1,'2001-01-01 00:02:00', 3); + +insert into holdings values('APPL', 1, '2000-12-31 23:59:30', 5.16); +insert into holdings values('APPL', 2, '2001-01-01 00:00:30', 2.94); +insert into holdings values('APPL', 1, '2001-01-01 00:01:30', 24.13); +insert into holdings values('MSFT', 1, '2000-12-31 23:59:30', 9.33); +insert into holdings values('MSFT', 2, '2001-01-01 00:00:30', 23.45); +insert into holdings values('MSFT', 1, '2001-01-01 00:01:30', 10.58); +insert into holdings values('DATA', 1, '2000-12-31 23:59:30', 6.65); +insert into holdings values('DATA', 1, '2001-01-01 00:00:30', 17.95); +insert into holdings values('DATA', 1, '2001-01-01 00:01:30', 18.37); + +---- +--- Normal Case +---- +set query_dop = 1; +SELECT h.ticker, h.wh, price * shares AS total +FROM holdings h +ASOF JOIN prices p ON h.ticker = p.ticker AND h.wh >= p.wh order by h.ticker; + +explain (verbose on, costs off) +SELECT h.ticker, h.wh, price * shares AS total +FROM holdings h +ASOF JOIN prices p ON h.ticker = p.ticker AND h.wh >= p.wh order by h.ticker; + +SELECT h.ticker, h.wh +FROM ( + SELECT ticker, + wh, + pa + FROM holdings +) h ASOF JOIN ( + SELECT ticker, + wh, + pa + FROM prices +) p + ON h.ticker = p.ticker AND h.pa = p.pa AND h.wh >= p.wh order by h.ticker; + +SELECT h.ticker, h.wh +FROM ( + SELECT ticker, + wh, + pa+1 as pas + FROM holdings +) h ASOF JOIN ( + SELECT ticker, + wh, + pa+1 as pas + FROM prices +) p + ON h.ticker = p.ticker AND h.wh >= p.wh AND h.pas >= p.pas order by h.ticker; + + +-- SMP case +set query_dop = 2; +SELECT h.ticker, h.wh, price * shares AS total +FROM holdings h +ASOF JOIN prices p ON h.ticker = p.ticker AND h.wh >= p.wh order by h.ticker; +explain (verbose on, costs off) +SELECT h.ticker, h.wh, price * shares AS total +FROM holdings h +ASOF JOIN prices p ON h.ticker = p.ticker AND h.wh >= p.wh order by h.ticker; + + + +---- +--- Clean table and resource +---- + +drop table prices; +drop table holdings; +