!2447 修复connect by子句中出现rownum时,判断条件损坏的问题

Merge pull request !2447 from 李秦朗/huihe
This commit is contained in:
opengauss-bot
2022-11-26 01:14:47 +00:00
committed by Gitee
7 changed files with 453 additions and 115 deletions

View File

@ -69,6 +69,8 @@ typedef struct StartWithTransformContext {
Node *connectByExpr;
Node *whereClause;
List *siblingsOrderBy;
List *rownum_or_level_list;
List *normal_list;
bool is_where;
StartWithConnectByType connect_by_type;
@ -858,6 +860,215 @@ static bool pseudo_level_rownum_walker(Node *node, Node *context_parent_node)
return raw_expression_tree_walker(node, (bool (*)()) pseudo_level_rownum_walker, node);
}
/*
* --------------------------------------------------------------------------------------
* @Brief: check is there any rownum/level/prior in expressions, if given expression
* containing prior and rownum/level simultaneously, then error it out.
* --------------------------------------------------------------------------------------
*/
static void rowNumOrLevelWalker(Node *expr, bool *hasRownumOrLevel, bool *hasPrior)
{
if (expr == NULL || (*hasRownumOrLevel && *hasPrior)) {
return;
}
switch (nodeTag(expr)) {
case T_ColumnRef: {
ColumnRef* colRef = (ColumnRef*)expr;
*hasRownumOrLevel = is_cref_by_name((Node *)colRef, "level") ||
*hasRownumOrLevel;
*hasPrior = colRef->prior || *hasPrior;
break;
}
case T_Rownum: {
*hasRownumOrLevel = true;
break;
}
case T_NullTest: {
NullTest* nullTestExpr = (NullTest*)expr;
Node* arg = (Node *)nullTestExpr->arg;
rowNumOrLevelWalker(arg, hasRownumOrLevel, hasPrior);
break;
}
case T_SubLink: {
SubLink *sublink = (SubLink *)expr;
Node *testexpr = sublink->testexpr;
rowNumOrLevelWalker(testexpr, hasRownumOrLevel, hasPrior);
break;
}
case T_CoalesceExpr: {
Node* node = (Node *)(((CoalesceExpr*)expr)->args);
rowNumOrLevelWalker(node, hasRownumOrLevel, hasPrior);
break;
}
case T_CollateClause: {
CollateClause *cc = (CollateClause*) expr;
rowNumOrLevelWalker(cc->arg, hasRownumOrLevel, hasPrior);
break;
}
case T_TypeCast: {
TypeCast* tc = (TypeCast*)expr;
rowNumOrLevelWalker(tc->arg, hasRownumOrLevel, hasPrior);
break;
}
case T_List: {
List* l = (List*)expr;
ListCell* lc = NULL;
foreach (lc, l) {
rowNumOrLevelWalker((Node*)lfirst(lc), hasRownumOrLevel, hasPrior);
}
break;
}
case T_FuncCall: {
ListCell *args = NULL;
FuncCall* fn = (FuncCall *)expr;
foreach (args, fn->args) {
rowNumOrLevelWalker((Node*)lfirst(args), hasRownumOrLevel, hasPrior);
}
break;
}
case T_A_Indirection: {
A_Indirection* idn = (A_Indirection *)expr;
rowNumOrLevelWalker(idn->arg, hasRownumOrLevel, hasPrior);
break;
}
case T_A_Expr: {
A_Expr *a_expr = (A_Expr *)expr;
Node *left_expr = a_expr->lexpr;
Node *right_expr = a_expr->rexpr;
switch (a_expr->kind) {
case AEXPR_OP:
case AEXPR_AND:
case AEXPR_OR:
case AEXPR_IN: {
rowNumOrLevelWalker(left_expr, hasRownumOrLevel, hasPrior);
rowNumOrLevelWalker(right_expr, hasRownumOrLevel, hasPrior);
break;
}
case AEXPR_NOT: {
rowNumOrLevelWalker(right_expr, hasRownumOrLevel, hasPrior);
break;
}
default: {
ereport(ERROR,
(errmodule(MOD_PARSER), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Unsupported expression found in START WITH / CONNECT BY "
"clause during rowNumOrLevelWalker."),
errdetail("Unsupported expr type: %d.", (int)a_expr->kind),
errcause("Unsupported expression in START WITH / CONNECT BY clause."),
erraction("Check and revise your query or contact Huawei engineers.")));
break;
}
}
break;
}
case T_A_Const: {
A_Const *n = (A_Const *)expr;
if (n->val.type == T_Integer) {
long val = n->val.val.ival;
if (val == CONNECT_BY_ROWNUM_FAKEVALUE || val == CONNECT_BY_LEVEL_FAKEVALUE) {
*hasRownumOrLevel = true;
}
}
break;
}
case T_A_ArrayExpr:
case T_ParamRef: {
break;
}
default: {
ereport(ERROR,
(errmodule(MOD_PARSER), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Unsupported expression found in START WITH / CONNECT BY "
"clause during rowNumOrLevelWalker."),
errdetail("Unsupported node type: %d.", (int)nodeTag(expr)),
errcause("Unsupported expression in START WITH / CONNECT BY clause."),
erraction("Check and revise your query or contact Huawei engineers.")));
break;
}
}
return;
}
/*
* --------------------------------------------------------------------------------------
* @Brief: flatten connectByExpr into two lists
* @Param:
* - rownum_or_level_list: the exprs in this list contains "level" or "rownum"
* - normal_list: the exprs in this list = connectByExpr - rownum_or_level_list
* - expr
* --------------------------------------------------------------------------------------
*/
static void flattenConnectByExpr(StartWithTransformContext *context, Node *expr)
{
if (expr == NULL) {
return;
}
bool hasRownumOrLevel = false;
bool hasPrior = false;
if (nodeTag(expr) == T_A_Expr) {
A_Expr *a_expr = (A_Expr *)expr;
if (a_expr->kind == AEXPR_AND) {
Node *lexpr = a_expr->lexpr;
Node *rexpr = a_expr->rexpr;
flattenConnectByExpr(context, lexpr);
flattenConnectByExpr(context, rexpr);
return;
}
}
rowNumOrLevelWalker(expr, &hasRownumOrLevel, &hasPrior);
if (hasRownumOrLevel && hasPrior) {
ereport(ERROR, (errmodule(MOD_PARSER), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("column specified by prior cannot concide with ROWNUM/LEVEL."),
errdetail("Unsupported node type: %d.", (int)nodeTag(expr)),
errcause("Unsupported expression in START WITH / CONNECT BY clause."),
erraction("Check and revise your query or contact Huawei engineers.")));
}
if (hasRownumOrLevel) {
context->rownum_or_level_list = lappend(context->rownum_or_level_list, expr);
} else {
context->normal_list = lappend(context->normal_list, expr);
}
}
/*
* --------------------------------------------------------------------------------------
* @Brief: build a internal expr from given list using "AND"
* @Return: the final expr
* --------------------------------------------------------------------------------------
*/
static Node *buildExprUsingAnd(ListCell *tempCell)
{
if (tempCell == NULL) {
return NULL;
}
Node *nowExpr = (Node *)lfirst(tempCell);
Node *nextExpr = buildExprUsingAnd(lnext(tempCell));
if (nextExpr != NULL) {
return (Node *)makeA_Expr(AEXPR_AND, NULL, nowExpr, nextExpr, -1);
} else {
return nowExpr;
}
}
/*
* --------------------------------------------------------------------------------------
* @Brief: SWCB's expr processing function, normally we do tow kinds of process
@ -968,17 +1179,12 @@ static void StartWithWalker(StartWithTransformContext *context, Node *expr)
if (left_expr != NULL && (is_cref_by_name(left_expr, "level") ||
IsA(left_expr, Rownum))) {
context->connect_by_type = CONNECT_BY_MIXED_LEVEL;
context->connectByLevelExpr = (Node *)copyObject(a_expr);
a_expr->kind = AEXPR_OR;
a_expr->lexpr = makeBoolAConst(true, -1);
a_expr->rexpr = makeBoolAConst(true, -1);
A_Expr* cble = (A_Expr*) context->connectByLevelExpr;
A_Const *n = makeNode(A_Const);
n->val.type = T_Integer;
n->val.val.ival = IsA(left_expr, Rownum) ?
CONNECT_BY_ROWNUM_FAKEVALUE : CONNECT_BY_LEVEL_FAKEVALUE;
n->location = -1;
cble->lexpr = (Node*) n;
a_expr->lexpr = (Node*) n;
break;
}
@ -1126,24 +1332,20 @@ static void transformStartWithClause(StartWithTransformContext *context, SelectS
context->nocycle = clause->nocycle;
context->siblingsOrderBy = (List *)clause->siblingsOrderBy;
/* Handling CONNECT BY ROWNUM / LEVEL */
if (startWithExpr == NULL) {
raw_expression_tree_walker((Node*)context->connectByExpr,
(bool (*)())pseudo_level_rownum_walker, (Node*)context->connectByExpr);
checkConnectByExprValidity((Node*)connectByExpr);
StartWithWalker(context, connectByExpr);
context->relInfoList = context->pstate->p_start_info;
if (context->connect_by_type != CONNECT_BY_PRIOR) {
context->connectByLevelExpr = connectByExpr;
context->connectByOtherExpr = NULL;
}
}
/* when ROWNUM or LEVEL appear in Expressions other than A_Expr, do an extra replacement */
raw_expression_tree_walker((Node*)context->connectByExpr,
(bool (*)())pseudo_level_rownum_walker, (Node*)context->connectByExpr);
checkConnectByExprValidity((Node*)connectByExpr);
context->relInfoList = context->pstate->p_start_info;
flattenConnectByExpr(context, connectByExpr);
context->connectByLevelExpr = buildExprUsingAnd(list_head(context->rownum_or_level_list));
context->connectByExpr = buildExprUsingAnd(list_head(context->normal_list));
/* transform start with ... connect by's expr */
StartWithWalker(context, startWithExpr);
if (startWithExpr != NULL) {
StartWithWalker(context, connectByExpr);
}
StartWithWalker(context, context->connectByLevelExpr);
StartWithWalker(context, context->connectByExpr);
/*
* now handle where quals which might need to be pushed down.
@ -1165,7 +1367,6 @@ static void transformStartWithClause(StartWithTransformContext *context, SelectS
context->whereClause = whereClause;
}
if (startWithExpr != NULL && context->connectby_prior_name == NULL) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@ -1434,49 +1635,21 @@ static SelectStmt *CreateStartWithCTEInnerBranch(ParseState* pstate,
/* process regular/level */
switch (context->connect_by_type) {
case CONNECT_BY_PRIOR: {
case CONNECT_BY_PRIOR:
case CONNECT_BY_ROWNUM:
case CONNECT_BY_LEVEL:
case CONNECT_BY_MIXED_LEVEL: {
join->jointype = JOIN_INNER;
join->isNatural = FALSE;
join->larg = (Node *)work_table;
join->rarg = origin_table;
join->usingClause = NIL;
join->quals = (Node *)copyObject(connectByExpr);
result->targetList = expandAllTargetList(relInfoList);
result->fromClause = list_make1(join);
break;
}
case CONNECT_BY_ROWNUM:
case CONNECT_BY_LEVEL: {
join->jointype = JOIN_INNER;
join->isNatural = TRUE;
join->larg = (Node *)work_table;
join->rarg = origin_table;
join->usingClause = NIL;
/* for connect-by-level we set joinquals to TRUE as a netural join */
join->quals = makeBoolAConst(true, -1);
result->targetList = expandAllTargetList(relInfoList);
result->fromClause = list_make1(join);
result->whereClause = (Node *)context->connectByOtherExpr;
break;
}
case CONNECT_BY_MIXED_LEVEL: {
join->jointype = JOIN_INNER;
join->isNatural = TRUE;
join->larg = (Node *)work_table;
join->rarg = origin_table;
join->usingClause = NIL;
/* for MIXED connect-by-level we keep the original quals except for the level part */
join->quals = (Node *)copyObject(connectByExpr);
result->targetList = expandAllTargetList(relInfoList);
result->fromClause = list_make1(join);
result->whereClause = (Node *)context->connectByOtherExpr;
break;
}
default: {
elog(ERROR, "unrecognized connect by type %d", context->connect_by_type);
}
@ -1530,7 +1703,6 @@ static SelectStmt *CreateStartWithCTEOuterBranch(ParseState *pstate,
/* push whereClause down to init part, taking care to avoid NULL in expr. */
quals = (Node *)startWithExpr;
Node* whereClauseCopy = (Node *)copyObject(whereClause);
if (whereClause != NULL) {
/* only join quals can be pushed down */
raw_expression_tree_walker((Node*)whereClauseCopy,
@ -1710,7 +1882,7 @@ static void transformFromList(ParseState* pstate, Query* qry,
List *relInfoList = context->relInfoList;
Node *whereClause = context->whereClause;
/* make union-all branch for none- recursive part */
/* make union-all branch for none-recursive part */
SelectStmt *outerBranch = CreateStartWithCTEOuterBranch(pstate, context,
relInfoList, (Node *)startWithExpr, whereClause);

View File

@ -205,7 +205,7 @@ TupleTableSlot* ExecRecursiveUnion(RecursiveUnionState* node)
* For START WITH CONNECT BY, create converted tuple with pseudo columns.
*/
slot = isSW ? ConvertRuScanOutputSlot(node, slot, false) : slot;
swSlot = isSW ? GetStartWithSlot(node, slot) : NULL;
swSlot = isSW ? GetStartWithSlot(node, slot, false) : NULL;
if (isSW && swSlot == NULL) {
/* Not satisfy connect_by_level_qual,skip this tuple */
continue;
@ -379,14 +379,24 @@ TupleTableSlot* ExecRecursiveUnion(RecursiveUnionState* node)
* avoid order siblings by exist.
* */
if (node->iteration > max_times) {
ereport(ERROR,
(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
errmsg("Current Start With...Connect by has exceeded max iteration times %d", max_times),
errhint("Please check your connect by clause carefully")));
/* if connectByLevelQual can't offer a limited results, declare a cycle exception
* and suggest user add NOCYCLE into CONNECT BY clause.
*/
if (IsConnectByLevelStartWithPlan(swplan)) {
ereport(ERROR,
(errmodule(MOD_EXECUTOR),
errmsg("START WITH .. CONNECT BY statement runs into cycle exception because of bad"
" condition for evaluation given in CONNECT BY clause")));
} else {
ereport(ERROR,
(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
errmsg("Current Start With...Connect by has exceeded max iteration times %d", max_times),
errhint("Please check your connect by clause carefully")));
}
}
slot = ConvertRuScanOutputSlot(node, slot, true);
swSlot = GetStartWithSlot(node, slot);
swSlot = GetStartWithSlot(node, slot, true);
if (isSW && swSlot == NULL) {
/* Not satisfy connect_by_level_qual,skip this tuple */
continue;

View File

@ -32,8 +32,7 @@ typedef enum StartWithOpExecStatus {
SWOP_UNKNOWN = 0,
SWOP_BUILD = 1,
SWOP_EXECUTE,
SWOP_FINISH,
SWOP_ESCAPE
SWOP_FINISH
} StartWithOpExecStatus;
#define KEY_START_TAG "{"
@ -372,6 +371,7 @@ bool CheckCycleExeception(StartWithOpState *node, TupleTableSlot *slot)
{
RecursiveUnionState *rustate = NULL;
StartWithOp *swplan = (StartWithOp *)node->ps.plan;
bool nocycle = swplan->swoptions->nocycle;
bool incycle = false;
if (swplan->swoptions->siblings_orderby_clause != NULL) {
@ -383,7 +383,7 @@ bool CheckCycleExeception(StartWithOpState *node, TupleTableSlot *slot)
Assert (IsA(rustate, RecursiveUnionState));
if (IsConnectByLevelStartWithPlan(swplan) || node->sw_keyAttnum == 0) {
if ((!nocycle && IsConnectByLevelStartWithPlan(swplan)) || node->sw_keyAttnum == 0) {
/* for connect by level case, we do not do cycle check */
return false;
}
@ -397,7 +397,7 @@ bool CheckCycleExeception(StartWithOpState *node, TupleTableSlot *slot)
CheckIsCycle(node, &incycle);
/* compatible with ORA behavior, if NOCYCLE is not set, we report error */
if (!swplan->swoptions->nocycle && incycle) {
if (!nocycle && incycle) {
ereport(ERROR,
(errmodule(MOD_EXECUTOR),
errmsg("START WITH .. CONNECT BY statement runs into cycle exception")));
@ -473,7 +473,7 @@ static bool depth_first_connect(int currentLevel, StartWithOpState *node, List*
/* loop until all siblings' DFS are done */
for (;;) {
if (queue->head == NULL || node->swop_status == SWOP_ESCAPE) {
if (queue->head == NULL) {
return isCycle;
}
TupleTableSlot* leader = (TupleTableSlot*) lfirst(queue->head);
@ -558,7 +558,7 @@ void markSWLevelEnd(StartWithOpState *node, int64 rowCount)
* @Function: ExecStartWithRowLevelQual()
*
* @Brief:
* Check if LEVEL/ROWNUM conditions still hold for the recursion to
* Check if SWCB's conditions still hold for the recursion to
* continue. Level and rownum should have been made available
* in dstSlot by ConvertStartWithOpOutputSlot() already.
*
@ -570,12 +570,11 @@ void markSWLevelEnd(StartWithOpState *node, int64 rowCount)
bool ExecStartWithRowLevelQual(RecursiveUnionState* node, TupleTableSlot* dstSlot)
{
StartWithOp* swplan = (StartWithOp*)node->swstate->ps.plan;
ExprContext* expr = node->swstate->ps.ps_ExprContext;
if (!IsConnectByLevelStartWithPlan(swplan)) {
return true;
}
ExprContext* expr = node->swstate->ps.ps_ExprContext;
/*
* Level and rownum pseudo attributes are extracted from StartWithOpPlan
* node so we set filtering tuple as ecxt_scantuple
@ -587,51 +586,18 @@ bool ExecStartWithRowLevelQual(RecursiveUnionState* node, TupleTableSlot* dstSlo
return true;
}
static bool isStoppedByRowNum(RecursiveUnionState* node, TupleTableSlot* slot)
{
TupleTableSlot* dstSlot = node->swstate->ps.ps_ResultTupleSlot;
bool ret = false;
/* 1. roll back to the converted result row */
node->swstate->sw_rownum--;
/* 2. roll back to one row before execution to check rownum stop condition */
node->swstate->sw_rownum--;
dstSlot = ConvertStartWithOpOutputSlot(node->swstate, slot, dstSlot);
if (ExecStartWithRowLevelQual(node, dstSlot)) {
ret = true;
}
/* undo the rollback after conversion (yes, just one line) */
node->swstate->sw_rownum++;
return ret;
}
static void DiscardSWLastTuple(StartWithOpState *node)
{
node->sw_rownum--;
node->sw_numtuples--;
}
TupleTableSlot* GetStartWithSlot(RecursiveUnionState* node, TupleTableSlot* slot)
TupleTableSlot* GetStartWithSlot(RecursiveUnionState* node, TupleTableSlot* slot, bool isRecursive)
{
TupleTableSlot* dstSlot = node->swstate->ps.ps_ResultTupleSlot;
dstSlot = ConvertStartWithOpOutputSlot(node->swstate, slot, dstSlot);
if (!ExecStartWithRowLevelQual(node, dstSlot)) {
StartWithOpState *swnode = node->swstate;
StartWithOp *swplan = (StartWithOp *)swnode->ps.plan;
PlanState *outerNode = outerPlanState(swnode);
bool isDfsEnabled = swplan->swoptions->nocycle && !IsA(outerNode, SortState);
/*
* ROWNUM/LEVEL limit reached:
* Tell ExecRecursiveUnion to terminate the recursion by returning NULL
*
* Specifically for ROWNUM limit reached:
* Tell DFS routine to stop immediately by setting SW status to ESCAPE.
*/
node->swstate->swop_status = isDfsEnabled && isStoppedByRowNum(node, slot) ?
SWOP_ESCAPE :
node->swstate->swop_status;
if (isRecursive && !ExecStartWithRowLevelQual(node, dstSlot)) {
DiscardSWLastTuple(node->swstate);
return NULL;
}

View File

@ -57,6 +57,6 @@ extern int SibglingsKeyCmpFast(Datum x, Datum y, SortSupport ssup);
extern void markSWLevelBegin(StartWithOpState *node);
extern void markSWLevelEnd(StartWithOpState *node, int64 rowCount);
extern TupleTableSlot* GetStartWithSlot(RecursiveUnionState* node, TupleTableSlot* slot);
extern TupleTableSlot* GetStartWithSlot(RecursiveUnionState* node, TupleTableSlot* slot, bool isRecursive);
extern bool ExecStartWithRowLevelQual(RecursiveUnionState* node, TupleTableSlot* dstSlot);
#endif /* NODECTESCAN_H */

View File

@ -1496,7 +1496,9 @@ SELECT start AS connect, prior AS start FROM CONNECT CONNECT CONNECT BY ROWNUM <
1 | 3
3 | 4
3 | 5
(4 rows)
5 | 6
6 | 7
(6 rows)
DROP TABLE IF EXISTS start;
DROP TABLE IF EXISTS connect;
@ -1575,9 +1577,10 @@ ERROR: Invalid column reference in START WITH / CONNECT BY clause.
DETAIL: The column referred to may not exist.
--test connectby level bug
select * from (select 'test111' col from sys_dummy) connect by rownum < length(translate(col, '$' || col, '$'));
col
-----
(0 rows)
col
---------
test111
(1 row)
--test find siblings target name bug
select test1.a, cast (min(1) OVER (PARTITION BY test1.a ORDER BY test1.b) as integer) from test1 where test1.b is NULL connect by exists(select test2.id from test2 where false limit 40) order siblings by test1.ctid;

View File

@ -245,7 +245,51 @@ CONNECT BY PRIOR brand_cd=UOM_CD;
SELECT 1, level FROM t1_area CONNECT BY length(level) IS NULL;
?column? | level
----------+-------
(0 rows)
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
1 | 1
(44 rows)
/* prior params of procedure */
create or replace function test_tmp1(out id int,out pid int,out name varchar,out level int) return SETOF RECORD
@ -879,8 +923,7 @@ select count(*) from tt22;
set max_recursive_times=200;
insert into tt22 select level from dual connect by level <=1000;
ERROR: Current Start With...Connect by has exceeded max iteration times 200
HINT: Please check your connect by clause carefully
ERROR: START WITH .. CONNECT BY statement runs into cycle exception because of bad condition for evaluation given in CONNECT BY clause
drop table tt22;
/* 修复RecursiveUnion的inner分支备planning成BaseResult节点 */
explain select t1.id,t1.pid,t1.name from test_hcb_ptb t1 start with id=141 connect by (prior pid)=id and prior pid>10 and 1=0;
@ -1393,7 +1436,47 @@ SELECT id,pid,name,rownum,level FROM test_hcb_ptb START WITH id=1 CONNECT BY NOC
121 | 111 | 江宁区 | 4 | 4
131 | 121 | 东山街 | 5 | 5
141 | 131 | 江南摩卡 | 6 | 6
(6 rows)
142 | 131 | 四季云顶 | 7 | 6
143 | 131 | 盛世江南 | 8 | 6
144 | 131 | 七里香都 | 9 | 6
145 | 131 | 西山枫林 | 10 | 6
146 | 131 | 醉墨小镇 | 11 | 6
147 | 131 | 布拉格调 | 12 | 6
148 | 131 | 清幽别院 | 13 | 6
149 | 131 | 璀璨天城 | 14 | 6
132 | 121 | 秣陵街 | 15 | 5
133 | 121 | 汤山街 | 16 | 5
135 | 121 | 禄口街 | 17 | 5
134 | 121 | 淳化街 | 18 | 5
136 | 121 | 江宁街 | 19 | 5
137 | 121 | 谷里街 | 20 | 5
138 | 121 | 湖熟街 | 21 | 5
139 | 121 | 横溪街 | 22 | 5
122 | 111 | 雨花台 | 23 | 4
123 | 111 | 鼓楼区 | 24 | 4
124 | 111 | 玄武区 | 25 | 4
125 | 111 | 建邺区 | 26 | 4
126 | 111 | 秦淮区 | 27 | 4
127 | 111 | 浦口区 | 28 | 4
128 | 111 | 浦口区 | 29 | 4
129 | 111 | 六合区 | 30 | 4
112 | 11 | 宿迁市 | 31 | 3
113 | 11 | 徐州市 | 32 | 3
114 | 11 | 苏州市 | 33 | 3
115 | 11 | 盐城市 | 34 | 3
117 | 11 | 常州市 | 35 | 3
116 | 11 | 无锡市 | 36 | 3
118 | 11 | 连云港 | 37 | 3
119 | 11 | 泰州市 | 38 | 3
12 | 1 | 山东省 | 39 | 2
13 | 1 | 安徽省 | 40 | 2
14 | 1 | 河南省 | 41 | 2
15 | 1 | 河北省 | 42 | 2
16 | 1 | 湖南省 | 43 | 2
17 | 1 | 湖北省 | 44 | 2
18 | 1 | 贵州省 | 45 | 2
19 | 1 | 武汉省 | 46 | 2
(46 rows)
--test subquery pushdown
SELECT subq_0.c1 as c0
@ -1733,3 +1816,88 @@ select c1,c2 from dts_t1 start with c1=1 connect by prior c2+1=c2 ;
(2 rows)
drop table dts_t1;
-- test rownum/level appear in connect by clause
DROP TABLE IF EXISTS RLTEST;
CREATE TABLE RLTEST(
A CHAR(1),
B CHAR(1)
);
INSERT INTO RLTEST VALUES('1','2'),('2','3'),('3','1'),('4','5'),('5','6'),('7','8');
SELECT * FROM RLTEST START WITH A=1 CONNECT BY PRIOR B=A AND (1=1 OR ROWNUM=1);
ERROR: START WITH .. CONNECT BY statement runs into cycle exception because of bad condition for evaluation given in CONNECT BY clause
SELECT * FROM RLTEST START WITH A=1 CONNECT BY NOCYCLE PRIOR B=A AND (1=1 OR ROWNUM=1);
a | b
---+---
1 | 2
2 | 3
3 | 1
(3 rows)
SELECT * FROM RLTEST CONNECT BY (PRIOR a = b) AND (LEVEL < 2) AND (ROWNUM < 2);
a | b
---+---
1 | 2
2 | 3
3 | 1
4 | 5
5 | 6
7 | 8
(6 rows)
SELECT * FROM RLTEST CONNECT BY (PRIOR a = b) AND (LEVEL < 2 OR ROWNUM < 2);
a | b
---+---
1 | 2
2 | 3
3 | 1
4 | 5
5 | 6
7 | 8
(6 rows)
SELECT * FROM RLTEST CONNECT BY (LEVEL < 1 OR ROWNUM < 2);
a | b
---+---
1 | 2
2 | 3
3 | 1
4 | 5
5 | 6
7 | 8
(6 rows)
SELECT * FROM RLTEST CONNECT BY PRIOR B=A AND ROWNUM = LENGTH(LEVEL);
a | b
---+---
1 | 2
2 | 3
3 | 1
4 | 5
5 | 6
7 | 8
(6 rows)
SELECT * FROM RLTEST CONNECT BY NOCYCLE PRIOR B=A AND (MOD(ROWNUM+1,2) = 0);
a | b
---+---
1 | 2
2 | 3
3 | 1
3 | 1
1 | 2
4 | 5
5 | 6
5 | 6
7 | 8
(9 rows)
SELECT * FROM RLTEST CONNECT BY PRIOR B=A OR (LEVEL < 1 OR ROWNUM < 2);
ERROR: column specified by prior cannot concide with ROWNUM/LEVEL.
DETAIL: Unsupported node type: 900.
SELECT * FROM RLTEST CONNECT BY PRIOR B=A AND (LEVEL=1 OR B<10) AND (ROWNUM<3 OR PRIOR A=B);
ERROR: column specified by prior cannot concide with ROWNUM/LEVEL.
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.
DROP TABLE RLTEST;

View File

@ -623,3 +623,22 @@ insert into dts_t1 values(2,2,2);
select c1,prior c2,c2 from dts_t1 start with c1=1 connect by prior c2+1=c2 ;
select c1,c2 from dts_t1 start with c1=1 connect by prior c2+1=c2 ;
drop table dts_t1;
-- test rownum/level appear in connect by clause
DROP TABLE IF EXISTS RLTEST;
CREATE TABLE RLTEST(
A CHAR(1),
B CHAR(1)
);
INSERT INTO RLTEST VALUES('1','2'),('2','3'),('3','1'),('4','5'),('5','6'),('7','8');
SELECT * FROM RLTEST START WITH A=1 CONNECT BY PRIOR B=A AND (1=1 OR ROWNUM=1);
SELECT * FROM RLTEST START WITH A=1 CONNECT BY NOCYCLE PRIOR B=A AND (1=1 OR ROWNUM=1);
SELECT * FROM RLTEST CONNECT BY (PRIOR a = b) AND (LEVEL < 2) AND (ROWNUM < 2);
SELECT * FROM RLTEST CONNECT BY (PRIOR a = b) AND (LEVEL < 2 OR ROWNUM < 2);
SELECT * FROM RLTEST CONNECT BY (LEVEL < 1 OR ROWNUM < 2);
SELECT * FROM RLTEST CONNECT BY PRIOR B=A AND ROWNUM = LENGTH(LEVEL);
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);
DROP TABLE RLTEST;