diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 32bc15f2c..6111ea743 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -2894,12 +2894,25 @@ static void transformVariableSetValueStmt(ParseState* pstate, VariableSetStmt* s static Query* transformSelectStmt(ParseState* pstate, SelectStmt* stmt, bool isFirstNode, bool isCreateView) { Query* qry = makeNode(Query); + ParseState *origin_pstate = NULL; + SelectStmt *origin_stmt = NULL; Node* qual = NULL; ListCell* l = NULL; qry->commandType = CMD_SELECT; if (stmt->startWithClause != NULL) { + errno_t rc; + origin_stmt = (SelectStmt *)copyObject(stmt); + origin_pstate = make_parsestate(NULL); + rc = memcpy_s(origin_pstate, sizeof(ParseState), pstate, sizeof(ParseState)); + origin_pstate->p_rtable = list_copy(pstate->p_rtable); + origin_pstate->p_ctenamespace = list_copy(pstate->p_ctenamespace); + origin_pstate->p_relnamespace = list_copy(pstate->p_relnamespace); + origin_pstate->p_varnamespace = list_copy(pstate->p_varnamespace); + origin_pstate->p_joinlist = list_copy(pstate->p_joinlist); + origin_pstate->p_joinexprs = list_copy(pstate->p_joinexprs); + securec_check(rc, "\0", "\0"); pstate->p_addStartInfo = true; pstate->p_sw_selectstmt = stmt; pstate->origin_with = (WithClause *)copyObject(stmt->withClause); @@ -2934,7 +2947,7 @@ static Query* transformSelectStmt(ParseState* pstate, SelectStmt* stmt, bool isF /* transform START WITH...CONNECT BY clause */ if (shouldTransformStartWithStmt(pstate, stmt, qry)) { - transformStartWith(pstate, stmt, qry); + transformStartWith(pstate, origin_pstate, stmt, origin_stmt, qry, isFirstNode, isCreateView); } /* transform targetlist */ diff --git a/src/common/backend/parser/parse_expr.cpp b/src/common/backend/parser/parse_expr.cpp index 3a6f1e9ff..f36cc1558 100644 --- a/src/common/backend/parser/parse_expr.cpp +++ b/src/common/backend/parser/parse_expr.cpp @@ -98,6 +98,7 @@ static Node* transformConnectByRootFuncCall(ParseState* pstate, Node* funcNameVa static bool CheckSwAbortedRTE(ParseState *pstate, char *relname); static char *ColumnRefFindRelname(ParseState *pstate, const char *colname); static Node *transformStartWithColumnRef(ParseState *pstate, ColumnRef *cref, char **colname); +static Node *transformStartWithWhereClauseColumnRef(ParseState *pstate, ColumnRef *cref, char *colname); static Node* tryTransformFunc(ParseState* pstate, List* fields, int location); static void SubCheckOutParam(List* exprtargs, Oid funcid); static Node* transformPrefixKey(ParseState* pstate, PrefixKey* pkey); @@ -859,8 +860,13 @@ Node* transformColumnRef(ParseState* pstate, ColumnRef* cref) AssertEreport(IsA(field1, String), MOD_OPT, ""); colname = strVal(field1); - if (pstate->p_hasStartWith) { - Node *expr = transformStartWithColumnRef(pstate, cref, &colname); + if (pstate->p_hasStartWith || pstate->p_split_where_for_swcb) { + Node *expr = NULL; + if (pstate->p_hasStartWith) { + expr = transformStartWithColumnRef(pstate, cref, &colname); + } else { + expr = transformStartWithWhereClauseColumnRef(pstate, cref, colname); + } /* function case, return directly */ if (expr != NULL) { @@ -1802,7 +1808,7 @@ static Node* transformFuncCall(ParseState* pstate, FuncCall* fn) /* ... and hand off to ParseFuncOrColumn */ result = ParseFuncOrColumn(pstate, fn->funcname, targs, last_srf, fn, fn->location, fn->call_func); - if (IsStartWithFunction((FuncExpr*)result) && !pstate->p_hasStartWith) { + if (IsStartWithFunction((FuncExpr*)result) && !pstate->p_hasStartWith && !pstate->p_split_where_for_swcb) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_OPT), errmsg("Invalid function call."), @@ -3852,3 +3858,47 @@ ParseExprKindName(ParseExprKind exprKind) } return "unrecognized expression kind"; } + +/* + * transformStartWithWhereClauseColumnRef + * transform cref for start with where spliting stage + */ +static Node *transformStartWithWhereClauseColumnRef(ParseState *pstate, ColumnRef *cref, char *colname) +{ + Assert (colname != NULL); + + ListCell *lc = NULL; + RangeTblEntry *rte = NULL; + foreach (lc, pstate->p_rtable) { + rte = (RangeTblEntry *)lfirst(lc); + if (rte->rtekind == RTE_SUBQUERY && rte->alias != NULL && rte->alias->aliasname != NULL && + pg_strcasecmp(rte->alias->aliasname, "__sw_pseudo_col_table__") == 0) { + break; + } + } + + int len = list_length(cref->fields); + if (len == 1) { + Node *field = (Node*)linitial(cref->fields); + + if (pg_strcasecmp(colname, "connect_by_root") == 0) { + Node *funexpr = transformConnectByRootFuncCall(pstate, field, cref); + + /* + * Return function funexpr, otherwise process + * connect_by_root as regular case + */ + if (funexpr != NULL) { + return funexpr; + } + } + + /* for pseudo column, we return the corresponding var */ + if (IsPseudoReturnColumn(colname)) { + return scanRTEForColumn(pstate, rte, colname, cref->location); + } + } + + return NULL; +} + diff --git a/src/common/backend/parser/parse_relation.cpp b/src/common/backend/parser/parse_relation.cpp index bfcd6d852..82d84396c 100755 --- a/src/common/backend/parser/parse_relation.cpp +++ b/src/common/backend/parser/parse_relation.cpp @@ -2202,6 +2202,12 @@ void expandRTE(RangeTblEntry* rte, int rtindex, int sublevels_up, int location, break; case RTE_SUBQUERY: { /* Subquery RTE */ + /* for start with pseudo table, we mark its targetlist resjunk */ + if (rte->alias != NULL && rte->alias->aliasname != NULL && + pg_strcasecmp(rte->alias->aliasname, "__sw_pseudo_col_table__") == 0) { + break; + } + ListCell* aliasp_item = list_head(rte->eref->colnames); ListCell* tlistitem = NULL; diff --git a/src/common/backend/parser/parse_startwith.cpp b/src/common/backend/parser/parse_startwith.cpp index e50e520ce..d1b8f8f2d 100644 --- a/src/common/backend/parser/parse_startwith.cpp +++ b/src/common/backend/parser/parse_startwith.cpp @@ -62,6 +62,8 @@ */ typedef struct StartWithTransformContext { ParseState* pstate; + ParseState* origin_pstate; + SelectStmt *origin_stmt; List *relInfoList; List *where_infos; List *connectby_prior_name; @@ -95,6 +97,8 @@ typedef struct StartWithTransformContext { Node *connectByLevelExpr; Node *connectByOtherExpr; bool nocycle; + /* used to track the varnos of given expr */ + Bitmapset *expr_varno_set; } StartWithTransformContext; typedef enum StartWithRewrite { @@ -133,11 +137,11 @@ static void AddWithClauseToBranch(ParseState *pstate, SelectStmt *stmt, List *re static void transformSingleRTE(ParseState* pstate, Query* qry, StartWithTransformContext *context, - Node *start_clause); -static void transformFromList(ParseState* pstate, - Query* qry, - StartWithTransformContext *context, - Node *n); + Node *sw_clause); +static void transformFromList(ParseState* pstate, Query* qry, + StartWithTransformContext *context, Node *sw_clause, + List *tlist, + bool is_first_node, bool is_creat_view); static RangeTblEntry *transformStartWithCTE(ParseState* pstate, List *prior_names); static bool preSkipPLSQLParams(ParseState *pstate, ColumnRef *cref); @@ -162,6 +166,7 @@ static void CreateStartWithCTE(ParseState *pstate, static Node *makeBoolAConst(bool state, int location); static StartWithRewrite ChooseSWCBStrategy(StartWithTransformContext context); static Node *tryReplaceFakeValue(Node *node); +static RangeSubselect *makeSWCBPseudoTable(); static Node *makeBoolAConst(bool state, int location) { @@ -232,7 +237,8 @@ static Node *makeBoolAConst(bool state, int location) * **************************************************************************************** */ -void transformStartWith(ParseState *pstate, SelectStmt *stmt, Query *qry) +void transformStartWith(ParseState *pstate, ParseState *origin_pstate, SelectStmt *stmt, + SelectStmt *origin_stmt, Query *qry, bool is_first_node, bool is_creat_view) { ListCell *lc = NULL; StartWithTransformContext context; @@ -249,6 +255,8 @@ void transformStartWith(ParseState *pstate, SelectStmt *stmt, Query *qry) securec_check(rc, "\0", "\0"); context.pstate = pstate; + context.origin_pstate = origin_pstate; + context.origin_stmt = origin_stmt; context.relInfoList = NULL; context.connectby_prior_name = NULL; @@ -272,7 +280,8 @@ void transformStartWith(ParseState *pstate, SelectStmt *stmt, Query *qry) if (op == SW_SINGLE) { transformSingleRTE(pstate, qry, &context, (Node *)stmt->startWithClause); } else { - transformFromList(pstate, qry, &context, (Node *)stmt->startWithClause); + transformFromList(pstate, qry, &context, (Node *)stmt->startWithClause, stmt->targetList, + is_first_node, is_creat_view); stmt->whereClause = context.whereClause; } @@ -1510,19 +1519,84 @@ static void AddWithClauseToBranch(ParseState *pstate, SelectStmt *stmt, List *re return; } -static bool count_columnref_walker(Node *node, int *columnref_count) +static void make_full_varnamespace(StartWithTransformContext *context, RangeSubselect *pseudo_rte, + bool is_first_node, bool is_create_view) +{ + SelectStmt *stmt = makeNode(SelectStmt); + stmt->withClause = context->origin_stmt->withClause; + stmt->fromClause = lappend(context->origin_stmt->fromClause, pseudo_rte); + stmt->targetList = context->origin_stmt->targetList; + stmt->lockingClause = context->origin_stmt->lockingClause; + stmt->windowClause = context->origin_stmt->windowClause; + stmt->intoClause = context->origin_stmt->intoClause; + + (void)transformStmt(context->origin_pstate, (Node *)stmt, is_first_node, is_create_view); +} + +static bool is_swcb_pseudo_var(Var *var, StartWithTransformContext *context) +{ + bool is_pseudo_tbl = false; + if (var != NULL && var->varlevelsup == 0) { + RangeTblEntry *rte = rt_fetch(var->varno, context->origin_pstate->p_rtable); + if (rte->rtekind == RTE_SUBQUERY) { + Alias *alias = rte->alias; + is_pseudo_tbl = alias != NULL && alias->aliasname != NULL && + pg_strcasecmp(alias->aliasname, "__sw_pseudo_col_table__") == 0; + } + } + return is_pseudo_tbl; +} + +static bool find_varno_in_node(Node *node, StartWithTransformContext *context) +{ + if (node == NULL) { + return false; + } + + check_stack_depth(); + + if (IsA(node, Var)) { + /* if var is passed from upper query, we don't push it down into start with */ + Var *var = (Var *)node; + if (((Var *)node)->varlevelsup == 0) { + context->expr_varno_set = bms_add_member(context->expr_varno_set, var->varno); + } else { + bms_free_ext(context->expr_varno_set); + return true; + } + } else { + return expression_tree_walker(node, (bool (*)())find_varno_in_node, (void *)context); + } + + return false; +} + +static bool count_table_ref_walker(Node *node, StartWithTransformContext *context) { if (node == NULL) { return false; } - if (!IsA(node, ColumnRef)) { - return raw_expression_tree_walker(node, (bool (*)()) count_columnref_walker, (void*)columnref_count); + check_stack_depth(); + + /* we don't push down sublink */ + if (IsA(node, SubLink)) { + bms_free_ext(context->expr_varno_set); + return true; } - *columnref_count = *columnref_count + 1; + if (!IsA(node, ColumnRef)) { + return raw_expression_tree_walker(node, (bool (*)()) count_table_ref_walker, (void*)context); + } - return false; + Node *ret_node = (Node *)transformColumnRef(context->origin_pstate, (ColumnRef *)node); + + if ((IsA(ret_node, Var) && is_swcb_pseudo_var(((Var *)ret_node), context)) || + (IsA(ret_node, FuncExpr) && IsStartWithFunction((FuncExpr *)ret_node))) { + return false; + } + + return find_varno_in_node(ret_node, context); } /* @@ -1544,7 +1618,8 @@ static bool count_columnref_walker(Node *node, int *columnref_count) * NON-JOIN expr will be * (C) OR (E) AND (F) OR (I) */ -static void split_where_expr_by_join(Node **p_expr_join, Node **p_expr_non_join) +static void split_where_expr_by_join(Node **p_expr_join, Node **p_expr_non_join, + StartWithTransformContext *context) { if (p_expr_join == NULL || (p_expr_join != NULL && *p_expr_join == NULL)) { return; @@ -1559,10 +1634,10 @@ static void split_where_expr_by_join(Node **p_expr_join, Node **p_expr_non_join) if (a_expr_join->kind == AEXPR_AND || a_expr_join->kind == AEXPR_OR) { if (a_expr_join->lexpr != NULL) { - split_where_expr_by_join(&(a_expr_join->lexpr), &(a_expr_non_join->lexpr)); + split_where_expr_by_join(&(a_expr_join->lexpr), &(a_expr_non_join->lexpr), context); } if (a_expr_join->rexpr != NULL) { - split_where_expr_by_join(&(a_expr_join->rexpr), &(a_expr_non_join->rexpr)); + split_where_expr_by_join(&(a_expr_join->rexpr), &(a_expr_non_join->rexpr), context); } if (a_expr_join->lexpr == NULL) { *p_expr_join = a_expr_join->rexpr; @@ -1575,68 +1650,32 @@ static void split_where_expr_by_join(Node **p_expr_join, Node **p_expr_non_join) } else if (a_expr_non_join->rexpr == NULL) { *p_expr_non_join = a_expr_non_join->lexpr; } - } else if (a_expr_join->kind == AEXPR_OP) { - int l_cref_count = 0; - int r_cref_count = 0; - count_columnref_walker(a_expr_join->lexpr, &l_cref_count); - count_columnref_walker(a_expr_join->rexpr, &r_cref_count); - - /* if there're columnRefs at both sides meaning its a join-qual, otherwise, non-join-qual */ - if (l_cref_count != 0 && r_cref_count != 0) { + } else { + bms_free_ext(context->expr_varno_set); + count_table_ref_walker((Node *)a_expr_join, context); + /* if there're more than one tableref in this expr, then must be a join */ + if (bms_membership(context->expr_varno_set) == BMS_MULTIPLE) { pfree_ext(expr_non_join); *p_expr_non_join = NULL; } else { pfree_ext(expr_join); *p_expr_join = NULL; } - } else { - /* must not be a join qual for non AEXPR_OP type */ - pfree_ext(expr_join); - *p_expr_join = NULL; } } else { /* must not be a join qual for non-a_expr type */ - pfree_ext(expr_join); - *p_expr_join = NULL; + bms_free_ext(context->expr_varno_set); + count_table_ref_walker(expr_join, context); + if (bms_membership(context->expr_varno_set) == BMS_MULTIPLE) { + pfree_ext(expr_non_join); + *p_expr_non_join = NULL; + } else { + pfree_ext(expr_join); + *p_expr_join = NULL; + } } } -static bool walker_to_exclude_non_join_quals(Node *node, Node *context_node) -{ - if (node == NULL) { - return false; - } - - if (!IsA(node, A_Expr)) { - return raw_expression_tree_walker(node, (bool (*)()) walker_to_exclude_non_join_quals, (void*)NULL); - } - - A_Expr* expr = (A_Expr*) node; - if (expr->kind != AEXPR_OP) { - return raw_expression_tree_walker(node, (bool (*)()) walker_to_exclude_non_join_quals, (void*)NULL); - } - - /* - * this is to achieve consistent result sets with those produced by the original - * start with .. connect by syntax, which does not push filter quals down to connect quals. - * if no more than one column item appears inside an AEXPR_OP, we guess that it is - * not a join qual so should not be filtered in sw op, and force it to be true. - * this rule is not always correct but should work fine most of the time. - * could be improved later on, e.g. find better ways to extract non-join quals - * from the where clause. - */ - int columnref_count = 0; - (void) raw_expression_tree_walker(node, (bool (*)()) count_columnref_walker, (void*)&columnref_count); - - if (columnref_count <= 1) { - expr->lexpr = makeBoolAConst(true, -1); - expr->rexpr = makeBoolAConst(true, -1); - expr->kind = AEXPR_OR; - } - - return false; -} - /* * -------------------------------------------------------------------------------------- * @Brief: Create SWCB's conversion CTE's inner branch, normally we add ConnectByExpr to @@ -1690,17 +1729,6 @@ static SelectStmt *CreateStartWithCTEInnerBranch(ParseState* pstate, origin_table = (Node *)joiniter; } - - if (whereClause != NULL) { - JoinExpr *final_join = (JoinExpr *)origin_table; - - if (final_join->quals == NULL) { - final_join->quals = whereClause; - } else { - final_join->quals = - (Node *)makeA_Expr(AEXPR_AND, NULL, whereClause, final_join->quals, -1); - } - } } /* process regular/level */ @@ -1714,7 +1742,15 @@ static SelectStmt *CreateStartWithCTEInnerBranch(ParseState* pstate, join->larg = (Node *)work_table; join->rarg = origin_table; join->usingClause = NIL; - join->quals = (Node *)copyObject(connectByExpr); + Node *where_quals = NULL; + if (whereClause != NULL && connectByExpr != NULL) { + where_quals = (Node *)makeA_Expr(AEXPR_AND, NULL, whereClause, connectByExpr, -1); + } else if (whereClause != NULL) { + where_quals = (Node *)copyObject(whereClause); + } else { + where_quals = (Node *)copyObject(connectByExpr); + } + join->quals = where_quals; result->targetList = expandAllTargetList(relInfoList); result->fromClause = list_make1(join); @@ -1937,8 +1973,8 @@ static RangeTblEntry *transformStartWithCTE(ParseState* pstate, List *prior_name * @Return: detail to be added * -------------------------------------------------------------------------------------- */ -static void transformFromList(ParseState* pstate, Query* qry, - StartWithTransformContext *context, Node *n) +static void transformFromList(ParseState* pstate, Query* qry, StartWithTransformContext *context, + Node *sw_clause, List *tlist, bool is_first_node, bool is_creat_view) { ListCell *lc = NULL; A_Expr *startWithExpr = (A_Expr *)context->startWithExpr; @@ -1947,7 +1983,13 @@ static void transformFromList(ParseState* pstate, Query* qry, Node *whereClauseOnlyJoin = (Node *)copyObject(context->whereClause); if (context->whereClause != NULL) { - split_where_expr_by_join(&whereClauseOnlyJoin, &(context->whereClause)); + RangeSubselect *sw_pseudo_table = makeSWCBPseudoTable(); + context->origin_pstate->p_split_where_for_swcb = true; + + /* add pseudo var namespace and alias var namespace into where_pstate */ + make_full_varnamespace(context, sw_pseudo_table, is_first_node, is_creat_view); + split_where_expr_by_join(&whereClauseOnlyJoin, &(context->whereClause), context); + free_parsestate(context->origin_pstate); } /* make union-all branch for none-recursive part */ @@ -1956,7 +1998,7 @@ static void transformFromList(ParseState* pstate, Query* qry, /* make joinExpr for recursive part */ SelectStmt *innerBranch = CreateStartWithCTEInnerBranch(pstate, context, - relInfoList, (Node *)connectByExpr, whereClauseOnlyJoin); + relInfoList, (Node *)connectByExpr, (Node *)copyObject(whereClauseOnlyJoin)); CreateStartWithCTE(pstate, qry, outerBranch, innerBranch, context); @@ -2009,7 +2051,7 @@ static void transformFromList(ParseState* pstate, Query* qry, * -------------------------------------------------------------------------------------- */ static void transformSingleRTE(ParseState* pstate, Query* qry, - StartWithTransformContext *context, Node *start_clause) + StartWithTransformContext *context, Node *sw_clause) { ListCell *lc = NULL; @@ -2119,3 +2161,44 @@ static List *expandAllTargetList(List *targetRelInfoList) return targetlist; } + +/* + * semtc_make_swcb_pseudo_table + * make a rte contains all of the start with pseudo cols, used for transform + */ +static RangeSubselect *makeSWCBPseudoTable() +{ + /* make a stmt for subquery */ + List *pseudo_tlist = NULL; + ResTarget *res_target = NULL; + A_Const *a_const = NULL; + StartWithCTEPseudoReturnAtts *att = NULL; + for (uint i = 0; i < STARTWITH_PSEUDO_RETURN_ATTNUMS; i++) { + /* if allow keyword rownum as ident, we don't treat "rownum" as pseudo column */ + if (i == SWCOL_ROWNUM) { + continue; + } + att = &g_StartWithCTEPseudoReturnAtts[i]; + + /* make var for pseudo return column */ + a_const = makeNode(A_Const); + a_const->val.type = T_Integer; + a_const->val.val.ival = 1; + a_const->location = -1; + res_target = makeNode(ResTarget); + res_target->name = att->colname; + res_target->indirection = NIL; + res_target->val = (Node *)a_const; + res_target->location = -1; + pseudo_tlist = lappend(pseudo_tlist, res_target); + } + + SelectStmt *stmt = makeNode(SelectStmt); + stmt->targetList = pseudo_tlist; + + RangeSubselect* subselect = makeNode(RangeSubselect); + subselect->subquery = (Node*)stmt; + Alias* alias = makeAlias("__sw_pseudo_col_table__", NIL); + subselect->alias = alias; + return subselect; +} diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index 238dae399..8cc76d838 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -53,7 +53,8 @@ extern ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, bool lateral_on * StartWith support transformStartWith() is the only entry point for START WITH...CONNECT BY * processing in parser/transformar layer */ -extern void transformStartWith(ParseState *pstate, SelectStmt *stmt, Query *qry); +extern void transformStartWith(ParseState *pstate, ParseState *origin_pstate, SelectStmt *stmt, + SelectStmt *origin_stmt, Query *qry, bool is_first_node, bool is_creat_view); extern void AddStartWithCTEPseudoReturnColumns(CommonTableExpr *cte, RangeTblEntry *rte, Index rte_index); extern void pretransformAggWithUserSet(ParseState* pstate, List** targetList, Node* groupClause, ParseExprKind exprKind); diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 45cca960c..ca831d37a 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -196,6 +196,7 @@ struct ParseState { List *sw_fromClause; WithClause *origin_with; bool p_hasStartWith; + bool p_split_where_for_swcb; bool p_has_ignore; /* whether SQL has ignore hint */ /* diff --git a/src/test/regress/expected/sw_bugfix-2.out b/src/test/regress/expected/sw_bugfix-2.out index 47da77ea0..99cd9f242 100755 --- a/src/test/regress/expected/sw_bugfix-2.out +++ b/src/test/regress/expected/sw_bugfix-2.out @@ -1897,6 +1897,130 @@ DETAIL: Unsupported node type: 900. SELECT * FROM RLTEST CONNECT BY PRIOR B=A OR (MOD(ROWNUM+1,2) = 0); ERROR: column specified by prior cannot concide with ROWNUM/LEVEL. DETAIL: Unsupported node type: 900. +-- test for multi table join used with pseudo col in where clause +delete from rltest; +INSERT INTO RLTEST VALUES('1','2'),('2','3'),('3','1'); +explain select * from rltest r1,rltest r2 where r1.a<=connect_by_isleaf+level+connect_by_iscycle+connect_by_root(r1.a) start with r1.a=1 connect by nocycle prior r2.b=r1.a; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + CTE Scan on tmp_reuslt (cost=788273757.40..1796601238.36 rows=8402729008 width=32) + Filter: (("r1@a")::bigint <= (((connect_by_isleaf + level) + connect_by_iscycle) + (connect_by_root(("r1@a")::text))::bigint)) + CTE tmp_reuslt + -> StartWith Operator (cost=0.00..788273757.40 rows=25208187024 width=32) + Start With pseudo atts: RUITR, array_key_4, array_col_1 + -> Recursive Union (cost=0.00..788273757.40 rows=25208187024 width=32) + -> Nested Loop (cost=0.00..264.22 rows=15984 width=32) + -> Seq Scan on rltest r2 (cost=0.00..27.76 rows=1776 width=16) + -> Materialize (cost=0.00..36.69 rows=9 width=16) + -> Seq Scan on rltest r1 (cost=0.00..36.64 rows=9 width=16) + Filter: ((a)::bigint = 1) + -> Hash Join (cost=5194.80..28410975.27 rows=2520817104 width=32) + Hash Cond: (r1.a = tmp_reuslt."r2@b") + -> Nested Loop (cost=0.00..39487.16 rows=3154176 width=32) + -> Seq Scan on rltest r2 (cost=0.00..27.76 rows=1776 width=16) + -> Materialize (cost=0.00..36.64 rows=1776 width=16) + -> Seq Scan on rltest r1 (cost=0.00..27.76 rows=1776 width=16) + -> Hash (cost=3196.80..3196.80 rows=159840 width=8) + -> WorkTable Scan on tmp_reuslt (cost=0.00..3196.80 rows=159840 width=8) +(19 rows) + +select * from rltest r1,rltest r2 where r1.a<=connect_by_isleaf+level+connect_by_iscycle+connect_by_root(r1.a) start with r1.a=1 connect by nocycle prior r2.b=r1.a; + a | b | a | b +---+---+---+--- + 1 | 2 | 1 | 2 + 2 | 3 | 2 | 3 + 3 | 1 | 3 | 1 + 2 | 3 | 3 | 1 + 1 | 2 | 2 | 3 + 1 | 2 | 2 | 3 + 3 | 1 | 1 | 2 + 2 | 3 | 3 | 1 + 3 | 1 | 3 | 1 + 1 | 2 | 1 | 2 + 1 | 2 | 3 | 1 + 1 | 2 | 1 | 2 + 2 | 3 | 2 | 3 + 1 | 2 | 2 | 3 + 3 | 1 | 1 | 2 +(15 rows) + +-- test cross reference in start with nested query +create table test_t1(a1 int,b1 int); +create table test_t2(a2 int,b2 int); +create table test_t3(a3 int,b3 int); +insert into test_t1 values(1,2),(2,3),(3,1); +insert into test_t2 values(1,2),(2,3),(3,1); +insert into test_t3 values(1,2),(2,3),(3,1); +explain select * from test_t1 left join test_t2 on a1=a2 where exists (select 1 from test_t3 where a1+a2 is not null connect by level<2) connect by level<2; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + CTE Scan on tmp_reuslt (cost=1732932777.76..800532784124548096.00 rows=26659731370 width=16) + Filter: (SubPlan 3) + CTE tmp_reuslt + -> StartWith Operator (cost=58.35..1732932777.76 rows=53319462741 width=16) + Start With pseudo atts: RUITR + -> Recursive Union (cost=58.35..1732932777.76 rows=53319462741 width=16) + -> Hash Left Join (cost=58.35..355.67 rows=23091 width=16) + Hash Cond: (swtest.test_t1.a1 = swtest.test_t2.a2) + -> Seq Scan on test_t1 (cost=0.00..31.49 rows=2149 width=8) + -> Hash (cost=31.49..31.49 rows=2149 width=8) + -> Seq Scan on test_t2 (cost=0.00..31.49 rows=2149 width=8) + -> Nested Loop (cost=58.35..66654316.73 rows=5331943965 width=16) + -> WorkTable Scan on tmp_reuslt (cost=0.00..4618.20 rows=230910 width=0) + -> Materialize (cost=58.35..471.13 rows=23091 width=16) + -> Hash Left Join (cost=58.35..355.67 rows=23091 width=16) + Hash Cond: (swtest.test_t1.a1 = swtest.test_t2.a2) + -> Seq Scan on test_t1 (cost=0.00..31.49 rows=2149 width=8) + -> Hash (cost=31.49..31.49 rows=2149 width=8) + -> Seq Scan on test_t2 (cost=0.00..31.49 rows=2149 width=8) + SubPlan 3 + -> Result (cost=15013894.35..24250339.33 rows=461822249 width=0) + One-Time Filter: ((tmp_reuslt."test_t1@a1" + tmp_reuslt."test_t2@a2") IS NOT NULL) + CTE tmp_reuslt + -> StartWith Operator (cost=0.00..15013894.35 rows=461822249 width=8) + Start With pseudo atts: RUITR + -> Recursive Union (cost=0.00..15013894.35 rows=461822249 width=8) + -> Seq Scan on test_t3 (cost=0.00..31.49 rows=2149 width=8) + -> Nested Loop (cost=0.00..577741.79 rows=46182010 width=8) + -> WorkTable Scan on tmp_reuslt (cost=0.00..429.80 rows=21490 width=0) + -> Materialize (cost=0.00..42.23 rows=2149 width=8) + -> Seq Scan on test_t3 (cost=0.00..31.49 rows=2149 width=8) + -> CTE Scan on tmp_reuslt (cost=0.00..9236444.98 rows=461822249 width=0) +(32 rows) + +drop table test_t1; +drop table test_t2; +drop table test_t3; +-- test sys funcs in multi table join +select sys_connect_by_path(r1.a,'->') from rltest r1, rltest r2 where r1.a=r2.a connect by level<2; + sys_connect_by_path +--------------------- + ->1 + ->2 + ->3 +(3 rows) + +select connect_by_root(r1.a) from rltest r1, rltest r2 where r1.a=r2.a connect by level<2; + connect_by_root +----------------- + 1 + 2 + 3 +(3 rows) + +-- test for where push down +create table sin_col_tbl(a int); +SELECT 1 +FROM sin_col_tbl, + rltest +WHERE (sin_col_tbl.a+rltest.b) IS NOT NULL +START WITH 1=1 +CONNECT BY level<2 and prior rltest.b=rltest.a; + ?column? +---------- +(0 rows) + +drop table sin_col_tbl; DROP TABLE RLTEST; create table nocycle_tbl(id int, lid int, name text); insert into nocycle_tbl values (1,3,'A'),(2,1,'B'),(3,2,'C'),(4,2,'D'),(5,3,'E'); @@ -2130,6 +2254,7 @@ drop table if exists sw_test; drop table if exists sw_tb_1; create table sw_tb_1(a int,b int,c int,d int); create table sw_tb_2(a int,b int,c int,d int); +create table sw_tb_3(a int, b int, c int, d int); insert into sw_tb_1 values(1,1,1,1); insert into sw_tb_1 values(2,2,2,2); insert into sw_tb_1 values(3,3,3,3); @@ -2189,5 +2314,31 @@ explain select * from sw_tb_1,sw_tb_2 where (sw_tb_1.a+sw_tb_1.b=sw_tb_2.b or sw -> WorkTable Scan on tmp_reuslt (cost=0.00..16.40 rows=820 width=4) (22 rows) +explain select * from sw_tb_3 where exists (select * from sw_tb_1, sw_tb_2 where sw_tb_1.a + sw_tb_2.a = sw_tb_3.a connect by level < 2); + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------- + Seq Scan on sw_tb_3 (cost=0.00..57437072471119520.00 rows=888 width=16) + Filter: (SubPlan 2) + SubPlan 2 + -> CTE Scan on tmp_reuslt (cost=32340693958958.68..57212759635253.08 rows=4974413135259 width=0) + Filter: (("sw_tb_1@a" + "sw_tb_2@a") = sw_tb_3.a) + CTE tmp_reuslt + -> StartWith Operator (cost=0.00..32340693958958.68 rows=994882627051776 width=32) + Start With pseudo atts: RUITR + -> Recursive Union (cost=0.00..32340693958958.68 rows=994882627051776 width=32) + -> Nested Loop (cost=0.00..39487.16 rows=3154176 width=32) + -> Seq Scan on sw_tb_1 (cost=0.00..27.76 rows=1776 width=16) + -> Materialize (cost=0.00..36.64 rows=1776 width=16) + -> Seq Scan on sw_tb_2 (cost=0.00..27.76 rows=1776 width=16) + -> Nested Loop (cost=0.00..1244304137843.60 rows=99488262389760 width=32) + -> Nested Loop (cost=0.00..700857939.40 rows=56018165760 width=16) + -> WorkTable Scan on tmp_reuslt (cost=0.00..630835.20 rows=31541760 width=0) + -> Materialize (cost=0.00..36.64 rows=1776 width=16) + -> Seq Scan on sw_tb_1 (cost=0.00..27.76 rows=1776 width=16) + -> Materialize (cost=0.00..36.64 rows=1776 width=16) + -> Seq Scan on sw_tb_2 (cost=0.00..27.76 rows=1776 width=16) +(20 rows) + drop table sw_tb_1; drop table sw_tb_2; +drop table sw_tb_3; diff --git a/src/test/regress/sql/sw_bugfix-2.sql b/src/test/regress/sql/sw_bugfix-2.sql index bc6a37187..d98ff7900 100644 --- a/src/test/regress/sql/sw_bugfix-2.sql +++ b/src/test/regress/sql/sw_bugfix-2.sql @@ -642,6 +642,37 @@ SELECT * FROM RLTEST CONNECT BY NOCYCLE PRIOR B=A AND (MOD(ROWNUM+1,2) = 0); SELECT * FROM RLTEST CONNECT BY PRIOR B=A OR (LEVEL < 1 OR ROWNUM < 2); SELECT * FROM RLTEST CONNECT BY PRIOR B=A AND (LEVEL=1 OR B<10) AND (ROWNUM<3 OR PRIOR A=B); SELECT * FROM RLTEST CONNECT BY PRIOR B=A OR (MOD(ROWNUM+1,2) = 0); + +-- test for multi table join used with pseudo col in where clause +delete from rltest; +INSERT INTO RLTEST VALUES('1','2'),('2','3'),('3','1'); +explain select * from rltest r1,rltest r2 where r1.a<=connect_by_isleaf+level+connect_by_iscycle+connect_by_root(r1.a) start with r1.a=1 connect by nocycle prior r2.b=r1.a; +select * from rltest r1,rltest r2 where r1.a<=connect_by_isleaf+level+connect_by_iscycle+connect_by_root(r1.a) start with r1.a=1 connect by nocycle prior r2.b=r1.a; + +-- test cross reference in start with nested query +create table test_t1(a1 int,b1 int); +create table test_t2(a2 int,b2 int); +create table test_t3(a3 int,b3 int); +insert into test_t1 values(1,2),(2,3),(3,1); +insert into test_t2 values(1,2),(2,3),(3,1); +insert into test_t3 values(1,2),(2,3),(3,1); +explain select * from test_t1 left join test_t2 on a1=a2 where exists (select 1 from test_t3 where a1+a2 is not null connect by level<2) connect by level<2; +drop table test_t1; +drop table test_t2; +drop table test_t3; + +-- test sys funcs in multi table join +select sys_connect_by_path(r1.a,'->') from rltest r1, rltest r2 where r1.a=r2.a connect by level<2; +select connect_by_root(r1.a) from rltest r1, rltest r2 where r1.a=r2.a connect by level<2; +-- test for where push down +create table sin_col_tbl(a int); +SELECT 1 +FROM sin_col_tbl, + rltest +WHERE (sin_col_tbl.a+rltest.b) IS NOT NULL +START WITH 1=1 +CONNECT BY level<2 and prior rltest.b=rltest.a; +drop table sin_col_tbl; DROP TABLE RLTEST; create table nocycle_tbl(id int, lid int, name text); insert into nocycle_tbl values (1,3,'A'),(2,1,'B'),(3,2,'C'),(4,2,'D'),(5,3,'E'); @@ -817,6 +848,7 @@ drop table if exists sw_test; drop table if exists sw_tb_1; create table sw_tb_1(a int,b int,c int,d int); create table sw_tb_2(a int,b int,c int,d int); +create table sw_tb_3(a int, b int, c int, d int); insert into sw_tb_1 values(1,1,1,1); insert into sw_tb_1 values(2,2,2,2); insert into sw_tb_1 values(3,3,3,3); @@ -830,5 +862,7 @@ select * from sw_tb_1,sw_tb_2 where (sw_tb_1.a=sw_tb_2.b or sw_tb_1.a not in (se select * from sw_tb_1,sw_tb_2 where sw_tb_1.a !=3 or sw_tb_1.c=sw_tb_2.d start with sw_tb_1.a>2 connect by nocycle prior sw_tb_1.d=sw_tb_2.c; select * from sw_tb_1,sw_tb_2 where (sw_tb_1.a+sw_tb_1.b=sw_tb_2.b or sw_tb_1.a=sw_tb_2.c) and (sw_tb_1.b=sw_tb_2.a or (substr(sw_tb_1.b,2)=substr(sw_tb_2.b,2) and sw_tb_1.b is null)) or (sw_tb_1.c=sw_tb_2.d or sw_tb_1.b!=2) start with sw_tb_1.a=2 connect by nocycle prior sw_tb_1.d=sw_tb_2.c; explain select * from sw_tb_1,sw_tb_2 where (sw_tb_1.a+sw_tb_1.b=sw_tb_2.b or sw_tb_1.a=sw_tb_2.c) and (sw_tb_1.b=sw_tb_2.a or (substr(sw_tb_1.b,2)=substr(sw_tb_2.b,2) and sw_tb_1.b is null)) or (sw_tb_1.c=sw_tb_2.d or sw_tb_1.b!=2) start with sw_tb_1.a=2 connect by nocycle prior sw_tb_1.d=sw_tb_2.c; +explain select * from sw_tb_3 where exists (select * from sw_tb_1, sw_tb_2 where sw_tb_1.a + sw_tb_2.a = sw_tb_3.a connect by level < 2); drop table sw_tb_1; -drop table sw_tb_2; \ No newline at end of file +drop table sw_tb_2; +drop table sw_tb_3; \ No newline at end of file