From d2e10a6b557bc4f08bc0b69255d78c7fe3733ef4 Mon Sep 17 00:00:00 2001 From: wangfeihuo Date: Sat, 26 Oct 2024 14:11:42 +0800 Subject: [PATCH] support cross apply --- contrib/postgres_fdw/postgres_fdw.cpp | 2 +- src/common/backend/nodes/copyfuncs.cpp | 10 +- src/common/backend/nodes/equalfuncs.cpp | 31 +- src/common/backend/nodes/outfuncs.cpp | 15 +- src/common/backend/nodes/readfuncs.cpp | 3 + src/common/backend/parser/analyze.cpp | 8 +- src/common/backend/parser/gram.y | 80 +- src/common/backend/parser/parse_expr.cpp | 7 +- src/common/backend/parser/parser.cpp | 26 +- src/common/backend/utils/adt/ruleutils.cpp | 17 +- src/common/backend/utils/init/globals.cpp | 3 +- .../interfaces/libpq/frontend_parser/gram.y | 6 +- src/gausskernel/optimizer/path/allpaths.cpp | 2 +- src/gausskernel/optimizer/path/indxpath.cpp | 50 +- src/gausskernel/optimizer/path/joinpath.cpp | 107 +- src/gausskernel/optimizer/path/joinrels.cpp | 10 +- .../optimizer/plan/analyzejoins.cpp | 43 +- src/gausskernel/optimizer/plan/createplan.cpp | 81 +- src/gausskernel/optimizer/plan/initsplan.cpp | 314 +- src/gausskernel/optimizer/plan/planmain.cpp | 12 +- src/gausskernel/optimizer/plan/planner.cpp | 2 +- .../optimizer/prep/prepjointree.cpp | 54 +- src/gausskernel/optimizer/util/nodegroups.cpp | 2 +- src/gausskernel/optimizer/util/orclauses.cpp | 2 +- .../optimizer/util/placeholder.cpp | 190 +- src/gausskernel/optimizer/util/relnode.cpp | 2 + .../optimizer/util/restrictinfo.cpp | 22 +- src/gausskernel/optimizer/util/var.cpp | 27 +- src/gausskernel/runtime/executor/execQual.cpp | 10 +- src/include/miscadmin.h | 1 + src/include/nodes/pg_list.h | 15 + src/include/nodes/primnodes.h | 1 + src/include/nodes/relation.h | 13 +- src/include/optimizer/planmain.h | 4 +- src/include/optimizer/restrictinfo.h | 2 +- src/include/parser/kwlist.h | 2 + .../expected/sytcomp_del_upt4orderby.out | 6 +- src/test/regress/input/lateral.source | 614 ++++ src/test/regress/input/lateral_dump.source | 132 + .../regress/input/lateral_with_dop.source | 619 ++++ src/test/regress/output/lateral.source | 2664 ++++++++++++++++ src/test/regress/output/lateral_dump.source | 220 ++ .../regress/output/lateral_with_dop.source | 2669 +++++++++++++++++ src/test/regress/parallel_schedule0A | 2 + 44 files changed, 7709 insertions(+), 393 deletions(-) create mode 100644 src/test/regress/input/lateral.source create mode 100644 src/test/regress/input/lateral_dump.source create mode 100644 src/test/regress/input/lateral_with_dop.source create mode 100644 src/test/regress/output/lateral.source create mode 100644 src/test/regress/output/lateral_dump.source create mode 100644 src/test/regress/output/lateral_with_dop.source diff --git a/contrib/postgres_fdw/postgres_fdw.cpp b/contrib/postgres_fdw/postgres_fdw.cpp index 91895286f..9094c985d 100644 --- a/contrib/postgres_fdw/postgres_fdw.cpp +++ b/contrib/postgres_fdw/postgres_fdw.cpp @@ -759,7 +759,7 @@ static void postgresGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid RestrictInfo *rinfo = (RestrictInfo *)lfirst(lc); /* Check if clause can be moved to this rel */ - if (!join_clause_is_movable_to(rinfo, baserel->relid)) { + if (!join_clause_is_movable_to(rinfo, baserel)) { continue; } diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index 52506abb0..e8329235a 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -3425,6 +3425,7 @@ static JoinExpr* _copyJoinExpr(const JoinExpr* from) COPY_NODE_FIELD(alias); COPY_SCALAR_FIELD(rtindex); COPY_SCALAR_FIELD(is_straight_join); + COPY_SCALAR_FIELD(is_apply_join); return newnode; } @@ -3705,12 +3706,12 @@ static SpecialJoinInfo* _copySpecialJoinInfo(const SpecialJoinInfo* from) static LateralJoinInfo * _copyLateralJoinInfo(const LateralJoinInfo *from) { - LateralJoinInfo *newnode = makeNode(LateralJoinInfo); + LateralJoinInfo *newnode = makeNode(LateralJoinInfo); - COPY_SCALAR_FIELD(lateral_rhs); - COPY_BITMAPSET_FIELD(lateral_lhs); + COPY_BITMAPSET_FIELD(lateral_lhs); + COPY_BITMAPSET_FIELD(lateral_rhs); - return newnode; + return newnode; } /* @@ -3740,6 +3741,7 @@ static PlaceHolderInfo* _copyPlaceHolderInfo(const PlaceHolderInfo* from) COPY_SCALAR_FIELD(phid); COPY_NODE_FIELD(ph_var); COPY_BITMAPSET_FIELD(ph_eval_at); + COPY_BITMAPSET_FIELD(ph_lateral); COPY_BITMAPSET_FIELD(ph_needed); COPY_SCALAR_FIELD(ph_width); diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index 2ca1752bf..ab934c98c 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -751,6 +751,7 @@ static bool _equalJoinExpr(const JoinExpr* a, const JoinExpr* b) COMPARE_NODE_FIELD(alias); COMPARE_SCALAR_FIELD(rtindex); COMPARE_SCALAR_FIELD(is_straight_join); + COMPARE_SCALAR_FIELD(is_apply_join); return true; } @@ -832,20 +833,23 @@ static bool _equalRestrictInfo(const RestrictInfo* a, const RestrictInfo* b) static bool _equalPlaceHolderVar(const PlaceHolderVar* a, const PlaceHolderVar* b) { /* - * We intentionally do not compare phexpr. Two PlaceHolderVars with the + * We intentionally do not compare phexpr. Two PlaceHolderVars with the * same ID and levelsup should be considered equal even if the contained - * expressions have managed to mutate to different states. One way in - * which that can happen is that initplan sublinks would get replaced by - * differently-numbered Params when sublink folding is done. (The end - * result of such a situation would be some unreferenced initplans, which - * is annoying but not really a problem.) + * expressions have managed to mutate to different states. This will + * happen during final plan construction when there are nested PHVs, since + * the inner PHV will get replaced by a Param in some copies of the outer + * PHV. Another way in which it can happen is that initplan sublinks + * could get replaced by differently-numbered Params when sublink folding + * is done. (The end result of such a situation would be some + * unreferenced initplans, which is annoying but not really a problem.) On + * the same reasoning, there is no need to examine phrels. * * COMPARE_NODE_FIELD(phexpr); + * + * COMPARE_BITMAPSET_FIELD(phrels); */ - COMPARE_BITMAPSET_FIELD(phrels); COMPARE_SCALAR_FIELD(phid); COMPARE_SCALAR_FIELD(phlevelsup); - return true; } @@ -867,10 +871,9 @@ static bool _equalSpecialJoinInfo(const SpecialJoinInfo* a, const SpecialJoinInf static bool _equalLateralJoinInfo(const LateralJoinInfo *a, const LateralJoinInfo *b) { - COMPARE_SCALAR_FIELD(lateral_rhs); - COMPARE_BITMAPSET_FIELD(lateral_lhs); - - return true; + COMPARE_BITMAPSET_FIELD(lateral_lhs); + COMPARE_BITMAPSET_FIELD(lateral_rhs); + return true; } static bool _equalAppendRelInfo(const AppendRelInfo* a, const AppendRelInfo* b) @@ -888,11 +891,11 @@ static bool _equalAppendRelInfo(const AppendRelInfo* a, const AppendRelInfo* b) static bool _equalPlaceHolderInfo(const PlaceHolderInfo* a, const PlaceHolderInfo* b) { COMPARE_SCALAR_FIELD(phid); - COMPARE_NODE_FIELD(ph_var); + COMPARE_NODE_FIELD(ph_var); /* should be redundant */ COMPARE_BITMAPSET_FIELD(ph_eval_at); + COMPARE_BITMAPSET_FIELD(ph_lateral); COMPARE_BITMAPSET_FIELD(ph_needed); COMPARE_SCALAR_FIELD(ph_width); - return true; } diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index 07f1f37d9..0cb343742 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -3219,6 +3219,9 @@ static void _outJoinExpr(StringInfo str, JoinExpr* node) if (t_thrd.proc->workingVersionNum >= STRAIGHT_JOIN_VERSION_NUMBER) { WRITE_BOOL_FIELD(is_straight_join); } + if (t_thrd.proc->workingVersionNum >= APPLY_JOIN_VERSION_NUMBER) { + WRITE_BOOL_FIELD(is_apply_join); + } } static void _outFromExpr(StringInfo str, FromExpr* node) @@ -3642,6 +3645,9 @@ static void _outRelOptInfo(StringInfo str, RelOptInfo* node) WRITE_INT_FIELD(max_attr); WRITE_NODE_FIELD(lateral_vars); WRITE_BITMAPSET_FIELD(lateral_relids); + if (t_thrd.proc->workingVersionNum >= APPLY_JOIN_VERSION_NUMBER) { + WRITE_BITMAPSET_FIELD(lateral_referencers); + } WRITE_NODE_FIELD(indexlist); #ifndef ENABLE_MULTIPLE_NODES WRITE_NODE_FIELD(statlist); @@ -3866,10 +3872,10 @@ static void _outSpecialJoinInfo(StringInfo str, SpecialJoinInfo* node) static void _outLateralJoinInfo(StringInfo str, const LateralJoinInfo *node) { - WRITE_NODE_TYPE("LATERALJOININFO"); + WRITE_NODE_TYPE("LATERALJOININFO"); - WRITE_UINT_FIELD(lateral_rhs); - WRITE_BITMAPSET_FIELD(lateral_lhs); + WRITE_BITMAPSET_FIELD(lateral_lhs); + WRITE_BITMAPSET_FIELD(lateral_rhs); } static void _outAppendRelInfo(StringInfo str, AppendRelInfo* node) @@ -3894,6 +3900,9 @@ static void _outPlaceHolderInfo(StringInfo str, PlaceHolderInfo* node) WRITE_UINT_FIELD(phid); WRITE_NODE_FIELD(ph_var); WRITE_BITMAPSET_FIELD(ph_eval_at); + if (t_thrd.proc->workingVersionNum >= APPLY_JOIN_VERSION_NUMBER) { + WRITE_BITMAPSET_FIELD(ph_lateral); + } WRITE_BITMAPSET_FIELD(ph_needed); WRITE_INT_FIELD(ph_width); } diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index dd0e73b19..e6afe6163 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -3172,6 +3172,9 @@ static JoinExpr* _readJoinExpr(void) IF_EXIST(is_straight_join) { READ_BOOL_FIELD(is_straight_join); } + IF_EXIST(is_apply_join) { + READ_BOOL_FIELD(is_apply_join); + } READ_DONE(); } diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index cc87ad8dd..dde587e84 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -1337,12 +1337,14 @@ static Query* transformDeleteStmt(ParseState* pstate, DeleteStmt* stmt) if (u_sess->attr.attr_sql.sql_compatibility != B_FORMAT) { /* grab the namespace item made by setTargetTable */ nsitem = (ParseNamespaceItem *)llast(pstate->p_relnamespace); - /* subqueries in USING can see the result relation only via LATERAL */ + /* subqueries in USING cannot access the result relation */ nsitem->p_lateral_only = true; + nsitem->p_lateral_ok = false; transformFromClause(pstate, stmt->usingClause); - /* remaining clauses can see the result relation normally */ + /* remaining clauses can reference the result relation normally */ nsitem->p_lateral_only = false; + nsitem->p_lateral_ok = true; } @@ -4531,6 +4533,7 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt) foreach(l, pstate->p_varnamespace) { nsitem = (ParseNamespaceItem *)lfirst(l); nsitem->p_lateral_only = true; + nsitem->p_lateral_ok = false; } CheckUDRelations(pstate, stmt->sortClause, stmt->limitClause, stmt->returningList, false); @@ -4550,6 +4553,7 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt) } nsitem = (ParseNamespaceItem *)lfirst(l); nsitem->p_lateral_only = false; + nsitem->p_lateral_ok = true; nsitem_count--; } diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 5013d70b3..3b974a29b 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -902,7 +902,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 ARCHIVE ARRAY AS ASC + AGGREGATE ALGORITHM ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APP APPEND APPLY ARCHIVE ARRAY AS ASC 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 @@ -947,7 +947,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); KEY KILL KEY_PATH KEY_STORE - LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF LINES + LABEL LANGUAGE LARGE_P LAST_P LATERAL_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF LINES LEAST LESS LEFT LEVEL LIKE LIMIT LIST LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOG_ON LOG_P LOGGING LOGIN_ANY LOGIN_FAILURE LOGIN_SUCCESS LOGOUT LOOP MAPPING MASKING MASTER MATCH MATERIALIZED MATCHED MAXEXTENTS MAXSIZE MAXTRANS MAXVALUE MERGE MESSAGE_TEXT METHOD MINUS_P MINUTE_P MINUTE_SECOND_P MINVALUE MINEXTENTS MODE @@ -1028,6 +1028,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); NOT_IN NOT_BETWEEN NOT_LIKE NOT_ILIKE NOT_SIMILAR FORCE_INDEX USE_INDEX IGNORE_INDEX CURSOR_EXPR + LATERAL_EXPR /* Precedence: lowest to highest */ %nonassoc COMMENT @@ -1055,6 +1056,7 @@ static char* IdentResolveToChar(char *ident, core_yyscan_t yyscanner); %nonassoc INDEX %nonassoc ROTATE %nonassoc higher_than_rotate +%nonassoc LATERAL_P %left ',' /* * To support target_el without AS, we must give IDENT an explicit priority @@ -1107,7 +1109,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 +%left JOIN CROSS LEFT FULL RIGHT 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 @@ -25760,6 +25762,16 @@ 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); + n->funccallnode = $2; + n->alias = $3; + n->coldeflist = NIL; + n->lateral = true; + $$ = (Node *) n; + } | relation_expr index_hint_list { $1->indexhints = $2; @@ -26040,6 +26052,35 @@ table_ref_for_no_table_function: relation_expr %prec UMINUS n->rotate = $3; $$ = (Node *) n; } + + | LATERAL_P select_with_parens opt_alias_clause %prec LATERAL_P + { + RangeSubselect *n = makeNode(RangeSubselect); + + n->lateral = true; + n->subquery = $2; + n->alias = $3; + /* same comment as above */ + if ($3 == NULL) + { + if (IsA($2, SelectStmt) && + ((SelectStmt *) $2)->valuesLists) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES in FROM must have an alias"), + errhint("For example, FROM (VALUES ...) [AS] foo."), + parser_errposition(@2))); + } + else + { + Alias *a = makeNode(Alias); + a->aliasname = pstrdup("__unnamed_subquery__"); + n->alias = a; + } + } + $$ = (Node *) n; + } | joined_table { $$ = (Node *) $1; @@ -26136,6 +26177,38 @@ joined_table: n->quals = NULL; /* fill later */ $$ = n; } + | table_ref CROSS APPLY table_ref + { + /* CROSS JOIN is same as unqualified inner join */ + JoinExpr *n = makeNode(JoinExpr); + n->jointype = JOIN_INNER; + n->isNatural = FALSE; + n->is_apply_join = TRUE; + n->larg = $1; + n->rarg = $4; + if (IsA(n->rarg, RangeSubselect)) { + ((RangeSubselect*)n->rarg)->lateral = true; + } + n->usingClause = NIL; + n->quals = NULL; + $$ = n; + } + | table_ref OUTER_P APPLY table_ref + { + /* CROSS JOIN is same as unqualified inner join */ + JoinExpr *n = makeNode(JoinExpr); + n->jointype = JOIN_LEFT; + n->isNatural = FALSE; + n->is_apply_join = TRUE; + n->larg = $1; + n->rarg = $4; + if (IsA(n->rarg, RangeSubselect)) { + ((RangeSubselect*)n->rarg)->lateral = true; + } + n->usingClause = NIL; + n->quals = NULL; + $$ = n; + } ; alias_clause: @@ -31306,6 +31379,7 @@ unreserved_keyword: | LANGUAGE | LARGE_P | LAST_P + | LATERAL_P | LC_COLLATE_P | LC_CTYPE_P | LEAKPROOF diff --git a/src/common/backend/parser/parse_expr.cpp b/src/common/backend/parser/parse_expr.cpp index 6054582d4..c6a366a88 100644 --- a/src/common/backend/parser/parse_expr.cpp +++ b/src/common/backend/parser/parse_expr.cpp @@ -3910,6 +3910,11 @@ static Node* transformCursorOuterVarAsParam(ParseState* pstate, ColumnRef* cref, } pstate_temp = pstate_temp->parentParseState; + + if (pstate_temp == NULL || pstate_temp->is_outer_parse_state == false) { + return node; + } + List* para_var = pstate_temp->cursor_expression_para_var; pstate_temp->cursor_expression_para_var = lappend(para_var, (void*)(Var*)copyObject(node)); @@ -3979,7 +3984,7 @@ static Node* transformCursorExpression(ParseState* pstate, CursorExpression* cur nParamExec = list_length(parse_state_temp->cursor_expression_para_var); } - plan_tree->nParamExec = nParamExec; + plan_tree->nParamExec += nParamExec; newm->plan = (Node*)plan_tree; newm->options = cursor_expression->options; newm->raw_query_str = queryString; diff --git a/src/common/backend/parser/parser.cpp b/src/common/backend/parser/parser.cpp index cc4458119..277ff6e48 100644 --- a/src/common/backend/parser/parser.cpp +++ b/src/common/backend/parser/parser.cpp @@ -175,7 +175,7 @@ List* raw_parser(const char* str, List** query_string_locationlist) } while (0) -#define PARSE_CURSOR_PARENTHESES_AS_FUNCTION() \ +#define SET_LOOKAHEAD_2_TOKEN() \ do { \ yyextra->lookahead_token[1] = next_token_1; \ yyextra->lookahead_yylval[1] = core_yystype_2; \ @@ -783,7 +783,7 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) } else if (is_prefer_parse_cursor_parentheses_as_expr() && !is_cursor_function_exist()) { PARSE_CURSOR_PARENTHESES_AS_EXPR(); } else { - PARSE_CURSOR_PARENTHESES_AS_FUNCTION(); + SET_LOOKAHEAD_2_TOKEN(); } if (t_thrd.proc->workingVersionNum < CURSOR_EXPRESSION_VERSION_NUMBER && cur_token == CURSOR_EXPR) { @@ -792,6 +792,28 @@ int base_yylex(YYSTYPE* lvalp, YYLTYPE* llocp, core_yyscan_t yyscanner) } } break; + case LATERAL_P: + GET_NEXT_TOKEN(); + core_yystype_1 = cur_yylval; // the value of cursor + cur_yylloc_1 = cur_yylloc; // the lloc of cursor + next_token_1 = next_token; // the token after curosr + if (next_token_1 != IDENT) { + /* save the lookahead token for next time */ + SET_LOOKAHEAD_TOKEN(); + /* and back up the output info to cur_token */ + lvalp->core_yystype = cur_yylval; + *llocp = cur_yylloc; + } else { + GET_NEXT_TOKEN(); + core_yystype_2 = cur_yylval; // the value after cursor + cur_yylloc_2 = cur_yylloc; // the lloc after cursor + next_token_2 = next_token; // the token after after curosr + if (next_token_1 == IDENT && next_token == '(') { + cur_token = LATERAL_EXPR; + } + SET_LOOKAHEAD_2_TOKEN(); + } + break; case STATIC_P: GET_NEXT_TOKEN(); switch (next_token) { diff --git a/src/common/backend/utils/adt/ruleutils.cpp b/src/common/backend/utils/adt/ruleutils.cpp index d765e4761..3d3df9367 100644 --- a/src/common/backend/utils/adt/ruleutils.cpp +++ b/src/common/backend/utils/adt/ruleutils.cpp @@ -12171,13 +12171,22 @@ static void get_from_clause_item(Node* jtnode, Query* query, deparse_context* co } else { switch (j->jointype) { case JOIN_INNER: - if (j->quals) + if (j->quals) { appendContextKeyword(context, " JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); - else - appendContextKeyword(context, " CROSS JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 1); + } else { + if (j->is_apply_join) { + appendContextKeyword(context, " CROSS APPLY ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 1); + } else { + appendContextKeyword(context, " CROSS JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 1); + } + } break; case JOIN_LEFT: - appendContextKeyword(context, " LEFT JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); + if (j->is_apply_join) { + appendContextKeyword(context, " OUTER APPLY ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); + } else { + appendContextKeyword(context, " LEFT JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); + } break; case JOIN_FULL: appendContextKeyword(context, " FULL JOIN ", -PRETTYINDENT_JOIN, PRETTYINDENT_JOIN, 2); diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index 5f8fbba2d..f0bc8b3eb 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -77,12 +77,13 @@ bool will_shutdown = false; * ********************************************/ -const uint32 GRAND_VERSION_NUM = 93010; +const uint32 GRAND_VERSION_NUM = 93011; /******************************************** * 2.VERSION NUM FOR EACH FEATURE * Please write indescending order. ********************************************/ +const uint32 APPLY_JOIN_VERSION_NUMBER = 93011; const uint32 CHARBYTE_SEMANTIC_VERSION_NUMBER = 93001; const uint32 FLUSH_LSN_FUN_VERSION_NUM = 92951; const uint32 PUBLICATION_DDL_AT_VERSION_NUM = 92949; diff --git a/src/common/interfaces/libpq/frontend_parser/gram.y b/src/common/interfaces/libpq/frontend_parser/gram.y index 367165625..d6167d44d 100755 --- a/src/common/interfaces/libpq/frontend_parser/gram.y +++ b/src/common/interfaces/libpq/frontend_parser/gram.y @@ -512,7 +512,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 ARCHIVE ARRAY AS ASC + AGGREGATE ALGORITHM ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APP APPEND APPLY ARCHIVE ARRAY AS ASC 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 @@ -557,7 +557,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; KEY KILL KEY_PATH KEY_STORE - LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF LINES + LABEL LANGUAGE LARGE_P LAST_P LATERAL_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF LINES LEAST LESS LEFT LEVEL LIST LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOG_P LOGGING LOGIN_ANY LOGIN_SUCCESS LOGIN_FAILURE LOGOUT LOOP MAPPING MASKING MASTER MASTR MATCH MATERIALIZED MATCHED MAXEXTENTS MAXSIZE MAXTRANS MAXVALUE MERGE MESSAGE_TEXT METHOD MINUS_P MINUTE_P MINUTE_SECOND_P MINVALUE MINEXTENTS MODE MODIFY_P MONTH_P MOVE MOVEMENT @@ -11679,6 +11679,7 @@ unreserved_keyword: | ALWAYS | APP | APPEND + | APPLY | ASSERTION | ASSIGNMENT | AT @@ -11876,6 +11877,7 @@ unreserved_keyword: | LANGUAGE | LARGE_P | LAST_P + | LATERAL_P | LC_COLLATE_P | LC_CTYPE_P | LEAKPROOF diff --git a/src/gausskernel/optimizer/path/allpaths.cpp b/src/gausskernel/optimizer/path/allpaths.cpp index 7a0440710..3a9fe1d4b 100755 --- a/src/gausskernel/optimizer/path/allpaths.cpp +++ b/src/gausskernel/optimizer/path/allpaths.cpp @@ -2360,7 +2360,7 @@ static bool collect_lateral_vars_walker(Node *node, void *context) { rel->lateral_relids = bms_add_member(rel->lateral_relids, var->varno); rel->lateral_vars = lappend(rel->lateral_vars, var); - add_lateral_info(root, rel->relid, bms_make_singleton(var->varno)); + add_lateral_info(root, bms_make_singleton(var->varno), rel->relids); } return false; diff --git a/src/gausskernel/optimizer/path/indxpath.cpp b/src/gausskernel/optimizer/path/indxpath.cpp index 339101eae..389f8a17a 100755 --- a/src/gausskernel/optimizer/path/indxpath.cpp +++ b/src/gausskernel/optimizer/path/indxpath.cpp @@ -140,9 +140,9 @@ static bool check_index_only(PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* i static double get_loop_count(PlannerInfo* root, Relids outer_relids); static void match_restriction_clauses_to_index(RelOptInfo* rel, IndexOptInfo* index, IndexClauseSet* clauseset); static void match_join_clauses_to_index( - PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* index, Relids lateral_referencers, + PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* index, IndexClauseSet* clauseset, List** joinorclauses); -static void match_eclass_clauses_to_index(PlannerInfo* root, IndexOptInfo* index, Relids lateral_referencers, IndexClauseSet* clauseset); +static void match_eclass_clauses_to_index(PlannerInfo* root, IndexOptInfo* index, IndexClauseSet* clauseset); static void match_clauses_to_index(IndexOptInfo* index, List* clauses, IndexClauseSet* clauseset); static void match_clause_to_index(IndexOptInfo* index, RestrictInfo* rinfo, IndexClauseSet* clauseset); static bool match_clause_to_indexcol(IndexOptInfo* index, int indexcol, RestrictInfo* rinfo); @@ -205,6 +205,15 @@ Node* match_first_var_to_indkey(Node* node, int indkey); * 'rel' is the relation for which we want to generate index paths * * Note: check_partial_indexes() must have been run previously for this rel. + * + * Note: in cases involving LATERAL references in the relation's tlist, it's + * possible that rel->lateral_relids is nonempty. Currently, we include + * lateral_relids into the parameterization reported for each path, but don't + * take it into account otherwise. The fact that any such rels *must* be + * available as parameter sources perhaps should influence our choices of + * index quals ... but for now, it doesn't seem worth troubling over. + * In particular, comments below about "unparameterized" paths should be read + * as meaning "unparameterized so far as the indexquals are concerned". */ void create_index_paths(PlannerInfo* root, RelOptInfo* rel) { @@ -212,7 +221,6 @@ void create_index_paths(PlannerInfo* root, RelOptInfo* rel) List* bitindexpaths = NIL; List* bitjoinpaths = NIL; List* joinorclauses = NIL; - Relids lateral_referencers; IndexClauseSet rclauseset; IndexClauseSet jclauseset; IndexClauseSet eclauseset; @@ -236,23 +244,6 @@ void create_index_paths(PlannerInfo* root, RelOptInfo* rel) return; } - /* - * If there are any rels that have LATERAL references to this one, we - * cannot use join quals referencing them as index quals for this one, - * since such rels would have to be on the inside not the outside of a - * nestloop join relative to this one. Create a Relids set listing all - * such rels, for use in checks of potential join clauses. - */ - lateral_referencers = NULL; - foreach(lc, root->lateral_info_list) - { - LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc); - - if (bms_is_member(rel->relid, ljinfo->lateral_lhs)) - lateral_referencers = bms_add_member(lateral_referencers, - ljinfo->lateral_rhs); - } - /* Bitmap paths are collected and then dealt with at the end */ bitindexpaths = bitjoinpaths = joinorclauses = NIL; @@ -310,7 +301,7 @@ void create_index_paths(PlannerInfo* root, RelOptInfo* rel) */ errorno = memset_s(&jclauseset, sizeof(IndexClauseSet), 0, sizeof(jclauseset)); securec_check(errorno, "\0", "\0"); - match_join_clauses_to_index(root, rel, index, lateral_referencers, &jclauseset, &joinorclauses); + match_join_clauses_to_index(root, rel, index, &jclauseset, &joinorclauses); /* * Look for EquivalenceClasses that can generate joinclauses matching @@ -318,7 +309,7 @@ void create_index_paths(PlannerInfo* root, RelOptInfo* rel) */ errorno = memset_s(&eclauseset, sizeof(IndexClauseSet), 0, sizeof(eclauseset)); securec_check(errorno, "\0", "\0"); - match_eclass_clauses_to_index(root, index, lateral_referencers, &eclauseset); + match_eclass_clauses_to_index(root, index, &eclauseset); /* * If we found any plain or eclass join clauses, build parameterized @@ -2190,7 +2181,7 @@ static void match_restriction_clauses_to_index(RelOptInfo* rel, IndexOptInfo* in * Also, add any potentially usable join OR clauses to *joinorclauses. */ static void match_join_clauses_to_index( - PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* index, Relids lateral_referencers, + PlannerInfo* root, RelOptInfo* rel, IndexOptInfo* index, IndexClauseSet* clauseset, List** joinorclauses) { ListCell* lc = NULL; @@ -2200,11 +2191,7 @@ static void match_join_clauses_to_index( RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc); /* Check if clause can be moved to this rel */ - if (!join_clause_is_movable_to(rinfo, rel->relid)) - continue; - - /* Not useful if it conflicts with any LATERAL references */ - if (bms_overlap(rinfo->clause_relids, lateral_referencers)) + if (!join_clause_is_movable_to(rinfo, rel)) continue; /* Potentially usable, so see if it matches the index or is an OR */ @@ -2220,8 +2207,7 @@ static void match_join_clauses_to_index( * Identify EquivalenceClass join clauses for the rel that match the index. * Matching clauses are added to *clauseset. */ -static void match_eclass_clauses_to_index(PlannerInfo* root, IndexOptInfo* index, - Relids lateral_referencers,IndexClauseSet* clauseset) +static void match_eclass_clauses_to_index(PlannerInfo* root, IndexOptInfo* index, IndexClauseSet* clauseset) { int indexcol; @@ -2233,7 +2219,7 @@ static void match_eclass_clauses_to_index(PlannerInfo* root, IndexOptInfo* index List* clauses = NIL; /* Generate clauses, skipping any that join to lateral_referencers */ - clauses = generate_implied_equalities_for_indexcol(root, index, indexcol, lateral_referencers); + clauses = generate_implied_equalities_for_indexcol(root, index, indexcol, index->rel->lateral_referencers); /* * We have to check whether the results actually do match the index, @@ -2830,7 +2816,7 @@ void check_partial_indexes(PlannerInfo* root, RelOptInfo* rel) RestrictInfo* rinfo = (RestrictInfo*)lfirst(lc); /* Check if clause can be moved to this rel */ - if (!join_clause_is_movable_to(rinfo, rel->relid)) + if (!join_clause_is_movable_to(rinfo, rel)) continue; clauselist = lappend(clauselist, rinfo); diff --git a/src/gausskernel/optimizer/path/joinpath.cpp b/src/gausskernel/optimizer/path/joinpath.cpp index a94f11c00..595dd831f 100755 --- a/src/gausskernel/optimizer/path/joinpath.cpp +++ b/src/gausskernel/optimizer/path/joinpath.cpp @@ -50,12 +50,14 @@ static void copy_JoinCostWorkspace(JoinCostWorkspace* to, JoinCostWorkspace* from); static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels); + List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, + Relids param_source_rels, Relids extra_lateral_rels); static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels); + List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, + Relids param_source_rels, Relids extra_lateral_rels); 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 param_source_rels, Relids extra_lateral_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); @@ -125,6 +127,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou List* mergeclause_list = NIL; bool mergejoin_allowed = true; Relids param_source_rels = NULL; + Relids extra_lateral_rels = NULL; ListCell* lc = NULL; List *mergejoin_hint = u_sess->attr.attr_sql.enable_mergejoin ? NIL @@ -274,19 +277,57 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou { LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc); - if (bms_is_member(ljinfo->lateral_rhs, joinrel->relids)) { + if (bms_is_subset(ljinfo->lateral_rhs, joinrel->relids)) { param_source_rels = bms_join(param_source_rels, bms_difference(ljinfo->lateral_lhs, joinrel->relids)); } } + /* + * Another issue created by LATERAL references is that PlaceHolderVars + * that need to be computed at this join level might contain lateral + * references to rels not in the join, meaning that the paths for the join + * would need to be marked as parameterized by those rels, independently + * of all other considerations. Set extra_lateral_rels to the set of such + * rels. This will not affect our decisions as to which paths to + * generate; we merely add these rels to their required_outer sets. + */ + foreach(lc, root->placeholder_list) { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); + + /* PHVs without lateral refs can be skipped over quickly */ + if (phinfo->ph_lateral == NULL) + continue; + /* Is it due to be evaluated at this join, and not in either input? */ + if (bms_is_subset(phinfo->ph_eval_at, joinrel->relids) && + !bms_is_subset(phinfo->ph_eval_at, outerrel->relids) && + !bms_is_subset(phinfo->ph_eval_at, innerrel->relids)) { + /* Yes, remember its lateral rels */ + extra_lateral_rels = bms_add_members(extra_lateral_rels, + 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 + * it to required_outer below, while preserving the property that + * required_outer is exactly NULL if empty.) + */ + extra_lateral_rels = bms_del_members(extra_lateral_rels, joinrel->relids); + if (bms_is_empty(extra_lateral_rels)) { + bms_free(extra_lateral_rels); + extra_lateral_rels = NULL; + } + /* * 1. Consider mergejoin paths where both relations must be explicitly * sorted. Skip this if we can't mergejoin. */ if (mergejoin_allowed) sort_inner_and_outer( - root, joinrel, outerrel, innerrel, restrictlist, mergeclause_list, jointype, &extra, param_source_rels); + root, joinrel, outerrel, innerrel, restrictlist, mergeclause_list, jointype, + &extra, param_source_rels, extra_lateral_rels); /* * 2. Consider paths where the outer relation need not be explicitly @@ -304,7 +345,8 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou mergeclause_list, jointype, &extra, - param_source_rels); + param_source_rels, + extra_lateral_rels); #ifdef NOT_USED @@ -329,7 +371,8 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou jointype, sjinfo, &semifactors, - param_source_rels); + param_source_rels, + extra_lateral_rels); #endif /* @@ -339,7 +382,7 @@ void add_paths_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* ou */ if (u_sess->attr.attr_sql.enable_hashjoin || jointype == JOIN_FULL || hashjoin_hint != NIL) hash_inner_and_outer( - root, joinrel, outerrel, innerrel, restrictlist, jointype, &extra, param_source_rels); + root, joinrel, outerrel, innerrel, restrictlist, jointype, &extra, param_source_rels, extra_lateral_rels); #ifdef PGXC /* @@ -572,7 +615,7 @@ static void TryNestLoopPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy * the joinrel's pathlist via add_path(). */ static void try_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - JoinPathExtraData* extra, Relids param_source_rels, Path* outer_path, + JoinPathExtraData* extra, Relids param_source_rels, Relids extra_lateral_rels, Path* outer_path, Path* inner_path, List* restrict_clauses, List* pathkeys) { bool execOnCoords = false; @@ -597,6 +640,12 @@ static void try_nestloop_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j return; } + /* + * Independently of that, add parameterization needed for any + * PlaceHolderVars that need to be computed at the join. + */ + required_outer = bms_add_members(required_outer, extra_lateral_rels); + /* * Do a precheck to quickly eliminate obviously-inferior paths. We * calculate a cheap lower bound on the path's cost and then use @@ -788,8 +837,9 @@ static bool TryMergeJoinPreCheck(PlannerInfo* root, Relids paramSourceRels, * the joinrel's pathlist via add_path(). */ static void try_mergejoin_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* pathkeys, List* mergeclauses, List* outersortkeys, List* innersortkeys) + JoinPathExtraData *extra, Relids param_source_rels, Relids extra_lateral_rels, Path* outer_path, + Path* inner_path, List* restrict_clauses, List* pathkeys, List* mergeclauses, List* outersortkeys, + List* innersortkeys) { bool execOnCoords = false; Relids required_outer; @@ -799,6 +849,12 @@ static void try_mergejoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType return; } + /* + * Independently of that, add parameterization needed for any + * PlaceHolderVars that need to be computed at the join. + */ + required_outer = bms_add_members(required_outer, extra_lateral_rels); + /* * If the given paths are already well enough ordered, we can skip doing * an explicit sort. @@ -955,7 +1011,7 @@ static void TryHashJoinPathSingle(PlannerInfo* root, RelOptInfo* joinrel, JoinTy * the joinrel's pathlist via add_path(). */ static void try_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType jointype, JoinType save_jointype, - JoinPathExtraData* extra, Relids param_source_rels, Path* outer_path, + JoinPathExtraData* extra, Relids param_source_rels, Relids extra_lateral_rels, Path* outer_path, Path* inner_path, List* restrict_clauses, List* hashclauses) { bool execOnCoords = false; @@ -978,6 +1034,12 @@ static void try_hashjoin_path(PlannerInfo* root, RelOptInfo* joinrel, JoinType j return; } + /* + * Independently of that, add parameterization needed for any + * PlaceHolderVars that need to be computed at the join. + */ + required_outer = bms_add_members(required_outer, extra_lateral_rels); + /* * See comments in try_nestloop_path(). Also note that hashjoin paths * never have any output pathkeys, per comments in create_hashjoin_path. @@ -1142,7 +1204,8 @@ static bool is_cheapest_path(RelOptInfo* outerrel, RelOptInfo* innerrel, Path* o * 'param_source_rels' are OK targets for parameterization of result paths */ static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData *extra, Relids param_source_rels) + List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData *extra, Relids param_source_rels, + Relids extra_lateral_rels) { JoinType save_jointype = jointype; List* all_pathkeys = NIL; @@ -1286,6 +1349,7 @@ static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, outer_path, inner_path, restrictlist, @@ -1339,7 +1403,8 @@ static void sort_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI * 'param_source_rels' are OK targets for parameterization of result paths */ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outerrel, RelOptInfo* innerrel, - List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels) + List* restrictlist, List* mergeclause_list, JoinType jointype, JoinPathExtraData* extra, Relids param_source_rels, + Relids extra_lateral_rels) { JoinType save_jointype = jointype; bool nestjoinOK = false; @@ -1501,6 +1566,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, outerpath, inner_cheapest_total, restrictlist, @@ -1525,6 +1591,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, outerpath, innerpath, restrictlist, @@ -1541,6 +1608,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, outerpath, matpath, restrictlist, @@ -1591,6 +1659,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, outerpath, inner_cheapest_total, restrictlist, @@ -1673,6 +1742,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, outerpath, innerpath, restrictlist, @@ -1707,6 +1777,7 @@ static void match_unsorted_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, outerpath, innerpath, restrictlist, @@ -1750,7 +1821,7 @@ 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 param_source_rels, Relids extra_lateral_rels) { JoinType save_jointype = jointype; bool isouterjoin = IS_OUTER_JOIN((uint32)jointype); @@ -1856,6 +1927,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, cheapest_total_outer, cheapest_total_inner, restrictlist, @@ -1871,6 +1943,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, cheapest_total_outer, cheapest_total_inner, restrictlist, @@ -1883,6 +1956,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, cheapest_startup_outer, cheapest_total_inner, restrictlist, @@ -1905,6 +1979,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, cheapest_total_outer, cheapest_total_inner, restrictlist, @@ -1919,6 +1994,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, cheapest_startup_outer, cheapest_total_inner, restrictlist, @@ -1956,6 +2032,7 @@ static void hash_inner_and_outer(PlannerInfo* root, RelOptInfo* joinrel, RelOptI save_jointype, extra, param_source_rels, + extra_lateral_rels, outerpath, innerpath, restrictlist, diff --git a/src/gausskernel/optimizer/path/joinrels.cpp b/src/gausskernel/optimizer/path/joinrels.cpp index 568fa0874..5075d60ba 100755 --- a/src/gausskernel/optimizer/path/joinrels.cpp +++ b/src/gausskernel/optimizer/path/joinrels.cpp @@ -505,7 +505,7 @@ static bool join_is_legal(PlannerInfo* root, RelOptInfo* rel1, RelOptInfo* rel2, { LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l); - if (bms_is_member(ljinfo->lateral_rhs, rel2->relids) && + if (bms_is_subset(ljinfo->lateral_rhs, rel2->relids) && bms_overlap(ljinfo->lateral_lhs, rel1->relids)) { /* has to be implemented as nestloop with rel1 on left */ @@ -518,7 +518,7 @@ static bool join_is_legal(PlannerInfo* root, RelOptInfo* rel1, RelOptInfo* rel2, ((reversed && !unique_exchange) || match_sjinfo->jointype == JOIN_FULL)) return false; /* not implementable as nestloop */ } - if (bms_is_member(ljinfo->lateral_rhs, rel1->relids) && + if (bms_is_subset(ljinfo->lateral_rhs, rel1->relids) && bms_overlap(ljinfo->lateral_lhs, rel2->relids)) { /* has to be implemented as nestloop with rel2 on left */ @@ -827,10 +827,10 @@ bool have_join_order_restriction(PlannerInfo* root, RelOptInfo* rel1, RelOptInfo { LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l); - if (bms_is_member(ljinfo->lateral_rhs, rel2->relids) && + if (bms_is_subset(ljinfo->lateral_rhs, rel2->relids) && bms_overlap(ljinfo->lateral_lhs, rel1->relids)) return true; - if (bms_is_member(ljinfo->lateral_rhs, rel1->relids) && + if (bms_is_subset(ljinfo->lateral_rhs, rel1->relids) && bms_overlap(ljinfo->lateral_lhs, rel2->relids)) return true; } @@ -912,7 +912,7 @@ static bool has_join_restriction(PlannerInfo* root, RelOptInfo* rel) { LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l); - if (bms_is_member(ljinfo->lateral_rhs, rel->relids) || + if (bms_is_subset(ljinfo->lateral_rhs, rel->relids) || bms_overlap(ljinfo->lateral_lhs, rel->relids)) return true; } diff --git a/src/gausskernel/optimizer/plan/analyzejoins.cpp b/src/gausskernel/optimizer/plan/analyzejoins.cpp index 662ef0f5b..457287bd2 100644 --- a/src/gausskernel/optimizer/plan/analyzejoins.cpp +++ b/src/gausskernel/optimizer/plan/analyzejoins.cpp @@ -199,17 +199,23 @@ static bool join_is_removable(PlannerInfo* root, SpecialJoinInfo* sjinfo) * that will be used above the join. We only need to fail if such a PHV * actually references some inner-rel attributes; but the correct check * for that is relatively expensive, so we first check against ph_eval_at, - * which must mention the inner rel if the PHV uses any inner-rel attrs. + * which must mention the inner rel if the PHV uses any inner-rel attrs as + * non-lateral references. Note that if the PHV's syntactic scope is just + * the inner rel, we can't drop the rel even if the PHV is variable-free. */ foreach (l, root->placeholder_list) { - PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(l); - + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l); + if (bms_is_subset(phinfo->ph_needed, joinrelids)) - continue; /* PHV is not used above the join */ + continue; /* PHV is not used above the join */ + if (bms_overlap(phinfo->ph_lateral, innerrel->relids)) + return false; /* it references innerrel laterally */ if (!bms_overlap(phinfo->ph_eval_at, innerrel->relids)) - continue; /* it definitely doesn't reference innerrel */ - if (bms_overlap(pull_varnos((Node*)phinfo->ph_var), innerrel->relids)) - return false; /* it does reference innerrel */ + continue; /* it definitely doesn't reference innerrel */ + if (bms_is_subset(phinfo->ph_eval_at, innerrel->relids)) + return false; /* there isn't any other place to eval PHV */ + if (bms_overlap(pull_varnos((Node *) phinfo->ph_var->phexpr), innerrel->relids)) + return false; /* it does reference innerrel */ } /* @@ -340,33 +346,28 @@ static void remove_rel_from_query(PlannerInfo* root, int relid, Relids joinrelid * included in any lateral_lhs set. (It probably can't be, since that * should have precluded deciding to remove it; but let's cope anyway.) */ - for (l = list_head(root->lateral_info_list); l != NULL; l = nextl) - { + for (l = list_head(root->lateral_info_list); l != NULL; l = nextl) { LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l); nextl = lnext(l); - if (ljinfo->lateral_rhs == (Index)relid) - root->lateral_info_list = list_delete_ptr(root->lateral_info_list, - ljinfo); - else + ljinfo->lateral_rhs = bms_del_member(ljinfo->lateral_rhs, relid); + if (bms_is_empty(ljinfo->lateral_rhs)) { + root->lateral_info_list = list_delete_ptr(root->lateral_info_list, ljinfo); + } else { ljinfo->lateral_lhs = bms_del_member(ljinfo->lateral_lhs, relid); + AssertEreport(!bms_is_empty(ljinfo->lateral_lhs), MOD_OPT, ""); + } } /* * Likewise remove references from PlaceHolderVar data structures. * - * Here we have a special case: if a PHV's eval_at set is just the target - * relid, we want to leave it that way instead of reducing it to the empty - * set. An empty eval_at set would confuse later processing since it - * would match every possible eval placement. */ foreach (l, root->placeholder_list) { PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(l); - phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid); - if (bms_is_empty(phinfo->ph_eval_at)) /* oops, belay that */ - phinfo->ph_eval_at = bms_add_member(phinfo->ph_eval_at, relid); - + AssertEreport(!bms_is_empty(phinfo->ph_eval_at), MOD_OPT, ""); + AssertEreport(!bms_is_member(relid, phinfo->ph_lateral), MOD_OPT, ""); phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid); } diff --git a/src/gausskernel/optimizer/plan/createplan.cpp b/src/gausskernel/optimizer/plan/createplan.cpp index d30577617..e615bae5a 100755 --- a/src/gausskernel/optimizer/plan/createplan.cpp +++ b/src/gausskernel/optimizer/plan/createplan.cpp @@ -479,7 +479,7 @@ Plan* create_stream_plan(PlannerInfo* root, StreamPath* best_path) } /* We don't want any excess columns when streaming */ - disuse_physical_tlist(subplan, best_path->subpath); + disuse_physical_tlist(root, subplan, best_path->subpath); /* * If there has stream path for replication node, @@ -647,19 +647,11 @@ static Plan* create_scan_plan(PlannerInfo* root, Path* best_path) tlist = build_physical_tlist(root, rel); /* if fail because of dropped cols, use regular method */ if (tlist == NIL) { - tlist = build_relation_tlist(rel); + tlist = build_path_tlist(root, best_path); } } } else { - tlist = build_relation_tlist(rel); - /* - * If it's a parameterized otherrel, there might be lateral references - * in the tlist, which need to be replaced with Params. This cannot - * happen for regular baserels, though. Note use_physical_tlist() - * always fails for otherrels, so we don't need to check this above. - */ - if (rel->reloptkind != RELOPT_BASEREL && best_path->param_info) - tlist = (List *) replace_nestloop_params(root, (Node *) tlist); + tlist = build_path_tlist(root, best_path); } /* @@ -1018,7 +1010,7 @@ static Path* wipe_dummy_path(Plan* plan, Path* path) * undo the decision made by use_physical_tlist(). Currently, Hash, Sort, * and Material nodes want this, so they don't have to store useless columns. */ -void disuse_physical_tlist(Plan* plan, Path* path) +void disuse_physical_tlist(PlannerInfo *root, Plan* plan, Path* path) { #ifndef ENABLE_MULTIPLE_NODES /* StreamPath may not create stream plan, but it already do this step, so just return. */ @@ -1065,7 +1057,7 @@ void disuse_physical_tlist(Plan* plan, Path* path) } List* tarList = NULL; if (path->parent != NULL) - tarList = build_relation_tlist(path->parent); + tarList = build_path_tlist(root, path); if (tarList != NULL) plan->targetlist = tarList; break; @@ -1081,7 +1073,7 @@ void disuse_physical_tlist(Plan* plan, Path* path) case T_BitmapHeapScan: case T_CStoreIndexHeapScan: case T_TidScan: { - List* sub_parList = build_relation_tlist(piPath->subPath->parent); + List* sub_parList = build_path_tlist(root, path); if (sub_parList != NULL) { subPlan->targetlist = sub_parList; plan->targetlist = sub_parList; @@ -1270,7 +1262,7 @@ static Plan* create_join_plan(PlannerInfo* root, JoinPath* best_path) static Plan* create_append_plan(PlannerInfo* root, AppendPath* best_path) { Append* plan = NULL; - List* tlist = build_relation_tlist(best_path->path.parent); + List* tlist = build_path_tlist(root, &best_path->path); List* subplans = NIL; ListCell* subpaths = NULL; @@ -1347,7 +1339,7 @@ static Plan* create_merge_append_plan(PlannerInfo* root, MergeAppendPath* best_p { MergeAppend* node = makeNode(MergeAppend); Plan* plan = &node->plan; - List* tlist = build_relation_tlist(best_path->path.parent); + List* tlist = build_path_tlist(root, &best_path->path); List* pathkeys = best_path->path.pathkeys; List* subplans = NIL; ListCell* subpaths = NULL; @@ -1666,7 +1658,7 @@ static Material* create_material_plan(PlannerInfo* root, MaterialPath* best_path subplan = create_plan_recurse(root, best_path->subpath); /* We don't want any excess columns in the materialized tuples */ - disuse_physical_tlist(subplan, best_path->subpath); + disuse_physical_tlist(root, subplan, best_path->subpath); plan = make_material(subplan, best_path->materialize_all); @@ -1740,7 +1732,7 @@ static Plan* create_unique_plan(PlannerInfo* root, UniquePath* best_path) uniq_exprs = best_path->uniq_exprs; /* initialize modified subplan tlist as just the "required" vars */ - newtlist = build_relation_tlist(best_path->path.parent); + newtlist = build_path_tlist(root, &best_path->path); nextresno = list_length(newtlist) + 1; itemChange = false; @@ -1824,7 +1816,7 @@ static Plan* create_unique_plan(PlannerInfo* root, UniquePath* best_path) if (best_path->umethod == UNIQUE_PATH_HASH) { long numGroups[2]; Oid* groupOperators = NULL; - List* tlist = build_relation_tlist(best_path->path.parent); + List* tlist = build_path_tlist(root, &best_path->path); numGroups[0] = (long)Min(PATH_LOCAL_ROWS(&best_path->path), (double)LONG_MAX); numGroups[1] = (long)Min(best_path->path.rows, (double)LONG_MAX); @@ -4149,7 +4141,7 @@ static ExtensiblePlan* create_extensible_plan( static NestLoop* create_nestloop_plan(PlannerInfo* root, NestPath* best_path, Plan* outer_plan, Plan* inner_plan) { NestLoop* join_plan = NULL; - List* tlist = build_relation_tlist(best_path->path.parent); + List* tlist = build_path_tlist(root, &best_path->path); List* joinrestrictclauses = best_path->joinrestrictinfo; List* joinclauses = NIL; List* otherclauses = NIL; @@ -4246,7 +4238,7 @@ static NestLoop* create_nestloop_plan(PlannerInfo* root, NestPath* best_path, Pl static MergeJoin* create_mergejoin_plan(PlannerInfo* root, MergePath* best_path, Plan* outer_plan, Plan* inner_plan) { - List* tlist = build_relation_tlist(best_path->jpath.path.parent); + List* tlist = build_path_tlist(root, &best_path->jpath.path); List* joinclauses = NIL; List* otherclauses = NIL; List* mergeclauses = NIL; @@ -4307,7 +4299,7 @@ static MergeJoin* create_mergejoin_plan(PlannerInfo* root, MergePath* best_path, * Make sure there are no excess columns in the inputs if sorting. */ if (best_path->outersortkeys) { - disuse_physical_tlist(outer_plan, best_path->jpath.outerjoinpath); + disuse_physical_tlist(root, outer_plan, best_path->jpath.outerjoinpath); outer_plan = (Plan*)make_sort_from_pathkeys(root, outer_plan, best_path->outersortkeys, -1.0); copy_mem_info(&((Sort*)outer_plan)->mem_info, &best_path->outer_mem_info); @@ -4327,7 +4319,7 @@ static MergeJoin* create_mergejoin_plan(PlannerInfo* root, MergePath* best_path, } if (best_path->innersortkeys) { - disuse_physical_tlist(inner_plan, best_path->jpath.innerjoinpath); + disuse_physical_tlist(root, inner_plan, best_path->jpath.innerjoinpath); inner_plan = (Plan*)make_sort_from_pathkeys(root, inner_plan, best_path->innersortkeys, -1.0); copy_mem_info(&((Sort*)inner_plan)->mem_info, &best_path->inner_mem_info); @@ -4881,7 +4873,7 @@ static void set_bloomfilter(PlannerInfo* root, Relids lefttree_relids, HashJoin* */ static HashJoin* create_hashjoin_plan(PlannerInfo* root, HashPath* best_path, Plan* outer_plan, Plan* inner_plan) { - List* tlist = build_relation_tlist(best_path->jpath.path.parent); + List* tlist = build_path_tlist(root, &best_path->jpath.path); List* joinclauses = NIL; List* otherclauses = NIL; List* hashclauses = NIL; @@ -4931,14 +4923,14 @@ static HashJoin* create_hashjoin_plan(PlannerInfo* root, HashPath* best_path, Pl hashclauses = get_switched_clauses(best_path->path_hashclauses, best_path->jpath.outerjoinpath->parent->relids); /* We don't want any excess columns in the hashed tuples */ - disuse_physical_tlist(inner_plan, best_path->jpath.innerjoinpath); + disuse_physical_tlist(root, inner_plan, best_path->jpath.innerjoinpath); /* If we expect batching, suppress excess columns in outer tuples too */ if (best_path->num_batches > 1 || (u_sess->attr.attr_sql.enable_vector_engine && u_sess->attr.attr_sql.vectorEngineStrategy != OFF_VECTOR_ENGINE && u_sess->attr.attr_sql.enable_vector_targetlist)) - disuse_physical_tlist(outer_plan, best_path->jpath.outerjoinpath); + disuse_physical_tlist(root, outer_plan, best_path->jpath.outerjoinpath); /* * If there is a single join clause and we can identify the outer variable @@ -5070,15 +5062,36 @@ static Node* replace_nestloop_params_mutator(Node* node, PlannerInfo* root) Assert(phv->phlevelsup == 0); /* - * If not to be replaced, just return the PlaceHolderVar unmodified. - * We use bms_overlap as a cheap/quick test to see if the PHV might be - * evaluated in the outer rels, and then grab its PlaceHolderInfo to - * tell for sure. + * Check whether we need to replace the PHV. We use bms_overlap as a + * cheap/quick test to see if the PHV might be evaluated in the outer + * rels, and then grab its PlaceHolderInfo to tell for sure. */ - if (!bms_overlap(phv->phrels, root->curOuterRels)) - return node; - if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at, root->curOuterRels)) - return node; + if (!bms_overlap(phv->phrels, root->curOuterRels) || + !bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at, + root->curOuterRels)) { + /* + * We can't replace the whole PHV, but we might still need to + * replace Vars or PHVs within its expression, in case it ends up + * actually getting evaluated here. (It might get evaluated in + * this plan node, or some child node; in the latter case we don't + * really need to process the expression here, but we haven't got + * enough info to tell if that's the case.) Flat-copy the PHV + * node and then recurse on its expression. + * + * Note that after doing this, we might have different + * representations of the contents of the same PHV in different + * parts of the plan tree. This is OK because equal() will just + * match on phid/phlevelsup, so setrefs.c will still recognize an + * 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 *) + replace_nestloop_params_mutator((Node *) phv->phexpr, root); + return (Node *) newphv; + } /* Create a Param representing the PlaceHolderVar */ param = assign_nestloop_param_placeholdervar(root, phv); /* Is this param already listed in root->curOuterParams? */ diff --git a/src/gausskernel/optimizer/plan/initsplan.cpp b/src/gausskernel/optimizer/plan/initsplan.cpp index d99fd5b8d..d9126b9d6 100644 --- a/src/gausskernel/optimizer/plan/initsplan.cpp +++ b/src/gausskernel/optimizer/plan/initsplan.cpp @@ -152,6 +152,8 @@ void add_vars_to_targetlist(PlannerInfo* root, List* vars, Relids where_needed, Var* var = (Var*)node; RelOptInfo* rel = find_base_rel(root, var->varno); int attno = var->varattno; + if (bms_is_subset(where_needed, rel->relids)) + continue; AssertEreport(attno >= rel->min_attr && attno <= rel->max_attr, MOD_OPT, "attno is out of range"); attno -= rel->min_attr; @@ -310,90 +312,153 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) void create_lateral_join_info(PlannerInfo *root) { - int rti; + int rti; + ListCell *lc = NULL; - /* We need do nothing if the query contains no LATERAL RTEs */ - if (!root->hasLateralRTEs) - return; + /* We need do nothing if the query contains no LATERAL RTEs */ + if (!root->hasLateralRTEs) + return; - /* - * Examine all baserels (the rel array has been set up by now). - */ - for (rti = 1; rti < root->simple_rel_array_size; rti++) - { - RelOptInfo *brel = root->simple_rel_array[rti]; - Relids lateral_relids; - ListCell *lc = NULL; + /* + * Examine all baserels (the rel array has been set up by now). + */ + for (rti = 1; rti < root->simple_rel_array_size; rti++) { + RelOptInfo *brel = root->simple_rel_array[rti]; + Relids lateral_relids; - /* there may be empty slots corresponding to non-baserel RTEs */ + /* there may be empty slots corresponding to non-baserel RTEs */ if (brel == NULL) - continue; + continue; - Assert(brel->relid == (Index)rti); /* sanity check on array */ + Assert(brel->relid == (Index)rti); /* sanity check on array */ - /* ignore RTEs that are "other rels" */ - if (brel->reloptkind != RELOPT_BASEREL) - continue; + /* ignore RTEs that are "other rels" */ + if (brel->reloptkind != RELOPT_BASEREL) + continue; - lateral_relids = NULL; + lateral_relids = NULL; /* consider each laterally-referenced Var or PHV */ - foreach(lc, brel->lateral_vars) - { - Node *node = (Node *) lfirst(lc); + foreach(lc, brel->lateral_vars) { + Node *node = (Node *) lfirst(lc); - if (IsA(node, Var)) - { - Var *var = (Var *) node; + if (IsA(node, Var)) { + Var *var = (Var *) node; + add_lateral_info(root, bms_make_singleton(var->varno), brel->relids); + lateral_relids = bms_add_member(lateral_relids, var->varno); + } else if (IsA(node, PlaceHolderVar)) { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false); - add_lateral_info(root, rti, bms_make_singleton(var->varno)); - lateral_relids = bms_add_member(lateral_relids, - var->varno); - } - else if (IsA(node, PlaceHolderVar)) - { - PlaceHolderVar *phv = (PlaceHolderVar *) node; - PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, - false); - - add_lateral_info(root, rti, bms_copy(phinfo->ph_eval_at)); - lateral_relids = bms_add_members(lateral_relids, + add_lateral_info(root, phinfo->ph_eval_at, brel->relids); + lateral_relids = bms_add_members(lateral_relids, phinfo->ph_eval_at); - } - else - Assert(false); - } + } else { + Assert(false); + } + } - /* We now know all the relids needed for lateral refs in this rel */ - if (bms_is_empty(lateral_relids)) - continue; /* ensure lateral_relids is NULL if empty */ - brel->lateral_relids = lateral_relids; + /* We now know all the relids needed for lateral refs in this rel */ + if (bms_is_empty(lateral_relids)) + continue; /* ensure lateral_relids is NULL if empty */ + brel->lateral_relids = lateral_relids; - /* - * If it's an appendrel parent, copy its lateral_relids to each child - * rel. We intentionally give each child rel the same minimum - * parameterization, even though it's quite possible that some don't - * reference all the lateral rels. This is because any append path - * for the parent will have to have the same parameterization for - * every child anyway, and there's no value in forcing extra - * reparameterize_path() calls. - */ - if (root->simple_rte_array[rti]->inh) - { - foreach(lc, root->append_rel_list) - { - AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc); - RelOptInfo *childrel = NULL; - - if (appinfo->parent_relid != (Index)rti) - continue; - childrel = root->simple_rel_array[appinfo->child_relid]; - Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); - Assert(childrel->lateral_relids == NULL); - childrel->lateral_relids = lateral_relids; - } - } - } + /* + * Now check for lateral references within PlaceHolderVars, and make + * LateralJoinInfos describing each such reference. Unlike references in + * unflattened LATERAL RTEs, the referencing location could be a join. + */ + foreach(lc, root->placeholder_list) { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); + Relids eval_at = phinfo->ph_eval_at; + + if (phinfo->ph_lateral != NULL) { + List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr, + PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + ListCell *lc2; + + foreach(lc2, vars) { + Node *node = (Node *) lfirst(lc2); + if (IsA(node, Var)) { + Var *var = (Var *) node; + if (!bms_is_member(var->varno, eval_at)) + add_lateral_info(root, + bms_make_singleton(var->varno), + eval_at); + } else if (IsA(node, PlaceHolderVar)) { + PlaceHolderVar *other_phv = (PlaceHolderVar *) node; + PlaceHolderInfo *other_phi; + + other_phi = find_placeholder_info(root, other_phv, + false); + if (!bms_is_subset(other_phi->ph_eval_at, eval_at)) + add_lateral_info(root, other_phi->ph_eval_at, eval_at); + } else { + Assert(false); + } + } + list_free(vars); + } + } + + /* If we found no lateral references, we're done. */ + if (root->lateral_info_list == NIL) + return; + + /* + * Now that we've identified all lateral references, make a second pass in + * which we mark each baserel with the set of relids of rels that + * reference it laterally (essentially, the inverse mapping of + * lateral_relids). We'll need this for join_clause_is_movable_to(). + * + * Also, propagate lateral_relids and lateral_referencers from appendrel + * parent rels to their child rels. We intentionally give each child rel + * the same minimum parameterization, even though it's quite possible that + * some don't reference all the lateral rels. This is because any append + * path for the parent will have to have the same parameterization for + * every child anyway, and there's no value in forcing extra + * reparameterize_path() calls. Similarly, a lateral reference to the + * parent prevents use of otherwise-movable join rels for each child. + */ + for (rti = 1; rti < root->simple_rel_array_size; rti++) { + RelOptInfo *brel = root->simple_rel_array[rti]; + Relids lateral_referencers; + + if (brel == NULL) + continue; + if (brel->reloptkind != RELOPT_BASEREL) + continue; + + /* Compute lateral_referencers using the finished lateral_info_list */ + lateral_referencers = NULL; + foreach(lc, root->lateral_info_list) { + LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc); + if (bms_is_member(brel->relid, ljinfo->lateral_lhs)) + lateral_referencers = bms_add_members(lateral_referencers, + ljinfo->lateral_rhs); + } + brel->lateral_referencers = lateral_referencers; + + /* + * If it's an appendrel parent, copy its lateral_relids and + * lateral_referencers to each child rel. + */ + if (root->simple_rte_array[rti]->inh) { + foreach(lc, root->append_rel_list) { + AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc); + RelOptInfo *childrel; + if (appinfo->parent_relid != rti) + continue; + childrel = root->simple_rel_array[appinfo->child_relid]; + Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); + Assert(childrel->lateral_relids == NULL); + childrel->lateral_relids = brel->lateral_relids; + Assert(childrel->lateral_referencers == NULL); + childrel->lateral_referencers = brel->lateral_referencers; + } + } + } + } } /* @@ -403,36 +468,40 @@ create_lateral_join_info(PlannerInfo *root) * We suppress redundant list entries. The passed lhs set must be freshly * made; we free it if not used in a new list entry. */ -void add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs) +void add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs) { - LateralJoinInfo *ljinfo = NULL; - ListCell *l = NULL; - - Assert(!bms_is_member(rhs, lhs)); - - /* - * If an existing list member has the same RHS and an LHS that is a subset - * of the new one, it's redundant, but we don't trouble to get rid of it. - * The only case that is really worth worrying about is identical entries, - * and we handle that well enough with this simple logic. - */ - foreach(l, root->lateral_info_list) { - ljinfo = (LateralJoinInfo *) lfirst(l); - if (rhs == ljinfo->lateral_rhs && - bms_is_subset(lhs, ljinfo->lateral_lhs)) { - bms_free(lhs); - return; - } - } - - /* Not there, so make a new entry */ - ljinfo = makeNode(LateralJoinInfo); - ljinfo->lateral_rhs = rhs; - ljinfo->lateral_lhs = lhs; - root->lateral_info_list = lappend(root->lateral_info_list, ljinfo); + LateralJoinInfo *ljinfo; + ListCell *lc; + + /* Sanity-check the input */ + Assert(!bms_is_empty(lhs)); + Assert(!bms_is_empty(rhs)); + Assert(!bms_overlap(lhs, rhs)); + + /* + * The input is redundant if it has the same RHS and an LHS that is a + * subset of an existing entry's. If an existing entry has the same RHS + * and an LHS that is a subset of the new one, it's redundant, but we + * don't trouble to get rid of it. The only case that is really worth + * worrying about is identical entries, and we handle that well enough + * with this simple logic. + */ + foreach(lc, root->lateral_info_list) { + ljinfo = (LateralJoinInfo *) lfirst(lc); + if (bms_equal(rhs, ljinfo->lateral_rhs) && + bms_is_subset(lhs, ljinfo->lateral_lhs)) + return; + } + + /* Not there, so make a new entry */ + ljinfo = makeNode(LateralJoinInfo); + ljinfo->lateral_lhs = bms_copy(lhs); + ljinfo->lateral_rhs = bms_copy(rhs); + root->lateral_info_list = lappend(root->lateral_info_list, ljinfo); } + /***************************************************************************** * * JOIN TREE PROCESSING @@ -871,6 +940,7 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, Relids left_inners, right_inners, nonnullable_rels, ojscope; List* leftjoinlist = NIL; List* rightjoinlist = NIL; + List* my_quals; SpecialJoinInfo* sjinfo = NULL; ListCell* l = NULL; @@ -954,6 +1024,29 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, check_join_view_quals(root, (List*)j->quals, non_keypreserved, j->jointype, (RangeTblRef*)leftpart, (RangeTblRef*)rightpart); } + /* + * Try to process any quals postponed by children. If they need + * further postponement, add them to my output postponed_qual_list. + * Quals that can be processed now must be included in my_quals, so + * that they'll be handled properly in make_outerjoininfo. + */ + my_quals = NIL; + foreach(l, child_postponed_quals) { + PostponedQual *pq = (PostponedQual *) lfirst(l); + + if (bms_is_subset(pq->relids, *qualscope)) { + my_quals = lappend(my_quals, pq->qual); + } else { + /* + * We should not be postponing any quals past an outer join. + * If this Assert fires, pull_up_subqueries() messed up. + */ + Assert(j->jointype == JOIN_INNER); + *postponed_qual_list = lappend(*postponed_qual_list, pq); + } + } + my_quals = list_concat(my_quals, (List *) j->quals); + /* * For an OJ, form the SpecialJoinInfo now, because we need the OJ's * semantic scope (ojscope) to pass to distribute_qual_to_rels. But @@ -965,7 +1058,7 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, */ if (j->jointype != JOIN_INNER) { sjinfo = make_outerjoininfo(root, leftids, rightids, *inner_join_rels, - j->jointype, (List*)j->quals); + j->jointype, my_quals); if (j->jointype == JOIN_SEMI) ojscope = NULL; else @@ -975,33 +1068,8 @@ static List* deconstruct_recurse(PlannerInfo* root, Node* jtnode, ojscope = NULL; } - /* - * Try to process any quals postponed by children. If they need - * further postponement, add them to my output postponed_qual_list. - */ - foreach(l, child_postponed_quals) - { - PostponedQual *pq = (PostponedQual *) lfirst(l); - - if (bms_is_subset(pq->relids, *qualscope)) - distribute_qual_to_rels(root, pq->qual, - false, below_outer_join, j->jointype, - root->qualSecurityLevel, *qualscope, - ojscope, nonnullable_rels, NULL, - NULL); - else - { - /* - * We should not be postponing any quals past an outer join. - * If this Assert fires, pull_up_subqueries() messed up. - */ - Assert(j->jointype == JOIN_INNER); - *postponed_qual_list = lappend(*postponed_qual_list, pq); - } - } - /* Process the JOIN's qual clauses */ - foreach (l, (List*)j->quals) { + foreach (l, my_quals) { Node* qual = (Node*)lfirst(l); distribute_qual_to_rels(root, diff --git a/src/gausskernel/optimizer/plan/planmain.cpp b/src/gausskernel/optimizer/plan/planmain.cpp index 4da32633f..6c06838b9 100755 --- a/src/gausskernel/optimizer/plan/planmain.cpp +++ b/src/gausskernel/optimizer/plan/planmain.cpp @@ -185,12 +185,6 @@ RelOptInfo* query_planner(PlannerInfo* root, List* tlist, process_security_clause_appendrel(root); - /* - * Create the LateralJoinInfo list now that we have finalized - * PlaceHolderVar eval levels. - */ - create_lateral_join_info(root); - /* * Reconsider any postponed outer-join quals now that we have built up * equivalence classes. (This could result in further additions or @@ -237,6 +231,12 @@ RelOptInfo* query_planner(PlannerInfo* root, List* tlist, */ add_placeholders_to_base_rels(root); + /* + * Construct the lateral reference sets now that we have finalized + * PlaceHolderVar eval levels. + */ + create_lateral_join_info(root); + /* * Look for join OR clauses that we can extract single-relation * restriction OR clauses from. diff --git a/src/gausskernel/optimizer/plan/planner.cpp b/src/gausskernel/optimizer/plan/planner.cpp index eac0ad1ed..c818a5733 100755 --- a/src/gausskernel/optimizer/plan/planner.cpp +++ b/src/gausskernel/optimizer/plan/planner.cpp @@ -3599,7 +3599,7 @@ static Plan* internal_grouping_planner(PlannerInfo* root, double tuple_fraction) (u_sess->attr.attr_sql.enable_vector_engine && u_sess->attr.attr_sql.vectorEngineStrategy != OFF_VECTOR_ENGINE && u_sess->attr.attr_sql.enable_vector_targetlist)) - disuse_physical_tlist(result_plan, best_path); + disuse_physical_tlist(root, result_plan, best_path); locate_grouping_columns(root, tlist, result_plan->targetlist, groupColIdx); } diff --git a/src/gausskernel/optimizer/prep/prepjointree.cpp b/src/gausskernel/optimizer/prep/prepjointree.cpp index f73280a73..38d1841cd 100755 --- a/src/gausskernel/optimizer/prep/prepjointree.cpp +++ b/src/gausskernel/optimizer/prep/prepjointree.cpp @@ -57,6 +57,8 @@ typedef struct pullup_replace_vars_context { PlannerInfo* root; List* targetlist; /* tlist of subquery being pulled up */ RangeTblEntry* target_rte; /* RTE of subquery */ + Relids relids; /* relids within subquery, as numbered after + * pullup (set only if target_rte->lateral) */ bool* outer_hasSubLinks; /* -> outer query's hasSubLinks */ int varno; /* varno of subquery */ bool need_phvs; /* do we need PlaceHolderVars? */ @@ -1244,6 +1246,11 @@ static Node* pull_up_simple_subquery(PlannerInfo* root, Node* jtnode, RangeTblEn rvcontext.root = root; rvcontext.targetlist = subquery->targetList; rvcontext.target_rte = rte; + if (rte->lateral) + rvcontext.relids = get_relids_in_jointree((Node *) subquery->jointree, + true); + else /* won't need relids */ + rvcontext.relids = NULL; rvcontext.outer_hasSubLinks = &parse->hasSubLinks; rvcontext.varno = varno; rvcontext.need_phvs = (lowest_nulling_outer_join != NULL || containing_appendrel != NULL); @@ -2213,7 +2220,16 @@ static Node* pullup_replace_vars_callback(Var* var, replace_rte_variables_contex bool wrap = false; if (newnode && IsA(newnode, Var) && ((Var*)newnode)->varlevelsup == 0) { - /* Simple Vars always escape being wrapped */ + /* + * Simple Vars always escape being wrapped, unless they are + * lateral references to something outside the subquery being + * pulled up. (Even then, we could omit the PlaceHolderVar if + * the referenced rel is under the same lowest outer join, but + * it doesn't seem worth the trouble to check that.) + */ + if (rcon->target_rte->lateral && !bms_is_member(((Var *) newnode)->varno, rcon->relids)) + wrap = true; + else wrap = false; } else if (newnode && IsA(newnode, PlaceHolderVar) && ((PlaceHolderVar*)newnode)->phlevelsup == 0) { /* No need to wrap a PlaceHolderVar with another one, either */ @@ -2222,20 +2238,28 @@ static Node* pullup_replace_vars_callback(Var* var, replace_rte_variables_contex /* Wrap all non-Vars in a PlaceHolderVar */ wrap = true; } else { - /* - * If it contains a Var of current level, and does not contain - * any non-strict constructs, then it's certainly nullable so - * we don't need to insert a PlaceHolderVar. - * - * This analysis could be tighter: in particular, a non-strict - * construct hidden within a lower-level PlaceHolderVar is not - * reason to add another PHV. But for now it doesn't seem - * worth the code to be more exact. - * - * Note: in future maybe we should insert a PlaceHolderVar - * anyway, if the tlist item is expensive to evaluate? - */ - if (contain_vars_of_level((Node*)newnode, 0) && !contain_nonstrict_functions((Node*)newnode)) { + /* + * If it contains a Var of the subquery being pulled up, and + * does not contain any non-strict constructs, then it's + * certainly nullable so we don't need to insert a + * PlaceHolderVar. + * + * This analysis could be tighter: in particular, a non-strict + * construct hidden within a lower-level PlaceHolderVar is not + * reason to add another PHV. But for now it doesn't seem + * worth the code to be more exact. + * + * Note: in future maybe we should insert a PlaceHolderVar + * anyway, if the tlist item is expensive to evaluate? + * + * For a LATERAL subquery, we have to check the actual var + * membership of the node, but if it's non-lateral then any + * level-zero var must belong to the subquery. + */ + if ((rcon->target_rte->lateral ? + bms_overlap(pull_varnos((Node *) newnode), rcon->relids) : + contain_vars_of_level((Node *) newnode, 0)) && + !contain_nonstrict_functions((Node *) newnode)) { /* No wrap needed */ wrap = false; } else { diff --git a/src/gausskernel/optimizer/util/nodegroups.cpp b/src/gausskernel/optimizer/util/nodegroups.cpp index ecf8ef855..4cbb2761c 100644 --- a/src/gausskernel/optimizer/util/nodegroups.cpp +++ b/src/gausskernel/optimizer/util/nodegroups.cpp @@ -2390,7 +2390,7 @@ Plan* ng_agg_force_shuffle(PlannerInfo* root, List* groupcls, Plan* subplan, Lis if (is_execute_on_datanodes(subplan) && (!root->is_correlated) && CNG_MODE_FORCE == cng_mode && !ng_is_same_group(distribution, target_distribution)) { if (subpath != NULL) - disuse_physical_tlist(subplan, subpath); + disuse_physical_tlist(root, subplan, subpath); double multiple_force = 1.0; List* distribute_keys = diff --git a/src/gausskernel/optimizer/util/orclauses.cpp b/src/gausskernel/optimizer/util/orclauses.cpp index c3eb80c9c..399b032be 100644 --- a/src/gausskernel/optimizer/util/orclauses.cpp +++ b/src/gausskernel/optimizer/util/orclauses.cpp @@ -102,7 +102,7 @@ void extract_restriction_or_clauses(PlannerInfo *root) foreach(lc, rel->joininfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - if (restriction_is_or_clause(rinfo) && join_clause_is_movable_to(rinfo, rti) && rinfo->norm_selec <= 1) { + if (restriction_is_or_clause(rinfo) && join_clause_is_movable_to(rinfo, rel) && rinfo->norm_selec <= 1) { /* Try to extract a qual for this rel only */ Expr *orclause = extract_or_clause(rinfo, rel); diff --git a/src/gausskernel/optimizer/util/placeholder.cpp b/src/gausskernel/optimizer/util/placeholder.cpp index 36c481521..6730d55bc 100644 --- a/src/gausskernel/optimizer/util/placeholder.cpp +++ b/src/gausskernel/optimizer/util/placeholder.cpp @@ -68,6 +68,7 @@ PlaceHolderVar* make_placeholder_expr(PlannerInfo* root, Expr* expr, Relids phre PlaceHolderInfo* find_placeholder_info(PlannerInfo* root, PlaceHolderVar* phv, bool create_new_ph) { PlaceHolderInfo* phinfo = NULL; + Relids rels_used; ListCell* lc = NULL; /* if this ever isn't true, we'd need to be able to look in parent lists */ @@ -90,7 +91,23 @@ PlaceHolderInfo* find_placeholder_info(PlannerInfo* root, PlaceHolderVar* phv, b phinfo->phid = phv->phid; phinfo->ph_var = (PlaceHolderVar*)copyObject(phv); - phinfo->ph_eval_at = pull_varnos((Node*)phv); + /* + * Any referenced rels that are outside the PHV's syntactic scope are + * LATERAL references, which should be included in ph_lateral but not in + * ph_eval_at. If no referenced rels are within the syntactic scope, + * force evaluation at the syntactic location. + */ + rels_used = pull_varnos((Node *) phv->phexpr); + phinfo->ph_lateral = bms_difference(rels_used, phv->phrels); + /* make it exactly NULL if empty */ + if (bms_is_empty(phinfo->ph_lateral)) + phinfo->ph_lateral = NULL; + phinfo->ph_eval_at = bms_int_members(rels_used, phv->phrels); + /* If no contained vars, force evaluation at syntactic location */ + if (bms_is_empty(phinfo->ph_eval_at)) { + phinfo->ph_eval_at = bms_copy(phv->phrels); + AssertEreport(!bms_is_empty(phinfo->ph_eval_at), MOD_OPT, ""); + } /* ph_eval_at may change later, see update_placeholder_eval_levels */ phinfo->ph_needed = NULL; /* initially it's unused */ /* for the moment, estimate width using just the datatype info */ @@ -112,8 +129,11 @@ PlaceHolderInfo* find_placeholder_info(PlannerInfo* root, PlaceHolderVar* phv, b * find_placeholders_in_jointree * Search the jointree for PlaceHolderVars, and build PlaceHolderInfos * - * We don't need to look at the targetlist because build_base_rel_tlists() - * will already have made entries for any PHVs in the tlist. + * This is called before we begin deconstruct_jointree. Once we begin + * deconstruct_jointree, all active placeholders must be present in + * root->placeholder_list, because make_outerjoininfo and + * update_placeholder_eval_levels require this info to be available + * while we crawl up the join tree. */ void find_placeholders_in_jointree(PlannerInfo* root) { @@ -210,7 +230,7 @@ static void find_placeholders_in_expr(PlannerInfo *root, Node *expr) * The initial eval_at level set by find_placeholder_info was the set of * rels used in the placeholder's expression (or the whole subselect below * the placeholder's syntactic location, if the expr is variable-free). - * If the subselect contains any outer joins that can null any of those rels, + * If the query contains any outer joins that can null any of those rels, * we must delay evaluation to above those joins. * * We repeat this operation each time we add another outer join to @@ -279,6 +299,9 @@ void update_placeholder_eval_levels(PlannerInfo* root, SpecialJoinInfo* new_sjin } } while (found_some); + /* Can't move the PHV's eval_at level to above its syntactic level */ + AssertEreport(bms_is_subset(eval_at, syn_level), MOD_OPT, ""); + phinfo->ph_eval_at = eval_at; } } @@ -289,28 +312,25 @@ void update_placeholder_eval_levels(PlannerInfo* root, SpecialJoinInfo* new_sjin * * This is called after we've finished determining the eval_at levels for * all placeholders. We need to make sure that all vars and placeholders - * needed to evaluate each placeholder will be available at the join level - * where the evaluation will be done. Note that this loop can have - * side-effects on the ph_needed sets of other PlaceHolderInfos; that's okay - * because we don't examine ph_needed here, so there are no ordering issues - * to worry about. + * needed to evaluate each placeholder will be available at the scan or join + * level where the evaluation will be done. (It might seem that scan-level + * evaluations aren't interesting, but that's not so: a LATERAL reference + * within a placeholder's expression needs to cause the referenced var or + * placeholder to be marked as needed in the scan where it's evaluated.) + * Note that this loop can have side-effects on the ph_needed sets of other + * PlaceHolderInfos; that's okay because we don't examine ph_needed here, so + * there are no ordering issues to worry about. */ void fix_placeholder_input_needed_levels(PlannerInfo* root) { ListCell* lc = NULL; foreach (lc, root->placeholder_list) { - PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(lc); - Relids eval_at = phinfo->ph_eval_at; - - /* No work unless it'll be evaluated above baserel level */ - if (bms_membership(eval_at) == BMS_MULTIPLE) { - List* vars = - pull_var_clause((Node*)phinfo->ph_var->phexpr, PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); - - add_vars_to_targetlist(root, vars, eval_at, false); - list_free_ext(vars); - } + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); + List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr, PVC_RECURSE_AGGREGATES, + PVC_INCLUDE_PLACEHOLDERS); + add_vars_to_targetlist(root, vars, phinfo->ph_eval_at, false); + list_free(vars); } } @@ -329,17 +349,40 @@ void add_placeholders_to_base_rels(PlannerInfo* root) { ListCell* lc = NULL; - foreach (lc, root->placeholder_list) { - PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(lc); - Relids eval_at = phinfo->ph_eval_at; - - if (bms_membership(eval_at) == BMS_SINGLETON && - bms_nonempty_difference(phinfo->ph_needed, eval_at)) { + foreach(lc, root->placeholder_list) { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); + Relids eval_at = phinfo->ph_eval_at; + + if (bms_membership(eval_at) == BMS_SINGLETON) { int varno = bms_singleton_member(eval_at); - RelOptInfo* rel = find_base_rel(root, varno); - - rel->reltarget->exprs = lappend(rel->reltarget->exprs, copyObject(phinfo->ph_var)); - /* reltarget's cost and width fields will be updated later */ + RelOptInfo *rel = find_base_rel(root, varno); + + /* add it to reltargetlist if needed above the rel scan level */ + if (bms_nonempty_difference(phinfo->ph_needed, eval_at)) + rel->reltarget->exprs = lappend(rel->reltarget->exprs, copyObject(phinfo->ph_var)); + /* if there are lateral refs in it, add them to lateral_vars */ + if (phinfo->ph_lateral != NULL) { + List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr, + PVC_RECURSE_AGGREGATES, PVC_INCLUDE_PLACEHOLDERS); + ListCell *lc2; + foreach(lc2, vars) { + Node *node = (Node *) lfirst(lc2); + if (IsA(node, Var)) { + Var *var = (Var *) node; + if (var->varno != varno) + rel->lateral_vars = lappend(rel->lateral_vars, var); + } else if (IsA(node, PlaceHolderVar)) { + PlaceHolderVar *other_phv = (PlaceHolderVar *) node; + PlaceHolderInfo *other_phi; + + other_phi = find_placeholder_info(root, other_phv, false); + if (!bms_is_subset(other_phi->ph_eval_at, eval_at)) + rel->lateral_vars = lappend(rel->lateral_vars, other_phv); + } else + Assert(false); + } + list_free(vars); + } } } } @@ -355,47 +398,60 @@ void add_placeholders_to_joinrel(PlannerInfo* root, RelOptInfo* joinrel, RelOptInfo* outer_rel, RelOptInfo* inner_rel) { Relids relids = joinrel->relids; - ListCell* lc = NULL; - - foreach (lc, root->placeholder_list) { - PlaceHolderInfo* phinfo = (PlaceHolderInfo*)lfirst(lc); - - /* Is it still needed above this joinrel? */ - if (bms_nonempty_difference(phinfo->ph_needed, relids)) { - /* Is it computable here? */ - if (bms_is_subset(phinfo->ph_eval_at, relids)) { + ListCell *lc; + + foreach(lc, root->placeholder_list) { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); + + /* Is it computable here? */ + if (bms_is_subset(phinfo->ph_eval_at, relids)) { + /* Is it still needed above this joinrel? */ + if (bms_nonempty_difference(phinfo->ph_needed, relids)) { /* Yup, add it to the output */ - joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, phinfo->ph_var); - /* Vars have cost zero, so no need to adjust reltarget->cost */ + joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, + phinfo->ph_var); joinrel->reltarget->width += phinfo->ph_width; - - if (root->parse->is_flt_frame) { - /* - * Charge the cost of evaluating the contained expression if - * the PHV can be computed here but not in either input. This - * is a bit bogus because we make the decision based on the - * first pair of possible input relations considered for the - * joinrel. With other pairs, it might be possible to compute - * the PHV in one input or the other, and then we'd be double - * charging the PHV's cost for some join paths. For now, live - * with that; but we might want to improve it later by - * refiguring the reltarget costs for each pair of inputs. - */ - if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) && - !bms_is_subset(phinfo->ph_eval_at, inner_rel->relids)) { - QualCost cost; - - cost_qual_eval_node(&cost, (Node *)phinfo->ph_var->phexpr, root); - joinrel->reltarget->cost.startup += cost.startup; - joinrel->reltarget->cost.per_tuple += cost.per_tuple; - } - } - - if (root->glob->vectorized) { - joinrel->encodedwidth += columnar_get_col_width(exprType((Node*)phinfo->ph_var), phinfo->ph_width); - joinrel->encodednum++; + + /* + * Charge the cost of evaluating the contained expression if + * the PHV can be computed here but not in either input. This + * is a bit bogus because we make the decision based on the + * first pair of possible input relations considered for the + * joinrel. With other pairs, it might be possible to compute + * the PHV in one input or the other, and then we'd be double + * charging the PHV's cost for some join paths. For now, live + * with that; but we might want to improve it later by + * refiguring the reltarget costs for each pair of inputs. + */ + if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) && + !bms_is_subset(phinfo->ph_eval_at, inner_rel->relids)) { + QualCost cost; + + cost_qual_eval_node(&cost, (Node *) phinfo->ph_var->phexpr, + root); + joinrel->reltarget->cost.startup += cost.startup; + joinrel->reltarget->cost.per_tuple += cost.per_tuple; } } + + /* + * Also adjust joinrel's direct_lateral_relids to include the + * PHV's source rel(s). We must do this even if we're not + * actually going to emit the PHV, otherwise join_is_legal() will + * reject valid join orderings. (In principle maybe we could + * instead remove the joinrel's lateral_relids dependency; but + * that's complicated to get right, and cases where we're not + * going to emit the PHV are too rare to justify the work.) + * + * In principle we should only do this if the join doesn't yet + * include the PHV's source rel(s). But our caller + * build_join_rel() will clean things up by removing the join's + * own relids from its direct_lateral_relids, so we needn't + * account for that here. + */ + joinrel->direct_lateral_relids = + bms_add_members(joinrel->direct_lateral_relids, + phinfo->ph_lateral); } } } diff --git a/src/gausskernel/optimizer/util/relnode.cpp b/src/gausskernel/optimizer/util/relnode.cpp index 3dd1e2bd2..ae20e10dc 100755 --- a/src/gausskernel/optimizer/util/relnode.cpp +++ b/src/gausskernel/optimizer/util/relnode.cpp @@ -217,6 +217,7 @@ RelOptInfo* build_simple_rel(PlannerInfo* root, int relid, RelOptKind reloptkind /* min_attr, max_attr, attr_needed, attr_widths are set below */ rel->lateral_vars = NIL; rel->lateral_relids = NULL; + rel->lateral_referencers = NULL; rel->indexlist = NIL; rel->pages = 0; rel->tuples = 0; @@ -713,6 +714,7 @@ RelOptInfo* build_join_rel(PlannerInfo* root, Relids joinrelids, RelOptInfo* out joinrel->attr_widths = NULL; joinrel->lateral_vars = NIL; joinrel->lateral_relids = NULL; + joinrel->lateral_referencers = NULL; joinrel->indexlist = NIL; joinrel->pages = 0.0; joinrel->tuples = 0; diff --git a/src/gausskernel/optimizer/util/restrictinfo.cpp b/src/gausskernel/optimizer/util/restrictinfo.cpp index bb30bfd8d..ab7153af0 100644 --- a/src/gausskernel/optimizer/util/restrictinfo.cpp +++ b/src/gausskernel/optimizer/util/restrictinfo.cpp @@ -588,21 +588,29 @@ void extract_actual_join_clauses(List* restrictinfo_list, List** joinquals, List * Also there must not be an outer join below the clause that would null the * Vars coming from the target relation. Otherwise the clause might give * results different from what it would give at its normal semantic level. + * + * Also, the join clause must not use any relations that have LATERAL + * references to the target relation, since we could not put such rels on + * the outer side of a nestloop with the target relation. */ -bool join_clause_is_movable_to(RestrictInfo* rinfo, Index baserelid) +bool join_clause_is_movable_to(RestrictInfo* rinfo, RelOptInfo *baserel) { /* Clause must physically reference target rel */ - if (!bms_is_member(baserelid, rinfo->clause_relids)) + if (!bms_is_member(baserel->relid, rinfo->clause_relids)) return false; - + /* Cannot move an outer-join clause into the join's outer side */ - if (bms_is_member(baserelid, rinfo->outer_relids)) + if (bms_is_member(baserel->relid, rinfo->outer_relids)) return false; - + /* Target rel must not be nullable below the clause */ - if (bms_is_member(baserelid, rinfo->nullable_relids)) + if (bms_is_member(baserel->relid, rinfo->nullable_relids)) return false; - + + /* Clause must not use any rels with LATERAL references to this rel */ + if (bms_overlap(baserel->lateral_referencers, rinfo->clause_relids)) + return false; + return true; } diff --git a/src/gausskernel/optimizer/util/var.cpp b/src/gausskernel/optimizer/util/var.cpp index 81d125b79..a7d8819ee 100644 --- a/src/gausskernel/optimizer/util/var.cpp +++ b/src/gausskernel/optimizer/util/var.cpp @@ -190,20 +190,29 @@ static bool pull_varnos_walker(Node* node, pull_varnos_context* context) } if (IsA(node, PlaceHolderVar)) { /* - * Normally, we can just take the varnos in the contained expression. - * But if it is variable-free, use the PHV's syntactic relids. + * A PlaceHolderVar acts as a variable of its syntactic scope, or + * lower than that if it references only a subset of the rels in its + * syntactic scope. It might also contain lateral references, but we + * should ignore such references when computing the set of varnos in + * an expression tree. Also, if the PHV contains no variables within + * its syntactic scope, it will be forced to be evaluated exactly at + * the syntactic scope, so take that as the relid set. */ PlaceHolderVar* phv = (PlaceHolderVar*)node; pull_varnos_context subcontext; - + subcontext.varnos = NULL; subcontext.sublevels_up = context->sublevels_up; - (void)pull_varnos_walker((Node*)phv->phexpr, &subcontext); - - if (bms_is_empty(subcontext.varnos) && phv->phlevelsup == (unsigned int)context->sublevels_up) - context->varnos = bms_add_members(context->varnos, phv->phrels); - else - context->varnos = bms_join(context->varnos, subcontext.varnos); + (void) pull_varnos_walker((Node *) phv->phexpr, &subcontext); + + if (phv->phlevelsup == context->sublevels_up) { + subcontext.varnos = bms_int_members(subcontext.varnos, + phv->phrels); + if (bms_is_empty(subcontext.varnos)) + context->varnos = bms_add_members(context->varnos, + phv->phrels); + } + context->varnos = bms_join(context->varnos, subcontext.varnos); return false; } if (IsA(node, SubLink) && context->isSkipSublink) { diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index e0d0f3b86..00a09a264 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -5918,17 +5918,9 @@ static Datum ExecEvalCursorExpression(CursorExpressionState* state, ExprContext* PortalStart(portal, econtext->ecxt_param_list_info, 0, GetActiveSnapshot()); - int plan_param_number = ((PlannedStmt*)(cursor_expression->plan))->nParamExec; int state_param_number = list_length(state->param); - if (unlikely(plan_param_number > state_param_number)) { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmodule(MOD_EXECUTOR), - errmsg("The expected number of parameters is %d, but actual is %d.", plan_param_number, state_param_number))); - } - - for (int i = 0; i < plan_param_number; i++) { + for (int i = 0; i < state_param_number; i++) { bool expr_is_null = false; ParamExecData* prm = &(portal->queryDesc->estate->es_param_exec_vals[i]); ExprState* expr_state = (ExprState*)list_nth(state->param, i); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 518beb2f7..7744259e1 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -158,6 +158,7 @@ extern const uint32 PARTITION_NAME_VERSION_NUM; extern const uint32 OBJECT_TYPE_VERSION_NUM; extern const uint32 MINMAXEXPR_CMPTYPE_VERSION_NUM; extern const uint32 CHARBYTE_SEMANTIC_VERSION_NUMBER; +extern const uint32 APPLY_JOIN_VERSION_NUMBER; extern void register_backend_version(uint32 backend_version); extern bool contain_backend_version(uint32 version_number); diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index 49cfefa5c..b5d0cdf72 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -400,6 +400,21 @@ extern List* list_insert_nth_oid(List *list, int pos, Oid datum); #define listCopy(list) list_copy(list) + +/* + * foreach_delete_current - + * delete the current list element from the List associated with a + * surrounding foreach() loop, returning the new List pointer. + * + * This is equivalent to list_delete_cell(), but it also adjusts the foreach + * loop's state so that no list elements will be missed. Do not delete + * elements from an active foreach loop's list in any other way! + */ +#define foreach_delete_current(lst, cell) \ + (cell##__state.i--, \ + (List *) (cell##__state.l = list_delete_cell(lst, cell))) + + extern int length(List* list); #endif /* ENABLE_LIST_COMPAT */ diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 35f6156cc..52fd0b6a4 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1415,6 +1415,7 @@ typedef struct JoinExpr { Alias* alias; /* user-written alias clause, if any */ int rtindex; /* RT index assigned for join, or 0 */ bool is_straight_join; /* set true if straight_join */ + bool is_apply_join; /* set true when on cross apply or outer apply join */ } JoinExpr; /* ---------- diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index a2518f219..f7dcd23f5 100755 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -781,6 +781,11 @@ 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 */ + /* information about a base rel (not set for join rels!) */ Index relid; Oid reltablespace; /* containing tablespace */ @@ -791,6 +796,7 @@ typedef struct RelOptInfo { int32* attr_widths; /* array indexed [min_attr .. max_attr] */ List* lateral_vars; /* LATERAL Vars and PHVs referenced by rel */ Relids lateral_relids; /* minimum parameterization of rel */ + Relids lateral_referencers; /* rels that reference me laterally */ List* indexlist; /* list of IndexOptInfo */ #ifndef ENABLE_MULTIPLE_NODES @@ -1977,9 +1983,9 @@ typedef struct SpecialJoinInfo { */ typedef struct LateralJoinInfo { - NodeTag type; - Index lateral_rhs; /* a baserel containing lateral refs */ - Relids lateral_lhs; /* some base relids it references */ + NodeTag type; + Relids lateral_lhs; /* some base relids it references */ + Relids lateral_rhs; /* some base relids it references */ } LateralJoinInfo; @@ -2099,6 +2105,7 @@ typedef struct PlaceHolderInfo { Index phid; /* ID for PH (unique within planner run) */ PlaceHolderVar* ph_var; /* copy of PlaceHolderVar tree */ Relids ph_eval_at; /* lowest level we can evaluate value at */ + Relids ph_lateral; /* relids of contained lateral refs, if any */ Relids ph_needed; /* highest level the value is needed at */ int32 ph_width; /* estimated attribute width */ } PlaceHolderInfo; diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index a79d8df0d..b10718b18 100755 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -67,7 +67,7 @@ extern Plan* optimize_minmax_aggregates( */ extern void set_plan_rows(Plan* plan, double globalRows, double multiple = 1.0); extern Plan* create_plan(PlannerInfo* root, Path* best_path); -extern void disuse_physical_tlist(Plan* plan, Path* path); +extern void disuse_physical_tlist(PlannerInfo *root, Plan* plan, Path* path); extern void copy_plan_costsize(Plan* dest, Plan* src); extern SubqueryScan* make_subqueryscan(List* qptlist, List* qpqual, Index scanrelid, Plan* subplan); extern ForeignScan* make_foreignscan(List* qptlist, List* qpqual, Index scanrelid, List* fdw_exprs, List* fdw_private, @@ -157,7 +157,7 @@ extern void build_base_rel_tlists(PlannerInfo* root, List* final_tlist); extern void add_vars_to_targetlist(PlannerInfo* root, List* vars, Relids where_needed, bool create_new_ph); extern void find_lateral_references(PlannerInfo *root); extern void create_lateral_join_info(PlannerInfo *root); -extern void add_lateral_info(PlannerInfo *root, Index rhs, Relids lhs); +extern void add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs); extern List* deconstruct_jointree(PlannerInfo* root, Relids* non_keypreserved = NULL); extern void distribute_restrictinfo_to_rels(PlannerInfo* root, RestrictInfo* restrictinfo); extern void process_security_clause_appendrel(PlannerInfo *root); diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h index 1c793525f..94140e7b3 100644 --- a/src/include/optimizer/restrictinfo.h +++ b/src/include/optimizer/restrictinfo.h @@ -29,7 +29,7 @@ extern List* get_actual_clauses(List* restrictinfo_list); extern List* get_all_actual_clauses(List* restrictinfo_list); extern List* extract_actual_clauses(List* restrictinfo_list, bool pseudoconstant); extern void extract_actual_join_clauses(List* restrictinfo_list, List** joinquals, List** otherquals); -extern bool join_clause_is_movable_to(RestrictInfo* rinfo, Index baserelid); +extern bool join_clause_is_movable_to(RestrictInfo* rinfo, RelOptInfo *baserel); extern bool join_clause_is_movable_into(RestrictInfo* rinfo, Relids currentrelids, Relids current_and_outer); extern void initialize_bucket_size(RestrictInfo* info); #endif /* RESTRICTINFO_H */ diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 12bcca37a..03094fcf1 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -47,6 +47,7 @@ PG_KEYWORD("and", AND, RESERVED_KEYWORD) PG_KEYWORD("any", ANY, RESERVED_KEYWORD) PG_KEYWORD("app", APP, UNRESERVED_KEYWORD) PG_KEYWORD("append", APPEND, UNRESERVED_KEYWORD) +PG_KEYWORD("apply", APPLY, UNRESERVED_KEYWORD) PG_KEYWORD("archive", ARCHIVE, UNRESERVED_KEYWORD) PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD) PG_KEYWORD("as", AS, RESERVED_KEYWORD) @@ -359,6 +360,7 @@ PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD) PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD) PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD) PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD) +PG_KEYWORD("lateral", LATERAL_P, UNRESERVED_KEYWORD) PG_KEYWORD("lc_collate", LC_COLLATE_P, UNRESERVED_KEYWORD) PG_KEYWORD("lc_ctype", LC_CTYPE_P, UNRESERVED_KEYWORD) PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD) diff --git a/src/test/regress/expected/sytcomp_del_upt4orderby.out b/src/test/regress/expected/sytcomp_del_upt4orderby.out index 96a71c969..e085a489f 100644 --- a/src/test/regress/expected/sytcomp_del_upt4orderby.out +++ b/src/test/regress/expected/sytcomp_del_upt4orderby.out @@ -154,15 +154,15 @@ explain WITH max_table as ( ) DELETE FROM sytc_t1 WHERE f1 = (SELECT mx FROM max_table where max_table.f1 = sytc_t1.f1) order by f2 desc limit 5; QUERY PLAN ------------------------------------------------------------------------------------------------------------- - Delete on sytc_t1 (cost=2575.48..2575.49 rows=5 width=14) + Delete on sytc_t1 (cost=2575.48..2575.49 rows=5 width=10) CTE max_table -> Limit (cost=0.00..28.72 rows=20 width=12) -> GroupAggregate (cost=0.00..287.15 rows=200 width=12) Group By Key: sytc_t2.f1 -> Index Scan Backward using sytc_t2_idx on sytc_t2 (cost=0.00..257.93 rows=5445 width=8) - -> Sort (cost=2546.76..2546.83 rows=27 width=14) + -> Sort (cost=2546.76..2546.83 rows=27 width=10) Sort Key: sytc_t1.f2 DESC - -> Seq Scan on sytc_t1 (cost=0.00..2546.31 rows=27 width=14) + -> Seq Scan on sytc_t1 (cost=0.00..2546.31 rows=27 width=10) Filter: (f1 = (SubPlan 2)) SubPlan 2 -> CTE Scan on max_table (cost=0.00..0.45 rows=1 width=4) diff --git a/src/test/regress/input/lateral.source b/src/test/regress/input/lateral.source new file mode 100644 index 000000000..d802a1789 --- /dev/null +++ b/src/test/regress/input/lateral.source @@ -0,0 +1,614 @@ +drop schema if exists lateral_test; +create schema lateral_test; +set search_path=lateral_test; + +CREATE TABLE tenk1 ( + unique1 int4, + unique2 int4, + two int4, + four int4, + ten int4, + twenty int4, + hundred int4, + thousand int4, + twothousand int4, + fivethous int4, + tenthous int4, + odd int4, + even int4, + stringu1 name, + stringu2 name, + string4 name +); + +COPY tenk1 FROM '@abs_srcdir@/data/tenk.data'; + +CREATE TABLE INT4_TBL(f1 int4); + +INSERT INTO INT4_TBL(f1) VALUES + (' 0 '), + ('123456 '), + (' -123456'), + ('2147483647'), -- largest and smallest values + ('-2147483647'); +VACUUM INT4_TBL; + + +CREATE TABLE INT8_TBL(q1 int8, q2 int8); + +INSERT INTO INT8_TBL VALUES + (' 123 ',' 456'), + ('123 ','4567890123456789'), + ('4567890123456789','123'), + (+4567890123456789,'4567890123456789'), + ('+4567890123456789','-4567890123456789'); +VACUUM INT8_TBL; + +CREATE TABLE INT2_TBL(f1 int2); + +INSERT INTO INT2_TBL(f1) VALUES + ('0 '), + (' 1234 '), + (' -1234'), + ('32767'), -- largest and smallest values + ('-32767'); +VACUUM INT2_TBL; + +-- +-- Test LATERAL +-- + +select unique2, x.* +from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; +explain (costs off) + select unique2, x.* + from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; +select unique2, x.* +from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; +explain (costs off) + select unique2, x.* + from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; +explain (costs off) + select unique2, x.* + from int4_tbl x cross join lateral (select unique2 from tenk1 where f1 = unique1) ss; +select unique2, x.* +from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; +explain (costs off) + select unique2, x.* + from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; + +-- check scoping of lateral versus parent references +-- the first of these should return int8_tbl.q2, the second int8_tbl.q1 +select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl; +select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl; + +-- lateral with function in FROM +select count(*) from tenk1 a, lateral generate_series(1,two) g; +explain (costs off) + select count(*) from tenk1 a, lateral generate_series(1,two) g; +explain (costs off) + select count(*) from tenk1 a cross join lateral generate_series(1,two) g; +-- don't need the explicit LATERAL keyword for functions +explain (costs off) + select count(*) from tenk1 a, generate_series(1,two) g; + +-- lateral with UNION ALL subselect +explain (costs off) + select * from generate_series(100,200) g, + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss; +select * from generate_series(100,200) g, + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss; + +-- lateral with VALUES +explain (costs off) + select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; + +-- lateral with VALUES, no flattening possible +explain (costs off) + select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; + +-- lateral injecting a strange outer join condition +explain (costs off) + select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; +select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; + +-- lateral reference to a join alias variable +select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (select x) ss2(y); +select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (values(x)) ss2(y); +select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j, + lateral (select x) ss2(y); + +-- lateral references requiring pullup +select * from (values(1)) x(lb), + lateral generate_series(lb,4) x4; +select * from (select f1/1000000000 from int4_tbl) x(lb), + lateral generate_series(lb,4) x4; +select * from (values(1)) x(lb), + lateral (values(lb)) y(lbcopy); +select * from (values(1)) x(lb), + lateral (select lb from int4_tbl) y(lbcopy); +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2); +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); +select x.* from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); +select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); +select v.* from + (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + +explain (verbose, costs off) +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; +explain (verbose, costs off) +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; + +-- lateral can result in join conditions appearing below their +-- real semantic level +explain (verbose, costs off) +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; +explain (verbose, costs off) +select * from int4_tbl i left join + lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; +select * from int4_tbl i left join + lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; +explain (verbose, costs off) +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; + +-- lateral reference in a PlaceHolderVar evaluated at join level +explain (verbose, costs off) +select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; +select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; + +-- case requiring nested PlaceHolderVars +explain (verbose, costs off) +select * from + int8_tbl c left join ( + int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select ss2.y offset 0) ss3; + +-- case that breaks the old ph_may_need optimization +explain (verbose, costs off) +select c.*,a.*,ss1.q1,ss2.q1,ss3.* from + int8_tbl c left join ( + int8_tbl a left join + (select q1, coalesce(q2,f1) as x from int8_tbl b, int4_tbl b2 + where q1 < f1) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select * from int4_tbl i where ss2.y > f1) ss3; + +-- check processing of postponed quals (bug #9041) +explain (verbose, costs off) +select * from + (select 1 as x offset 0) x cross join (select 2 as y offset 0) y + left join lateral ( + select * from (select 3 as z offset 0) z where z.z = x.x + ) zz on zz.z = y.y; + +-- a new postponed-quals issue (bug #17768) +explain (costs off) +select * from int4_tbl t1, + lateral (select * from int4_tbl t2 inner join int4_tbl t3 on t1.f1 = 1 + inner join (int4_tbl t4 left join int4_tbl t5 on true) on true) ss; + +-- check dummy rels with lateral references (bug #15694) +explain (verbose, costs off) +select * from int8_tbl i8 left join lateral + (select *, i8.q2 from int4_tbl where false) ss on true; +explain (verbose, costs off) +select * from int8_tbl i8 left join lateral + (select *, i8.q2 from int4_tbl i1, int4_tbl i2 where false) ss on true; + +-- check handling of nested appendrels inside LATERAL +select * from + ((select 2 as v) union all (select 3 as v)) as q1 + cross join lateral + ((select * from + ((select 4 as v) union all (select 5 as v)) as q3) + union all + (select q1.v) + ) as q2; + +-- check the number of columns specified +SELECT * FROM (int8_tbl i cross join int4_tbl j) ss(a,b,c,d); + +-- check we don't try to do a unique-ified semijoin with LATERAL +explain (verbose, costs off) +select * from + (values (0,9998), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; +select * from + (values (0,9998), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; + +-- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, +-- but we can make the test case much more compact with LATERAL) +explain (verbose, costs off) +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + +-- test some error cases where LATERAL should have been used but wasn't +select f1,g from int4_tbl a, (select f1 as g) ss; +select f1,g from int4_tbl a, (select a.f1 as g) ss; +select f1,g from int4_tbl a cross join (select f1 as g) ss; +select f1,g from int4_tbl a cross join (select a.f1 as g) ss; +-- SQL:2008 says the left table is in scope but illegal to access here +select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true; +select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true; +-- check we complain about ambiguous table references +select * from + int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss); +-- LATERAL can be used to put an aggregate into the FROM clause of its query +select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss; + +-- check behavior of LATERAL in UPDATE/DELETE + +create temp table xx1 as select f1 as x1, -f1 as x2 from int4_tbl; + +-- error, can't do this: +update xx1 set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; +update xx1 set x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss; +-- can't do it even with LATERAL: +update xx1 set x2 = f1 from lateral (select * from int4_tbl where f1 = x1) ss; +-- we might in future allow something like this, but for now it's an error: +update xx1 set x2 = f1 from xx1, lateral (select * from int4_tbl where f1 = x1) ss; + +-- also errors: +delete from xx1 using (select * from int4_tbl where f1 = x1) ss; +delete from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss; +delete from xx1 using lateral (select * from int4_tbl where f1 = x1) ss; + +-- +-- test LATERAL reference propagation down a multi-level inheritance hierarchy +-- produced for a multi-level partitioned table hierarchy. +-- +create table join_pt1 (a int, b int, c varchar) partition by range(a)( +PARTITION P1 VALUES LESS THAN(100), PARTITION P2 VALUES LESS THAN(200), PARTITION P3 VALUES LESS THAN(MAXVALUE)); + + +insert into join_pt1 values (1, 1, 'x'), (101, 101, 'y'); +create table join_ut1 (a int, b int, c varchar); +insert into join_ut1 values (101, 101, 'y'), (2, 2, 'z'); + +explain (verbose, costs off) +select t1.b, ss.phv from join_ut1 t1 left join lateral + (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv + from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss + on t1.a = ss.t2a order by t1.a; +select t1.b, ss.phv from join_ut1 t1 left join lateral + (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv + from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss + on t1.a = ss.t2a order by t1.a; + + + +-- cross apply +create table departments(department_name varchar(50), department_id int); +create table employees(employee_id int, department_id int, last_name varchar(50)); + + +insert into departments values ('Marketing', 1); +insert into departments values ('Public Relations', 2); +insert into departments values ('Operations', 3); +insert into departments values ('Develop', 4); +insert into departments values ('Research', 5); +insert into departments values ('CEO', 6); +insert into departments values ('CFO', 7); + + +insert into employees values(1, 1, 'zhangsan1'); +insert into employees values(2, 1, 'zhangsan2'); +insert into employees values(3, 1, 'zhangsan3'); +insert into employees values(4, 1, 'zhangsan4'); + + +insert into employees values(5, 2, 'lisi1'); +insert into employees values(6, 2, 'lisi2'); +insert into employees values(7, 2, 'lisi3'); +insert into employees values(8, 2, 'lisi4'); + +insert into employees values(9, 3, 'wangwu1'); +insert into employees values(10, 3, 'wangwu2'); +insert into employees values(11, 3, 'wangwu3'); +insert into employees values(12, 3, 'wangwu4'); + +insert into employees values(13, 4, 'heliu1'); +insert into employees values(14, 4, 'heliu2'); +insert into employees values(15, 4, 'heliu3'); +insert into employees values(16, 4, 'heliu4'); + +insert into employees values(17, 5, 'chenqi1'); +insert into employees values(18, 5, 'chenqi2'); +insert into employees values(19, 5, 'chenqi3'); +insert into employees values(20, 5, 'chenqi4'); + +create function fn_salar (departmentid int) returns table (employee_id int, department_id int, last_name varchar) language sql as 'select employee_id, department_id, concat(last_name,last_name) as last_name2 from employees WHERE department_id = departmentid'; + + +select* from departments d cross apply employees e where e.department_id = d.department_id; + +select* from departments d cross apply (select d.department_id from employees x) e where e.department_id = d.department_id; + +SELECT * FROM employees AS e CROSS APPLY fn_Salar(e.department_id) AS f; + + +SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +select* from departments d outer apply employees e where e.department_id = d.department_id; + +select* from departments d cross apply (select d.department_id from employees x) e where e.department_id = d.department_id; + +SELECT * FROM employees AS e outer APPLY fn_Salar(e.department_id) AS f; + +SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +-- view + +create view v1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +create view v2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +create view v3 as select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + + +create view v4 as select count(*) from tenk1 a, lateral generate_series(1,two) g; + +select * from v1; +select * from v2; +select * from v3; +select * from v4; + + +-- plsql +create or replace procedure plpgsql_1 (param1 varchar, param2 varchar, param3 varchar, param4 varchar ) +IS + BEGIN + create table tt1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN (param1, param2, param3) + ORDER BY d.department_name, v.employee_id; +END; +/ + +create or replace procedure plpgsql_2 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d OUTER APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN (param1, param2, param3) + ORDER BY d.department_name, v.employee_id; +END; +/ + +create or replace procedure plpgsql_3 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt3 as select id from (values (0), (1)) v(id), + lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; +END; +/ + +create or replace procedure plpgsql_4 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt4 as select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; +END; +/ + +call plpgsql_1(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); +call plpgsql_2(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); +call plpgsql_3(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); +call plpgsql_4(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + + +select * from tt1; +select * from tt2; +select * from tt3; +select * from tt4; + +drop PROCEDURE plpgsql_1; +drop PROCEDURE plpgsql_2; +drop PROCEDURE plpgsql_3; +drop PROCEDURE plpgsql_4; + + +-- cursor expression +create or replace procedure test_cursor_1 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT e.last_name, CURSOR(SELECT d.department_name FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') ORDER BY d.department_name, v.employee_id) abc FROM employees e; +begin + OPEN c1; + loop + fetch c1 into company_name, my_cur; + exit when c1%notfound; + raise notice 'company_name : % %',company_name, my_cur; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_1(); +drop procedure test_cursor_1; + + +create or replace procedure test_cursor_2 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT e.last_name, CURSOR(SELECT d.department_name FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') ORDER BY d.department_name, v.employee_id) abc FROM employees e; +begin + OPEN c1; + loop + fetch c1 into company_name, my_cur; + exit when c1%notfound; + raise notice 'company_name : % %',company_name, my_cur; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_2(); +drop procedure test_cursor_2; + + +create or replace procedure test_cursor_3 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT CURSOR(select xq1::varchar from int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2)) abc; + begin + OPEN c1; + loop + fetch c1 into my_cur; + exit when c1%notfound; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_3(); +drop procedure test_cursor_3; + + +drop table tt1; +drop table tt2; +drop table tt3; +drop table tt4; + +drop function fn_salar; +drop view v1; +drop view v2; +drop view v3; +drop view v4; +drop table tenk1; +drop table INT4_TBL; +drop table INT8_TBL; +drop table INT2_TBL; +drop table departments; +drop table employees; + +-- clean +drop schema if exists lateral_test cascade; \ No newline at end of file diff --git a/src/test/regress/input/lateral_dump.source b/src/test/regress/input/lateral_dump.source new file mode 100644 index 000000000..02ac5ef54 --- /dev/null +++ b/src/test/regress/input/lateral_dump.source @@ -0,0 +1,132 @@ +create database lateral_dump; +\c lateral_dump +create schema lateral_dump; +set search_path to lateral_dump; + + +CREATE TABLE tenk1 ( + unique1 int4, + unique2 int4, + two int4, + four int4, + ten int4, + twenty int4, + hundred int4, + thousand int4, + twothousand int4, + fivethous int4, + tenthous int4, + odd int4, + even int4, + stringu1 name, + stringu2 name, + string4 name +); + + +CREATE TABLE INT4_TBL(f1 int4); + +INSERT INTO INT4_TBL(f1) VALUES + (' 0 '), + ('123456 '), + (' -123456'), + ('2147483647'), -- largest and smallest values + ('-2147483647'); +VACUUM INT4_TBL; + + +CREATE TABLE INT8_TBL(q1 int8, q2 int8); + +INSERT INTO INT8_TBL VALUES + (' 123 ',' 456'), + ('123 ','4567890123456789'), + ('4567890123456789','123'), + (+4567890123456789,'4567890123456789'), + ('+4567890123456789','-4567890123456789'); +VACUUM INT8_TBL; + +CREATE TABLE INT2_TBL(f1 int2); + +INSERT INTO INT2_TBL(f1) VALUES + ('0 '), + (' 1234 '), + (' -1234'), + ('32767'), -- largest and smallest values + ('-32767'); +VACUUM INT2_TBL; + +create table departments(department_name varchar(50), department_id int); +create table employees(employee_id int, department_id int, last_name varchar(50)); + + +insert into departments values ('Marketing', 1); +insert into departments values ('Public Relations', 2); +insert into departments values ('Operations', 3); +insert into departments values ('Develop', 4); +insert into departments values ('Research', 5); +insert into departments values ('CEO', 6); +insert into departments values ('CFO', 7); + + +insert into employees values(1, 1, 'zhangsan1'); +insert into employees values(2, 1, 'zhangsan2'); +insert into employees values(3, 1, 'zhangsan3'); +insert into employees values(4, 1, 'zhangsan4'); + + +insert into employees values(5, 2, 'lisi1'); +insert into employees values(6, 2, 'lisi2'); +insert into employees values(7, 2, 'lisi3'); +insert into employees values(8, 2, 'lisi4'); + +insert into employees values(9, 3, 'wangwu1'); +insert into employees values(10, 3, 'wangwu2'); +insert into employees values(11, 3, 'wangwu3'); +insert into employees values(12, 3, 'wangwu4'); + +insert into employees values(13, 4, 'heliu1'); +insert into employees values(14, 4, 'heliu2'); +insert into employees values(15, 4, 'heliu3'); +insert into employees values(16, 4, 'heliu4'); + +insert into employees values(17, 5, 'chenqi1'); +insert into employees values(18, 5, 'chenqi2'); +insert into employees values(19, 5, 'chenqi3'); +insert into employees values(20, 5, 'chenqi4'); + + +create view v1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +create view v2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +create view v3 as select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + + +create view v4 as select count(*) from tenk1 a, lateral generate_series(1,two) g; + + +\! @abs_bindir@/gs_dump lateral_dump -p @portstring@ -f @abs_bindir@/lateral_dump.sql -n lateral_dump -w >/dev/null 2>&1; echo $? +drop schema lateral_dump cascade; +\! @abs_bindir@/gsql -dlateral_dump -p @portstring@ -f "@abs_bindir@/lateral_dump.sql"; + +-- lateral dump check + +select * from v1; +select * from v2; +select * from v3; +select * from v4; + +drop schema lateral_dump cascade; +\c postgres +drop database lateral_dump; diff --git a/src/test/regress/input/lateral_with_dop.source b/src/test/regress/input/lateral_with_dop.source new file mode 100644 index 000000000..37f1fa558 --- /dev/null +++ b/src/test/regress/input/lateral_with_dop.source @@ -0,0 +1,619 @@ +drop schema if exists lateral_with_dop_test; +create schema lateral_with_dop_test; +set search_path=lateral_with_dop_test; + +SET query_dop = 4; + + +CREATE TABLE tenk1 ( + unique1 int4, + unique2 int4, + two int4, + four int4, + ten int4, + twenty int4, + hundred int4, + thousand int4, + twothousand int4, + fivethous int4, + tenthous int4, + odd int4, + even int4, + stringu1 name, + stringu2 name, + string4 name +); + +COPY tenk1 FROM '@abs_srcdir@/data/tenk.data'; + +CREATE TABLE INT4_TBL(f1 int4); + +INSERT INTO INT4_TBL(f1) VALUES + (' 0 '), + ('123456 '), + (' -123456'), + ('2147483647'), -- largest and smallest values + ('-2147483647'); +VACUUM INT4_TBL; + + +CREATE TABLE INT8_TBL(q1 int8, q2 int8); + +INSERT INTO INT8_TBL VALUES + (' 123 ',' 456'), + ('123 ','4567890123456789'), + ('4567890123456789','123'), + (+4567890123456789,'4567890123456789'), + ('+4567890123456789','-4567890123456789'); +VACUUM INT8_TBL; + +CREATE TABLE INT2_TBL(f1 int2); + +INSERT INTO INT2_TBL(f1) VALUES + ('0 '), + (' 1234 '), + (' -1234'), + ('32767'), -- largest and smallest values + ('-32767'); +VACUUM INT2_TBL; + +-- +-- Test LATERAL +-- + +select unique2, x.* +from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; +explain (costs off) + select unique2, x.* + from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; +select unique2, x.* +from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; +explain (costs off) + select unique2, x.* + from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; +explain (costs off) + select unique2, x.* + from int4_tbl x cross join lateral (select unique2 from tenk1 where f1 = unique1) ss; +select unique2, x.* +from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; +explain (costs off) + select unique2, x.* + from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; + +-- check scoping of lateral versus parent references +-- the first of these should return int8_tbl.q2, the second int8_tbl.q1 +select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl; +select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl; + +-- lateral with function in FROM +select count(*) from tenk1 a, lateral generate_series(1,two) g; +explain (costs off) + select count(*) from tenk1 a, lateral generate_series(1,two) g; +explain (costs off) + select count(*) from tenk1 a cross join lateral generate_series(1,two) g; +-- don't need the explicit LATERAL keyword for functions +explain (costs off) + select count(*) from tenk1 a, generate_series(1,two) g; + +-- lateral with UNION ALL subselect +explain (costs off) + select * from generate_series(100,200) g, + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss; +select * from generate_series(100,200) g, + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss; + +-- lateral with VALUES +explain (costs off) + select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; + +-- lateral with VALUES, no flattening possible +explain (costs off) + select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; + +-- lateral injecting a strange outer join condition +explain (costs off) + select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; +select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; + +-- lateral reference to a join alias variable +select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (select x) ss2(y); +select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (values(x)) ss2(y); +select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j, + lateral (select x) ss2(y); + +-- lateral references requiring pullup +select * from (values(1)) x(lb), + lateral generate_series(lb,4) x4; +select * from (select f1/1000000000 from int4_tbl) x(lb), + lateral generate_series(lb,4) x4; +select * from (values(1)) x(lb), + lateral (values(lb)) y(lbcopy); +select * from (values(1)) x(lb), + lateral (select lb from int4_tbl) y(lbcopy); +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2); +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); +select x.* from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); +select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); +select v.* from + (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + +explain (verbose, costs off) +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; +explain (verbose, costs off) +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; + +-- lateral can result in join conditions appearing below their +-- real semantic level +explain (verbose, costs off) +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; +explain (verbose, costs off) +select * from int4_tbl i left join + lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; +select * from int4_tbl i left join + lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; +explain (verbose, costs off) +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; + +-- lateral reference in a PlaceHolderVar evaluated at join level +explain (verbose, costs off) +select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; +select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; + +-- case requiring nested PlaceHolderVars +explain (verbose, costs off) +select * from + int8_tbl c left join ( + int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select ss2.y offset 0) ss3; + +-- case that breaks the old ph_may_need optimization +explain (verbose, costs off) +select c.*,a.*,ss1.q1,ss2.q1,ss3.* from + int8_tbl c left join ( + int8_tbl a left join + (select q1, coalesce(q2,f1) as x from int8_tbl b, int4_tbl b2 + where q1 < f1) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select * from int4_tbl i where ss2.y > f1) ss3; + +-- check processing of postponed quals (bug #9041) +explain (verbose, costs off) +select * from + (select 1 as x offset 0) x cross join (select 2 as y offset 0) y + left join lateral ( + select * from (select 3 as z offset 0) z where z.z = x.x + ) zz on zz.z = y.y; + +-- a new postponed-quals issue (bug #17768) +explain (costs off) +select * from int4_tbl t1, + lateral (select * from int4_tbl t2 inner join int4_tbl t3 on t1.f1 = 1 + inner join (int4_tbl t4 left join int4_tbl t5 on true) on true) ss; + +-- check dummy rels with lateral references (bug #15694) +explain (verbose, costs off) +select * from int8_tbl i8 left join lateral + (select *, i8.q2 from int4_tbl where false) ss on true; +explain (verbose, costs off) +select * from int8_tbl i8 left join lateral + (select *, i8.q2 from int4_tbl i1, int4_tbl i2 where false) ss on true; + +-- check handling of nested appendrels inside LATERAL +select * from + ((select 2 as v) union all (select 3 as v)) as q1 + cross join lateral + ((select * from + ((select 4 as v) union all (select 5 as v)) as q3) + union all + (select q1.v) + ) as q2; + +-- check the number of columns specified +SELECT * FROM (int8_tbl i cross join int4_tbl j) ss(a,b,c,d); + +-- check we don't try to do a unique-ified semijoin with LATERAL +explain (verbose, costs off) +select * from + (values (0,9998), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; +select * from + (values (0,9998), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; + +-- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, +-- but we can make the test case much more compact with LATERAL) +explain (verbose, costs off) +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + +-- test some error cases where LATERAL should have been used but wasn't +select f1,g from int4_tbl a, (select f1 as g) ss; +select f1,g from int4_tbl a, (select a.f1 as g) ss; +select f1,g from int4_tbl a cross join (select f1 as g) ss; +select f1,g from int4_tbl a cross join (select a.f1 as g) ss; +-- SQL:2008 says the left table is in scope but illegal to access here +select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true; +select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true; +-- check we complain about ambiguous table references +select * from + int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss); +-- LATERAL can be used to put an aggregate into the FROM clause of its query +select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss; + +-- check behavior of LATERAL in UPDATE/DELETE + +create temp table xx1 as select f1 as x1, -f1 as x2 from int4_tbl; + +-- error, can't do this: +update xx1 set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; +update xx1 set x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss; +-- can't do it even with LATERAL: +update xx1 set x2 = f1 from lateral (select * from int4_tbl where f1 = x1) ss; +-- we might in future allow something like this, but for now it's an error: +update xx1 set x2 = f1 from xx1, lateral (select * from int4_tbl where f1 = x1) ss; + +-- also errors: +delete from xx1 using (select * from int4_tbl where f1 = x1) ss; +delete from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss; +delete from xx1 using lateral (select * from int4_tbl where f1 = x1) ss; + +-- +-- test LATERAL reference propagation down a multi-level inheritance hierarchy +-- produced for a multi-level partitioned table hierarchy. +-- +create table join_pt1 (a int, b int, c varchar) partition by range(a)( +PARTITION P1 VALUES LESS THAN(100), PARTITION P2 VALUES LESS THAN(200), PARTITION P3 VALUES LESS THAN(MAXVALUE)); + + +insert into join_pt1 values (1, 1, 'x'), (101, 101, 'y'); +create table join_ut1 (a int, b int, c varchar); +insert into join_ut1 values (101, 101, 'y'), (2, 2, 'z'); + +explain (verbose, costs off) +select t1.b, ss.phv from join_ut1 t1 left join lateral + (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv + from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss + on t1.a = ss.t2a order by t1.a; +select t1.b, ss.phv from join_ut1 t1 left join lateral + (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv + from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss + on t1.a = ss.t2a order by t1.a; + + + +-- cross apply +create table departments(department_name varchar(50), department_id int); +create table employees(employee_id int, department_id int, last_name varchar(50)); + + +insert into departments values ('Marketing', 1); +insert into departments values ('Public Relations', 2); +insert into departments values ('Operations', 3); +insert into departments values ('Develop', 4); +insert into departments values ('Research', 5); +insert into departments values ('CEO', 6); +insert into departments values ('CFO', 7); + + +insert into employees values(1, 1, 'zhangsan1'); +insert into employees values(2, 1, 'zhangsan2'); +insert into employees values(3, 1, 'zhangsan3'); +insert into employees values(4, 1, 'zhangsan4'); + + +insert into employees values(5, 2, 'lisi1'); +insert into employees values(6, 2, 'lisi2'); +insert into employees values(7, 2, 'lisi3'); +insert into employees values(8, 2, 'lisi4'); + +insert into employees values(9, 3, 'wangwu1'); +insert into employees values(10, 3, 'wangwu2'); +insert into employees values(11, 3, 'wangwu3'); +insert into employees values(12, 3, 'wangwu4'); + +insert into employees values(13, 4, 'heliu1'); +insert into employees values(14, 4, 'heliu2'); +insert into employees values(15, 4, 'heliu3'); +insert into employees values(16, 4, 'heliu4'); + +insert into employees values(17, 5, 'chenqi1'); +insert into employees values(18, 5, 'chenqi2'); +insert into employees values(19, 5, 'chenqi3'); +insert into employees values(20, 5, 'chenqi4'); + +create function fn_salar (departmentid int) returns table (employee_id int, department_id int, last_name varchar) language sql as 'select employee_id, department_id, concat(last_name,last_name) as last_name2 from employees WHERE department_id = departmentid'; + + +select* from departments d cross apply employees e where e.department_id = d.department_id; + +select* from departments d cross apply (select d.department_id from employees x) e where e.department_id = d.department_id; + +SELECT * FROM employees AS e CROSS APPLY fn_Salar(e.department_id) AS f; + + +SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +select* from departments d outer apply employees e where e.department_id = d.department_id; + +select* from departments d cross apply (select d.department_id from employees x) e where e.department_id = d.department_id; + +SELECT * FROM employees AS e outer APPLY fn_Salar(e.department_id) AS f; + +SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +-- view + +create view v1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +create view v2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + + +create view v3 as select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + + +create view v4 as select count(*) from tenk1 a, lateral generate_series(1,two) g; + +select * from v1; +select * from v2; +select * from v3; +select * from v4; + + +-- plsql +create or replace procedure plpgsql_1 (param1 varchar, param2 varchar, param3 varchar, param4 varchar ) +IS + BEGIN + create table tt1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN (param1, param2, param3) + ORDER BY d.department_name, v.employee_id; +END; +/ + +create or replace procedure plpgsql_2 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d OUTER APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN (param1, param2, param3) + ORDER BY d.department_name, v.employee_id; +END; +/ + +create or replace procedure plpgsql_3 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt3 as select id from (values (0), (1)) v(id), + lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; +END; +/ + +create or replace procedure plpgsql_4 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt4 as select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; +END; +/ + +call plpgsql_1(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); +call plpgsql_2(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); +call plpgsql_3(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); +call plpgsql_4(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + + +select * from tt1; +select * from tt2; +select * from tt3; +select * from tt4; + +drop PROCEDURE plpgsql_1; +drop PROCEDURE plpgsql_2; +drop PROCEDURE plpgsql_3; +drop PROCEDURE plpgsql_4; + + +-- cursor expression +create or replace procedure test_cursor_1 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT e.last_name, CURSOR(SELECT d.department_name FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') ORDER BY d.department_name, v.employee_id) abc FROM employees e; +begin + OPEN c1; + loop + fetch c1 into company_name, my_cur; + exit when c1%notfound; + raise notice 'company_name : % %',company_name, my_cur; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_1(); +drop procedure test_cursor_1; + + +create or replace procedure test_cursor_2 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT e.last_name, CURSOR(SELECT d.department_name FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') ORDER BY d.department_name, v.employee_id) abc FROM employees e; +begin + OPEN c1; + loop + fetch c1 into company_name, my_cur; + exit when c1%notfound; + raise notice 'company_name : % %',company_name, my_cur; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_2(); +drop procedure test_cursor_2; + + +create or replace procedure test_cursor_3 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT CURSOR(select xq1::varchar from int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2)) abc; + begin + OPEN c1; + loop + fetch c1 into my_cur; + exit when c1%notfound; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_3(); +drop procedure test_cursor_3; + + +drop table tt1; +drop table tt2; +drop table tt3; +drop table tt4; + +drop function fn_salar; +drop view v1; +drop view v2; +drop view v3; +drop view v4; +drop table tenk1; +drop table INT4_TBL; +drop table INT8_TBL; +drop table INT2_TBL; +drop table departments; +drop table employees; + +set query_dop = 1; + +-- clean +drop schema if exists lateral_with_dop_test cascade; \ No newline at end of file diff --git a/src/test/regress/output/lateral.source b/src/test/regress/output/lateral.source new file mode 100644 index 000000000..c5eed8d88 --- /dev/null +++ b/src/test/regress/output/lateral.source @@ -0,0 +1,2664 @@ +drop schema if exists lateral_test; +NOTICE: schema "lateral_test" does not exist, skipping +create schema lateral_test; +set search_path=lateral_test; +CREATE TABLE tenk1 ( + unique1 int4, + unique2 int4, + two int4, + four int4, + ten int4, + twenty int4, + hundred int4, + thousand int4, + twothousand int4, + fivethous int4, + tenthous int4, + odd int4, + even int4, + stringu1 name, + stringu2 name, + string4 name +); +COPY tenk1 FROM '@abs_srcdir@/data/tenk.data'; +CREATE TABLE INT4_TBL(f1 int4); +INSERT INTO INT4_TBL(f1) VALUES + (' 0 '), + ('123456 '), + (' -123456'), + ('2147483647'), -- largest and smallest values + ('-2147483647'); +VACUUM INT4_TBL; +CREATE TABLE INT8_TBL(q1 int8, q2 int8); +INSERT INTO INT8_TBL VALUES + (' 123 ',' 456'), + ('123 ','4567890123456789'), + ('4567890123456789','123'), + (+4567890123456789,'4567890123456789'), + ('+4567890123456789','-4567890123456789'); +VACUUM INT8_TBL; +CREATE TABLE INT2_TBL(f1 int2); +INSERT INTO INT2_TBL(f1) VALUES + ('0 '), + (' 1234 '), + (' -1234'), + ('32767'), -- largest and smallest values + ('-32767'); +VACUUM INT2_TBL; +-- +-- Test LATERAL +-- +select unique2, x.* +from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; + unique2 | f1 +---------+---- + 9998 | 0 +(1 row) + +explain (costs off) + select unique2, x.* + from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; + QUERY PLAN +------------------------------------ + Hash Join + Hash Cond: (a.unique1 = b.f1) + -> Seq Scan on tenk1 a + -> Hash + -> Seq Scan on int4_tbl b +(5 rows) + +select unique2, x.* +from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; + unique2 | f1 +---------+---- + 9998 | 0 +(1 row) + +explain (costs off) + select unique2, x.* + from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; + QUERY PLAN +------------------------------------- + Hash Join + Hash Cond: (tenk1.unique1 = x.f1) + -> Seq Scan on tenk1 + -> Hash + -> Seq Scan on int4_tbl x +(5 rows) + +explain (costs off) + select unique2, x.* + from int4_tbl x cross join lateral (select unique2 from tenk1 where f1 = unique1) ss; + QUERY PLAN +------------------------------------- + Hash Join + Hash Cond: (tenk1.unique1 = x.f1) + -> Seq Scan on tenk1 + -> Hash + -> Seq Scan on int4_tbl x +(5 rows) + +select unique2, x.* +from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; + unique2 | f1 +---------+------------- + 9998 | 0 + | 123456 + | -2147483647 + | 2147483647 + | -123456 +(5 rows) + +explain (costs off) + select unique2, x.* + from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; + QUERY PLAN +------------------------------------- + Hash Right Join + Hash Cond: (tenk1.unique1 = x.f1) + -> Seq Scan on tenk1 + -> Hash + -> Seq Scan on int4_tbl x +(5 rows) + +-- check scoping of lateral versus parent references +-- the first of these should return int8_tbl.q2, the second int8_tbl.q1 +select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl; + q1 | q2 | r +------------------+-------------------+------------------- + 123 | 456 | 456 + 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | -4567890123456789 +(5 rows) + +select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl; + q1 | q2 | r +------------------+-------------------+------------------ + 123 | 456 | 123 + 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 +(5 rows) + +-- lateral with function in FROM +select count(*) from tenk1 a, lateral generate_series(1,two) g; + count +------- + 5000 +(1 row) + +explain (costs off) + select count(*) from tenk1 a, lateral generate_series(1,two) g; + QUERY PLAN +------------------------------------------------ + Aggregate + -> Nested Loop + -> Seq Scan on tenk1 a + -> Function Scan on generate_series g +(4 rows) + +explain (costs off) + select count(*) from tenk1 a cross join lateral generate_series(1,two) g; + QUERY PLAN +------------------------------------------------ + Aggregate + -> Nested Loop + -> Seq Scan on tenk1 a + -> Function Scan on generate_series g +(4 rows) + +-- don't need the explicit LATERAL keyword for functions +explain (costs off) + select count(*) from tenk1 a, generate_series(1,two) g; + QUERY PLAN +------------------------------------------------ + Aggregate + -> Nested Loop + -> Seq Scan on tenk1 a + -> Function Scan on generate_series g +(4 rows) + +-- lateral with UNION ALL subselect +explain (costs off) + select * from generate_series(100,200) g, + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss; + QUERY PLAN +------------------------------------------ + Nested Loop + -> Function Scan on generate_series g + -> Append + -> Seq Scan on int8_tbl a + Filter: (g.g = q1) + -> Seq Scan on int8_tbl b + Filter: (g.g = q2) +(7 rows) + +select * from generate_series(100,200) g, + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss; + g | q1 | q2 +-----+------------------+------------------ + 123 | 123 | 456 + 123 | 123 | 4567890123456789 + 123 | 4567890123456789 | 123 +(3 rows) + +-- lateral with VALUES +explain (costs off) + select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; + QUERY PLAN +----------------------------------------------------- + Aggregate + -> Hash Join + Hash Cond: ("*VALUES*".column1 = b.unique2) + -> Nested Loop + -> Seq Scan on tenk1 a + -> Values Scan on "*VALUES*" + -> Hash + -> Seq Scan on tenk1 b +(8 rows) + +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; + count +------- + 10000 +(1 row) + +-- lateral with VALUES, no flattening possible +explain (costs off) + select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; + QUERY PLAN +----------------------------------------------------- + Aggregate + -> Hash Join + Hash Cond: ("*VALUES*".column1 = b.unique2) + -> Nested Loop + -> Seq Scan on tenk1 a + -> Values Scan on "*VALUES*" + -> Hash + -> Seq Scan on tenk1 b +(8 rows) + +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; + count +------- + 10000 +(1 row) + +-- lateral injecting a strange outer join condition +explain (costs off) + select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; + QUERY PLAN +------------------------------------------------ + Sort + Sort Key: a.q1, a.q2, x.q1, x.q2, ($0) + -> Nested Loop + -> Seq Scan on int8_tbl a + -> Hash Left Join + Hash Cond: (x.q2 = ($0)) + -> Seq Scan on int8_tbl x + -> Hash + -> Seq Scan on int4_tbl y +(9 rows) + +select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; + q1 | q2 | q1 | q2 | z +------------------+-------------------+------------------+-------------------+------------------ + 123 | 456 | 123 | 456 | + 123 | 456 | 123 | 4567890123456789 | + 123 | 456 | 4567890123456789 | -4567890123456789 | + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 4567890123456789 | + 123 | 4567890123456789 | 123 | 456 | + 123 | 4567890123456789 | 123 | 4567890123456789 | + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | + 4567890123456789 | -4567890123456789 | 123 | 456 | + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 | + 4567890123456789 | -4567890123456789 | 4567890123456789 | 123 | + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | -4567890123456789 | + 4567890123456789 | 123 | 4567890123456789 | 123 | + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 456 | + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 +(57 rows) + +-- lateral reference to a join alias variable +select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (select x) ss2(y); + x | f1 | y +---+----+--- + 0 | 0 | 0 +(1 row) + +select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (values(x)) ss2(y); + x | f1 | y +-------------+-------------+------------- + 0 | 0 | 0 + 123456 | 123456 | 123456 + -123456 | -123456 | -123456 + 2147483647 | 2147483647 | 2147483647 + -2147483647 | -2147483647 | -2147483647 +(5 rows) + +select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j, + lateral (select x) ss2(y); + x | f1 | y +---+----+--- + 0 | 0 | 0 +(1 row) + +-- lateral references requiring pullup +select * from (values(1)) x(lb), + lateral generate_series(lb,4) x4; + lb | x4 +----+---- + 1 | 1 + 1 | 2 + 1 | 3 + 1 | 4 +(4 rows) + +select * from (select f1/1000000000 from int4_tbl) x(lb), + lateral generate_series(lb,4) x4; + lb | x4 +--------------+---- + 0 | 0 + 0 | 1 + 0 | 2 + 0 | 3 + 0 | 4 + .000123456 | 0 + .000123456 | 1 + .000123456 | 2 + .000123456 | 3 + .000123456 | 4 + -.000123456 | 0 + -.000123456 | 1 + -.000123456 | 2 + -.000123456 | 3 + -.000123456 | 4 + 2.147483647 | 2 + 2.147483647 | 3 + 2.147483647 | 4 + -2.147483647 | -2 + -2.147483647 | -1 + -2.147483647 | 0 + -2.147483647 | 1 + -2.147483647 | 2 + -2.147483647 | 3 + -2.147483647 | 4 +(25 rows) + +select * from (values(1)) x(lb), + lateral (values(lb)) y(lbcopy); + lb | lbcopy +----+-------- + 1 | 1 +(1 row) + +select * from (values(1)) x(lb), + lateral (select lb from int4_tbl) y(lbcopy); + lb | lbcopy +----+-------- + 1 | 1 + 1 | 1 + 1 | 1 + 1 | 1 + 1 | 1 +(5 rows) + +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2); + q1 | q2 | q1 | q2 | xq1 | yq1 | yq2 +------------------+-------------------+------------------+-------------------+------------------+------------------+------------------- + 123 | 456 | | | 123 | | + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123 + 4567890123456789 | -4567890123456789 | | | 4567890123456789 | | +(10 rows) + +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); + q1 | q2 | q1 | q2 | xq1 | yq1 | yq2 +------------------+-------------------+------------------+-------------------+------------------+------------------+------------------- + 123 | 456 | | | 123 | | + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123 + 4567890123456789 | -4567890123456789 | | | 4567890123456789 | | +(10 rows) + +select x.* from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); + q1 | q2 +------------------+------------------- + 123 | 456 + 123 | 4567890123456789 + 123 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 +(10 rows) + +select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + vx | vy +-------------------+------------------- + 123 | + 456 | + 123 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 123 | 4567890123456789 + 4567890123456789 | 123 + 123 | 456 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | + -4567890123456789 | +(20 rows) + +select v.* from + (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + vx | vy +-------------------+------------------- + 123 | + 456 | + 123 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 123 | 4567890123456789 + 4567890123456789 | 123 + 123 | 456 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | + -4567890123456789 | +(20 rows) + +explain (verbose, costs off) +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; + QUERY PLAN +------------------------------------------- + Nested Loop Left Join + Output: a.q1, a.q2, b.q1, b.q2, ($0) + -> Seq Scan on lateral_test.int8_tbl a + Output: a.q1, a.q2 + -> Seq Scan on lateral_test.int8_tbl b + Output: b.q1, b.q2, a.q2 + Filter: (a.q2 = b.q1) +(7 rows) + +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; + q1 | q2 | q1 | q2 | x +------------------+-------------------+------------------+-------------------+------------------ + 123 | 456 | | | + 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | | | +(10 rows) + +explain (verbose, costs off) +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; + QUERY PLAN +-------------------------------------------------------------- + Nested Loop Left Join + Output: a.q1, a.q2, b.q1, b.q2, (COALESCE($0, 42::bigint)) + -> Seq Scan on lateral_test.int8_tbl a + Output: a.q1, a.q2 + -> Seq Scan on lateral_test.int8_tbl b + Output: b.q1, b.q2, COALESCE(a.q2, 42::bigint) + Filter: (a.q2 = b.q1) +(7 rows) + +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; + q1 | q2 | q1 | q2 | x +------------------+-------------------+------------------+-------------------+------------------ + 123 | 456 | | | + 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | | | +(10 rows) + +-- lateral can result in join conditions appearing below their +-- real semantic level +explain (verbose, costs off) +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; + QUERY PLAN +------------------------------------------------- + Hash Left Join + Output: i.f1, j.f1 + Hash Cond: (i.f1 = j.f1) + -> Seq Scan on lateral_test.int4_tbl i + Output: i.f1 + -> Hash + Output: j.f1 + -> Seq Scan on lateral_test.int2_tbl j + Output: j.f1 +(9 rows) + +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; + f1 | f1 +-------------+---- + 0 | 0 + 123456 | + -123456 | + 2147483647 | + -2147483647 | +(5 rows) + +explain (verbose, costs off) +select * from int4_tbl i left join + lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; + QUERY PLAN +------------------------------------------- + Nested Loop Left Join + Output: i.f1, (COALESCE($0)) + -> Seq Scan on lateral_test.int4_tbl i + Output: i.f1, i.* + -> Seq Scan on lateral_test.int2_tbl j + Output: j.f1, COALESCE(i.*) + Filter: (i.f1 = j.f1) +(7 rows) + +select * from int4_tbl i left join + lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; + f1 | coalesce +-------------+---------- + 0 | (0) + 123456 | + -123456 | + 2147483647 | + -2147483647 | +(5 rows) + +explain (verbose, costs off) +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; + QUERY PLAN +------------------------------------------------------- + Nested Loop + Output: a.f1, b.f1, c.q1, c.q2 + -> Seq Scan on lateral_test.int4_tbl a + Output: a.f1 + -> Hash Left Join + Output: b.f1, c.q1, c.q2 + Hash Cond: (b.f1 = c.q1) + -> Seq Scan on lateral_test.int4_tbl b + Output: b.f1 + -> Hash + Output: c.q1, c.q2 + -> Seq Scan on lateral_test.int8_tbl c + Output: c.q1, c.q2 + Filter: (a.f1 = c.q2) +(14 rows) + +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; + f1 | f1 | q1 | q2 +-------------+-------------+----+---- + 0 | 0 | | + 0 | 123456 | | + 0 | -123456 | | + 0 | 2147483647 | | + 0 | -2147483647 | | + 123456 | 0 | | + 123456 | 123456 | | + 123456 | -123456 | | + 123456 | 2147483647 | | + 123456 | -2147483647 | | + -123456 | 0 | | + -123456 | 123456 | | + -123456 | -123456 | | + -123456 | 2147483647 | | + -123456 | -2147483647 | | + 2147483647 | 0 | | + 2147483647 | 123456 | | + 2147483647 | -123456 | | + 2147483647 | 2147483647 | | + 2147483647 | -2147483647 | | + -2147483647 | 0 | | + -2147483647 | 123456 | | + -2147483647 | -123456 | | + -2147483647 | 2147483647 | | + -2147483647 | -2147483647 | | +(25 rows) + +-- lateral reference in a PlaceHolderVar evaluated at join level +explain (verbose, costs off) +select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; + QUERY PLAN +----------------------------------------------------------- + Nested Loop Left Join + Output: a.q1, a.q2, b.q1, c.q1, (LEAST($0, b.q1, c.q1)) + -> Seq Scan on lateral_test.int8_tbl a + Output: a.q1, a.q2 + -> Nested Loop + Output: b.q1, c.q1, LEAST(a.q1, b.q1, c.q1) + Join Filter: (a.q2 = b.q1) + -> Seq Scan on lateral_test.int8_tbl b + Output: b.q1, b.q2 + -> Materialize + Output: c.q1 + -> Seq Scan on lateral_test.int8_tbl c + Output: c.q1 +(13 rows) + +select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; + q1 | q2 | bq1 | cq1 | least +------------------+-------------------+------------------+------------------+------------------ + 123 | 456 | | | + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | | | +(42 rows) + +-- case requiring nested PlaceHolderVars +explain (verbose, costs off) +select * from + int8_tbl c left join ( + int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select ss2.y offset 0) ss3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Nested Loop + Output: c.q1, c.q2, a.q1, a.q2, b.q1, (COALESCE(b.q2, 42::bigint)), d.q1, (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)), ($0) + -> Hash Right Join + Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, 42::bigint)), (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)) + Hash Cond: (d.q1 = c.q2) + -> Nested Loop + Output: a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, 42::bigint)), (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)) + -> Hash Left Join + Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, 42::bigint)) + Hash Cond: (a.q2 = b.q1) + -> Seq Scan on lateral_test.int8_tbl a + Output: a.q1, a.q2 + -> Hash + Output: b.q1, (COALESCE(b.q2, 42::bigint)) + -> Seq Scan on lateral_test.int8_tbl b + Output: b.q1, COALESCE(b.q2, 42::bigint) + -> Materialize + Output: d.q1, (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)) + -> Seq Scan on lateral_test.int8_tbl d + Output: d.q1, COALESCE(COALESCE(b.q2, 42::bigint), d.q2) + -> Hash + Output: c.q1, c.q2 + -> Seq Scan on lateral_test.int8_tbl c + Output: c.q1, c.q2 + -> Limit + Output: ($0) + -> Result + Output: (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)) +(28 rows) + +-- case that breaks the old ph_may_need optimization +explain (verbose, costs off) +select c.*,a.*,ss1.q1,ss2.q1,ss3.* from + int8_tbl c left join ( + int8_tbl a left join + (select q1, coalesce(q2,f1) as x from int8_tbl b, int4_tbl b2 + where q1 < f1) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select * from int4_tbl i where ss2.y > f1) ss3; + QUERY PLAN +------------------------------------------------------------------------------- + Hash Right Join + Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, i.f1 + Hash Cond: (d.q1 = c.q2) + Filter: ((COALESCE($0, d.q2)) > i.f1) + -> Nested Loop + Output: a.q1, a.q2, b.q1, d.q1, (COALESCE($0, d.q2)) + -> Hash Right Join + Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, (b2.f1)::bigint)) + Hash Cond: (b.q1 = a.q2) + -> Nested Loop + Output: b.q1, COALESCE(b.q2, (b2.f1)::bigint) + Join Filter: (b.q1 < b2.f1) + -> Seq Scan on lateral_test.int8_tbl b + Output: b.q1, b.q2 + -> Materialize + Output: b2.f1 + -> Seq Scan on lateral_test.int4_tbl b2 + Output: b2.f1 + -> Hash + Output: a.q1, a.q2 + -> Seq Scan on lateral_test.int8_tbl a + Output: a.q1, a.q2 + -> Seq Scan on lateral_test.int8_tbl d + Output: d.q1, COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2) + -> Hash + Output: c.q1, c.q2, i.f1 + -> Nested Loop + Output: c.q1, c.q2, i.f1 + -> Seq Scan on lateral_test.int8_tbl c + Output: c.q1, c.q2 + -> Materialize + Output: i.f1 + -> Seq Scan on lateral_test.int4_tbl i + Output: i.f1 +(34 rows) + +-- check processing of postponed quals (bug #9041) +explain (verbose, costs off) +select * from + (select 1 as x offset 0) x cross join (select 2 as y offset 0) y + left join lateral ( + select * from (select 3 as z offset 0) z where z.z = x.x + ) zz on zz.z = y.y; + QUERY PLAN +---------------------------------------------- + Nested Loop Left Join + Output: (1), (2), (3) + Join Filter: (((3) = (1)) AND ((3) = (2))) + -> Nested Loop + Output: (1), (2) + -> Limit + Output: (1) + -> Result + Output: 1 + -> Limit + Output: (2) + -> Result + Output: 2 + -> Limit + Output: (3) + -> Result + Output: 3 +(17 rows) + +-- a new postponed-quals issue (bug #17768) +explain (costs off) +select * from int4_tbl t1, + lateral (select * from int4_tbl t2 inner join int4_tbl t3 on t1.f1 = 1 + inner join (int4_tbl t4 left join int4_tbl t5 on true) on true) ss; + QUERY PLAN +------------------------------------------------- + Nested Loop Left Join + -> Nested Loop + -> Nested Loop + -> Nested Loop + -> Seq Scan on int4_tbl t1 + Filter: (f1 = 1) + -> Seq Scan on int4_tbl t2 + -> Materialize + -> Seq Scan on int4_tbl t3 + -> Materialize + -> Seq Scan on int4_tbl t4 + -> Materialize + -> Seq Scan on int4_tbl t5 +(13 rows) + +-- check dummy rels with lateral references (bug #15694) +explain (verbose, costs off) +select * from int8_tbl i8 left join lateral + (select *, i8.q2 from int4_tbl where false) ss on true; + QUERY PLAN +----------------------------------------------- + Nested Loop Left Join + Output: i8.q1, i8.q2, int4_tbl.f1, ($0) + -> Seq Scan on lateral_test.int8_tbl i8 + Output: i8.q1, i8.q2 + -> Result + Output: int4_tbl.f1, ($0) + One-Time Filter: false + -> Seq Scan on lateral_test.int4_tbl + Output: int4_tbl.f1, i8.q2 +(9 rows) + +explain (verbose, costs off) +select * from int8_tbl i8 left join lateral + (select *, i8.q2 from int4_tbl i1, int4_tbl i2 where false) ss on true; + QUERY PLAN +--------------------------------------------- + Nested Loop Left Join + Output: i8.q1, i8.q2, i1.f1, i2.f1, i8.q2 + -> Seq Scan on lateral_test.int8_tbl i8 + Output: i8.q1, i8.q2 + -> Result + Output: i1.f1, i2.f1, i8.q2 + One-Time Filter: false +(7 rows) + +-- check handling of nested appendrels inside LATERAL +select * from + ((select 2 as v) union all (select 3 as v)) as q1 + cross join lateral + ((select * from + ((select 4 as v) union all (select 5 as v)) as q3) + union all + (select q1.v) + ) as q2; + v | v +---+--- + 2 | 4 + 2 | 5 + 2 | 2 + 3 | 4 + 3 | 5 + 3 | 3 +(6 rows) + +-- check the number of columns specified +SELECT * FROM (int8_tbl i cross join int4_tbl j) ss(a,b,c,d); +ERROR: column alias list for "ss" has too many entries +-- check we don't try to do a unique-ified semijoin with LATERAL +explain (verbose, costs off) +select * from + (values (0,9998), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; + QUERY PLAN +------------------------------------------------------------------ + Nested Loop + Output: "*VALUES*".column1, "*VALUES*".column2, int4_tbl.f1 + -> Values Scan on "*VALUES*" + Output: "*VALUES*".column1, "*VALUES*".column2 + -> Hash Right Semi Join + Output: int4_tbl.f1 + Hash Cond: (tenk1.unique1 = int4_tbl.f1) + -> Limit + Output: tenk1.unique1 + -> Seq Scan on lateral_test.tenk1 + Output: tenk1.unique1 + Filter: (tenk1.unique2 = "*VALUES*".column2) + -> Hash + Output: int4_tbl.f1 + -> Seq Scan on lateral_test.int4_tbl + Output: int4_tbl.f1 +(16 rows) + +select * from + (values (0,9998), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; + id | x | f1 +----+------+---- + 0 | 9998 | 0 +(1 row) + +-- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, +-- but we can make the test case much more compact with LATERAL) +explain (verbose, costs off) +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + QUERY PLAN +---------------------------------------------------------------------------- + Nested Loop + Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 + -> Seq Scan on lateral_test.int8_tbl t1 + Output: t1.q1, t1.q2 + -> Nested Loop + Output: "*VALUES*".column1, ss2.q1, ss2.q2 + -> Values Scan on "*VALUES*" + Output: "*VALUES*".column1 + -> Subquery Scan on ss2 + Output: ss2.q1, ss2.q2 + Filter: (t1.q1 = ss2.q2) + -> Limit + Output: t2.q1, t2.q2 + -> Seq Scan on lateral_test.int8_tbl t2 + Output: t2.q1, t2.q2 + Filter: (SubPlan 3) + SubPlan 3 + -> Result + Output: t3.q2 + One-Time Filter: $4 + InitPlan 1 (returns $2) + -> Result + Output: GREATEST($0, t2.q2) + InitPlan 2 (returns $4) + -> Result + Output: ($3 = 0) + -> Seq Scan on lateral_test.int8_tbl t3 + Output: t3.q1, t3.q2 + Filter: (t3.q2 = $2) +(29 rows) + +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + id | q1 | q2 | q1 | q2 +----+------------------+-------------------+------------------+------------------ + 0 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 0 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 0 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 +(3 rows) + +-- test some error cases where LATERAL should have been used but wasn't +select f1,g from int4_tbl a, (select f1 as g) ss; +ERROR: column "f1" does not exist +LINE 1: select f1,g from int4_tbl a, (select f1 as g) ss; + ^ +HINT: There is a column named "f1" in table "a", but it cannot be referenced from this part of the query. +CONTEXT: referenced column: g +select f1,g from int4_tbl a, (select a.f1 as g) ss; +ERROR: invalid reference to FROM-clause entry for table "a" +LINE 1: select f1,g from int4_tbl a, (select a.f1 as g) ss; + ^ +HINT: There is an entry for table "a", but it cannot be referenced from this part of the query. +CONTEXT: referenced column: g +select f1,g from int4_tbl a cross join (select f1 as g) ss; +ERROR: column "f1" does not exist +LINE 1: select f1,g from int4_tbl a cross join (select f1 as g) ss; + ^ +HINT: There is a column named "f1" in table "a", but it cannot be referenced from this part of the query. +CONTEXT: referenced column: g +select f1,g from int4_tbl a cross join (select a.f1 as g) ss; +ERROR: invalid reference to FROM-clause entry for table "a" +LINE 1: select f1,g from int4_tbl a cross join (select a.f1 as g) ss... + ^ +HINT: There is an entry for table "a", but it cannot be referenced from this part of the query. +CONTEXT: referenced column: g +-- SQL:2008 says the left table is in scope but illegal to access here +select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true; +ERROR: invalid reference to FROM-clause entry for table "a" +LINE 1: ... int4_tbl a right join lateral generate_series(0, a.f1) g on... + ^ +DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. +select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true; +ERROR: invalid reference to FROM-clause entry for table "a" +LINE 1: ...m int4_tbl a full join lateral generate_series(0, a.f1) g on... + ^ +DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. +-- check we complain about ambiguous table references +select * from + int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss); +ERROR: table reference "x" is ambiguous +LINE 2: ...cross join (int4_tbl x cross join lateral (select x.f1) ss); + ^ +CONTEXT: referenced column: f1 +-- LATERAL can be used to put an aggregate into the FROM clause of its query +select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss; +ERROR: aggregates not allowed in FROM clause +LINE 1: select 1 from tenk1 a, lateral (select max(a.unique1) from i... + ^ +CONTEXT: referenced column: max +-- check behavior of LATERAL in UPDATE/DELETE +create temp table xx1 as select f1 as x1, -f1 as x2 from int4_tbl; +-- error, can't do this: +update xx1 set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; +ERROR: column "x1" does not exist +LINE 1: ... set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; + ^ +HINT: There is a column named "x1" in table "xx1", but it cannot be referenced from this part of the query. +update xx1 set x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss; +ERROR: invalid reference to FROM-clause entry for table "xx1" +LINE 1: ...t x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss... + ^ +HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. +-- can't do it even with LATERAL: +update xx1 set x2 = f1 from lateral (select * from int4_tbl where f1 = x1) ss; +ERROR: invalid reference to FROM-clause entry for table "xx1" +LINE 1: ...= f1 from lateral (select * from int4_tbl where f1 = x1) ss; + ^ +DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. +-- we might in future allow something like this, but for now it's an error: +update xx1 set x2 = f1 from xx1, lateral (select * from int4_tbl where f1 = x1) ss; +ERROR: table name "xx1" specified more than once +-- also errors: +delete from xx1 using (select * from int4_tbl where f1 = x1) ss; +ERROR: column "x1" does not exist +LINE 1: ...te from xx1 using (select * from int4_tbl where f1 = x1) ss; + ^ +HINT: There is a column named "x1" in table "xx1", but it cannot be referenced from this part of the query. +delete from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss; +ERROR: invalid reference to FROM-clause entry for table "xx1" +LINE 1: ...from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss... + ^ +HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. +delete from xx1 using lateral (select * from int4_tbl where f1 = x1) ss; +ERROR: invalid reference to FROM-clause entry for table "xx1" +LINE 1: ...xx1 using lateral (select * from int4_tbl where f1 = x1) ss; + ^ +DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. +-- +-- test LATERAL reference propagation down a multi-level inheritance hierarchy +-- produced for a multi-level partitioned table hierarchy. +-- +create table join_pt1 (a int, b int, c varchar) partition by range(a)( +PARTITION P1 VALUES LESS THAN(100), PARTITION P2 VALUES LESS THAN(200), PARTITION P3 VALUES LESS THAN(MAXVALUE)); +insert into join_pt1 values (1, 1, 'x'), (101, 101, 'y'); +create table join_ut1 (a int, b int, c varchar); +insert into join_ut1 values (101, 101, 'y'), (2, 2, 'z'); +explain (verbose, costs off) +select t1.b, ss.phv from join_ut1 t1 left join lateral + (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv + from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss + on t1.a = ss.t2a order by t1.a; + QUERY PLAN +-------------------------------------------------------------------------- + Sort + Output: t1.b, (LEAST($2, t2.a, t3.a)), t1.a + Sort Key: t1.a + -> Nested Loop Left Join + Output: t1.b, (LEAST($2, t2.a, t3.a)), t1.a + -> Seq Scan on lateral_test.join_ut1 t1 + Output: t1.a, t1.b, t1.c + -> Hash Join + Output: t2.a, LEAST(t1.a, t2.a, t3.a) + Hash Cond: (t2.a = t3.b) + Join Filter: (t1.a = t2.a) + -> Partition Iterator + Output: t2.a, t2.b, t2.c + Iterations: 3 + -> Partitioned Seq Scan on lateral_test.join_pt1 t2 + Output: t2.a, t2.b, t2.c + Selected Partitions: 1..3 + -> Hash + Output: t3.b, t3.a + -> Seq Scan on lateral_test.join_ut1 t3 + Output: t3.b, t3.a +(21 rows) + +select t1.b, ss.phv from join_ut1 t1 left join lateral + (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv + from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss + on t1.a = ss.t2a order by t1.a; + b | phv +-----+----- + 2 | + 101 | 101 +(2 rows) + +-- cross apply +create table departments(department_name varchar(50), department_id int); +create table employees(employee_id int, department_id int, last_name varchar(50)); +insert into departments values ('Marketing', 1); +insert into departments values ('Public Relations', 2); +insert into departments values ('Operations', 3); +insert into departments values ('Develop', 4); +insert into departments values ('Research', 5); +insert into departments values ('CEO', 6); +insert into departments values ('CFO', 7); +insert into employees values(1, 1, 'zhangsan1'); +insert into employees values(2, 1, 'zhangsan2'); +insert into employees values(3, 1, 'zhangsan3'); +insert into employees values(4, 1, 'zhangsan4'); +insert into employees values(5, 2, 'lisi1'); +insert into employees values(6, 2, 'lisi2'); +insert into employees values(7, 2, 'lisi3'); +insert into employees values(8, 2, 'lisi4'); +insert into employees values(9, 3, 'wangwu1'); +insert into employees values(10, 3, 'wangwu2'); +insert into employees values(11, 3, 'wangwu3'); +insert into employees values(12, 3, 'wangwu4'); +insert into employees values(13, 4, 'heliu1'); +insert into employees values(14, 4, 'heliu2'); +insert into employees values(15, 4, 'heliu3'); +insert into employees values(16, 4, 'heliu4'); +insert into employees values(17, 5, 'chenqi1'); +insert into employees values(18, 5, 'chenqi2'); +insert into employees values(19, 5, 'chenqi3'); +insert into employees values(20, 5, 'chenqi4'); +create function fn_salar (departmentid int) returns table (employee_id int, department_id int, last_name varchar) language sql as 'select employee_id, department_id, concat(last_name,last_name) as last_name2 from employees WHERE department_id = departmentid'; +select* from departments d cross apply employees e where e.department_id = d.department_id; + department_name | department_id | employee_id | department_id | last_name +------------------+---------------+-------------+---------------+----------- + Marketing | 1 | 4 | 1 | zhangsan4 + Marketing | 1 | 3 | 1 | zhangsan3 + Marketing | 1 | 2 | 1 | zhangsan2 + Marketing | 1 | 1 | 1 | zhangsan1 + Public Relations | 2 | 8 | 2 | lisi4 + Public Relations | 2 | 7 | 2 | lisi3 + Public Relations | 2 | 6 | 2 | lisi2 + Public Relations | 2 | 5 | 2 | lisi1 + Operations | 3 | 12 | 3 | wangwu4 + Operations | 3 | 11 | 3 | wangwu3 + Operations | 3 | 10 | 3 | wangwu2 + Operations | 3 | 9 | 3 | wangwu1 + Develop | 4 | 16 | 4 | heliu4 + Develop | 4 | 15 | 4 | heliu3 + Develop | 4 | 14 | 4 | heliu2 + Develop | 4 | 13 | 4 | heliu1 + Research | 5 | 20 | 5 | chenqi4 + Research | 5 | 19 | 5 | chenqi3 + Research | 5 | 18 | 5 | chenqi2 + Research | 5 | 17 | 5 | chenqi1 +(20 rows) + +select* from departments d cross apply (select d.department_id from employees x) e where e.department_id = d.department_id; + department_name | department_id | department_id +------------------+---------------+--------------- + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 +(140 rows) + +SELECT * FROM employees AS e CROSS APPLY fn_Salar(e.department_id) AS f; + employee_id | department_id | last_name | employee_id | department_id | last_name +-------------+---------------+-----------+-------------+---------------+-------------------- + 1 | 1 | zhangsan1 | 1 | 1 | zhangsan1zhangsan1 + 1 | 1 | zhangsan1 | 2 | 1 | zhangsan2zhangsan2 + 1 | 1 | zhangsan1 | 3 | 1 | zhangsan3zhangsan3 + 1 | 1 | zhangsan1 | 4 | 1 | zhangsan4zhangsan4 + 2 | 1 | zhangsan2 | 1 | 1 | zhangsan1zhangsan1 + 2 | 1 | zhangsan2 | 2 | 1 | zhangsan2zhangsan2 + 2 | 1 | zhangsan2 | 3 | 1 | zhangsan3zhangsan3 + 2 | 1 | zhangsan2 | 4 | 1 | zhangsan4zhangsan4 + 3 | 1 | zhangsan3 | 1 | 1 | zhangsan1zhangsan1 + 3 | 1 | zhangsan3 | 2 | 1 | zhangsan2zhangsan2 + 3 | 1 | zhangsan3 | 3 | 1 | zhangsan3zhangsan3 + 3 | 1 | zhangsan3 | 4 | 1 | zhangsan4zhangsan4 + 4 | 1 | zhangsan4 | 1 | 1 | zhangsan1zhangsan1 + 4 | 1 | zhangsan4 | 2 | 1 | zhangsan2zhangsan2 + 4 | 1 | zhangsan4 | 3 | 1 | zhangsan3zhangsan3 + 4 | 1 | zhangsan4 | 4 | 1 | zhangsan4zhangsan4 + 5 | 2 | lisi1 | 5 | 2 | lisi1lisi1 + 5 | 2 | lisi1 | 6 | 2 | lisi2lisi2 + 5 | 2 | lisi1 | 7 | 2 | lisi3lisi3 + 5 | 2 | lisi1 | 8 | 2 | lisi4lisi4 + 6 | 2 | lisi2 | 5 | 2 | lisi1lisi1 + 6 | 2 | lisi2 | 6 | 2 | lisi2lisi2 + 6 | 2 | lisi2 | 7 | 2 | lisi3lisi3 + 6 | 2 | lisi2 | 8 | 2 | lisi4lisi4 + 7 | 2 | lisi3 | 5 | 2 | lisi1lisi1 + 7 | 2 | lisi3 | 6 | 2 | lisi2lisi2 + 7 | 2 | lisi3 | 7 | 2 | lisi3lisi3 + 7 | 2 | lisi3 | 8 | 2 | lisi4lisi4 + 8 | 2 | lisi4 | 5 | 2 | lisi1lisi1 + 8 | 2 | lisi4 | 6 | 2 | lisi2lisi2 + 8 | 2 | lisi4 | 7 | 2 | lisi3lisi3 + 8 | 2 | lisi4 | 8 | 2 | lisi4lisi4 + 9 | 3 | wangwu1 | 9 | 3 | wangwu1wangwu1 + 9 | 3 | wangwu1 | 10 | 3 | wangwu2wangwu2 + 9 | 3 | wangwu1 | 11 | 3 | wangwu3wangwu3 + 9 | 3 | wangwu1 | 12 | 3 | wangwu4wangwu4 + 10 | 3 | wangwu2 | 9 | 3 | wangwu1wangwu1 + 10 | 3 | wangwu2 | 10 | 3 | wangwu2wangwu2 + 10 | 3 | wangwu2 | 11 | 3 | wangwu3wangwu3 + 10 | 3 | wangwu2 | 12 | 3 | wangwu4wangwu4 + 11 | 3 | wangwu3 | 9 | 3 | wangwu1wangwu1 + 11 | 3 | wangwu3 | 10 | 3 | wangwu2wangwu2 + 11 | 3 | wangwu3 | 11 | 3 | wangwu3wangwu3 + 11 | 3 | wangwu3 | 12 | 3 | wangwu4wangwu4 + 12 | 3 | wangwu4 | 9 | 3 | wangwu1wangwu1 + 12 | 3 | wangwu4 | 10 | 3 | wangwu2wangwu2 + 12 | 3 | wangwu4 | 11 | 3 | wangwu3wangwu3 + 12 | 3 | wangwu4 | 12 | 3 | wangwu4wangwu4 + 13 | 4 | heliu1 | 13 | 4 | heliu1heliu1 + 13 | 4 | heliu1 | 14 | 4 | heliu2heliu2 + 13 | 4 | heliu1 | 15 | 4 | heliu3heliu3 + 13 | 4 | heliu1 | 16 | 4 | heliu4heliu4 + 14 | 4 | heliu2 | 13 | 4 | heliu1heliu1 + 14 | 4 | heliu2 | 14 | 4 | heliu2heliu2 + 14 | 4 | heliu2 | 15 | 4 | heliu3heliu3 + 14 | 4 | heliu2 | 16 | 4 | heliu4heliu4 + 15 | 4 | heliu3 | 13 | 4 | heliu1heliu1 + 15 | 4 | heliu3 | 14 | 4 | heliu2heliu2 + 15 | 4 | heliu3 | 15 | 4 | heliu3heliu3 + 15 | 4 | heliu3 | 16 | 4 | heliu4heliu4 + 16 | 4 | heliu4 | 13 | 4 | heliu1heliu1 + 16 | 4 | heliu4 | 14 | 4 | heliu2heliu2 + 16 | 4 | heliu4 | 15 | 4 | heliu3heliu3 + 16 | 4 | heliu4 | 16 | 4 | heliu4heliu4 + 17 | 5 | chenqi1 | 17 | 5 | chenqi1chenqi1 + 17 | 5 | chenqi1 | 18 | 5 | chenqi2chenqi2 + 17 | 5 | chenqi1 | 19 | 5 | chenqi3chenqi3 + 17 | 5 | chenqi1 | 20 | 5 | chenqi4chenqi4 + 18 | 5 | chenqi2 | 17 | 5 | chenqi1chenqi1 + 18 | 5 | chenqi2 | 18 | 5 | chenqi2chenqi2 + 18 | 5 | chenqi2 | 19 | 5 | chenqi3chenqi3 + 18 | 5 | chenqi2 | 20 | 5 | chenqi4chenqi4 + 19 | 5 | chenqi3 | 17 | 5 | chenqi1chenqi1 + 19 | 5 | chenqi3 | 18 | 5 | chenqi2chenqi2 + 19 | 5 | chenqi3 | 19 | 5 | chenqi3chenqi3 + 19 | 5 | chenqi3 | 20 | 5 | chenqi4chenqi4 + 20 | 5 | chenqi4 | 17 | 5 | chenqi1chenqi1 + 20 | 5 | chenqi4 | 18 | 5 | chenqi2chenqi2 + 20 | 5 | chenqi4 | 19 | 5 | chenqi3chenqi3 + 20 | 5 | chenqi4 | 20 | 5 | chenqi4chenqi4 +(80 rows) + + +SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + + +select* from departments d outer apply employees e where e.department_id = d.department_id; + department_name | department_id | employee_id | department_id | last_name +------------------+---------------+-------------+---------------+----------- + Marketing | 1 | 4 | 1 | zhangsan4 + Marketing | 1 | 3 | 1 | zhangsan3 + Marketing | 1 | 2 | 1 | zhangsan2 + Marketing | 1 | 1 | 1 | zhangsan1 + Public Relations | 2 | 8 | 2 | lisi4 + Public Relations | 2 | 7 | 2 | lisi3 + Public Relations | 2 | 6 | 2 | lisi2 + Public Relations | 2 | 5 | 2 | lisi1 + Operations | 3 | 12 | 3 | wangwu4 + Operations | 3 | 11 | 3 | wangwu3 + Operations | 3 | 10 | 3 | wangwu2 + Operations | 3 | 9 | 3 | wangwu1 + Develop | 4 | 16 | 4 | heliu4 + Develop | 4 | 15 | 4 | heliu3 + Develop | 4 | 14 | 4 | heliu2 + Develop | 4 | 13 | 4 | heliu1 + Research | 5 | 20 | 5 | chenqi4 + Research | 5 | 19 | 5 | chenqi3 + Research | 5 | 18 | 5 | chenqi2 + Research | 5 | 17 | 5 | chenqi1 +(20 rows) + +select* from departments d cross apply (select d.department_id from employees x) e where e.department_id = d.department_id; + department_name | department_id | department_id +------------------+---------------+--------------- + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 +(140 rows) + +SELECT * FROM employees AS e outer APPLY fn_Salar(e.department_id) AS f; + employee_id | department_id | last_name | employee_id | department_id | last_name +-------------+---------------+-----------+-------------+---------------+-------------------- + 1 | 1 | zhangsan1 | 1 | 1 | zhangsan1zhangsan1 + 1 | 1 | zhangsan1 | 2 | 1 | zhangsan2zhangsan2 + 1 | 1 | zhangsan1 | 3 | 1 | zhangsan3zhangsan3 + 1 | 1 | zhangsan1 | 4 | 1 | zhangsan4zhangsan4 + 2 | 1 | zhangsan2 | 1 | 1 | zhangsan1zhangsan1 + 2 | 1 | zhangsan2 | 2 | 1 | zhangsan2zhangsan2 + 2 | 1 | zhangsan2 | 3 | 1 | zhangsan3zhangsan3 + 2 | 1 | zhangsan2 | 4 | 1 | zhangsan4zhangsan4 + 3 | 1 | zhangsan3 | 1 | 1 | zhangsan1zhangsan1 + 3 | 1 | zhangsan3 | 2 | 1 | zhangsan2zhangsan2 + 3 | 1 | zhangsan3 | 3 | 1 | zhangsan3zhangsan3 + 3 | 1 | zhangsan3 | 4 | 1 | zhangsan4zhangsan4 + 4 | 1 | zhangsan4 | 1 | 1 | zhangsan1zhangsan1 + 4 | 1 | zhangsan4 | 2 | 1 | zhangsan2zhangsan2 + 4 | 1 | zhangsan4 | 3 | 1 | zhangsan3zhangsan3 + 4 | 1 | zhangsan4 | 4 | 1 | zhangsan4zhangsan4 + 5 | 2 | lisi1 | 5 | 2 | lisi1lisi1 + 5 | 2 | lisi1 | 6 | 2 | lisi2lisi2 + 5 | 2 | lisi1 | 7 | 2 | lisi3lisi3 + 5 | 2 | lisi1 | 8 | 2 | lisi4lisi4 + 6 | 2 | lisi2 | 5 | 2 | lisi1lisi1 + 6 | 2 | lisi2 | 6 | 2 | lisi2lisi2 + 6 | 2 | lisi2 | 7 | 2 | lisi3lisi3 + 6 | 2 | lisi2 | 8 | 2 | lisi4lisi4 + 7 | 2 | lisi3 | 5 | 2 | lisi1lisi1 + 7 | 2 | lisi3 | 6 | 2 | lisi2lisi2 + 7 | 2 | lisi3 | 7 | 2 | lisi3lisi3 + 7 | 2 | lisi3 | 8 | 2 | lisi4lisi4 + 8 | 2 | lisi4 | 5 | 2 | lisi1lisi1 + 8 | 2 | lisi4 | 6 | 2 | lisi2lisi2 + 8 | 2 | lisi4 | 7 | 2 | lisi3lisi3 + 8 | 2 | lisi4 | 8 | 2 | lisi4lisi4 + 9 | 3 | wangwu1 | 9 | 3 | wangwu1wangwu1 + 9 | 3 | wangwu1 | 10 | 3 | wangwu2wangwu2 + 9 | 3 | wangwu1 | 11 | 3 | wangwu3wangwu3 + 9 | 3 | wangwu1 | 12 | 3 | wangwu4wangwu4 + 10 | 3 | wangwu2 | 9 | 3 | wangwu1wangwu1 + 10 | 3 | wangwu2 | 10 | 3 | wangwu2wangwu2 + 10 | 3 | wangwu2 | 11 | 3 | wangwu3wangwu3 + 10 | 3 | wangwu2 | 12 | 3 | wangwu4wangwu4 + 11 | 3 | wangwu3 | 9 | 3 | wangwu1wangwu1 + 11 | 3 | wangwu3 | 10 | 3 | wangwu2wangwu2 + 11 | 3 | wangwu3 | 11 | 3 | wangwu3wangwu3 + 11 | 3 | wangwu3 | 12 | 3 | wangwu4wangwu4 + 12 | 3 | wangwu4 | 9 | 3 | wangwu1wangwu1 + 12 | 3 | wangwu4 | 10 | 3 | wangwu2wangwu2 + 12 | 3 | wangwu4 | 11 | 3 | wangwu3wangwu3 + 12 | 3 | wangwu4 | 12 | 3 | wangwu4wangwu4 + 13 | 4 | heliu1 | 13 | 4 | heliu1heliu1 + 13 | 4 | heliu1 | 14 | 4 | heliu2heliu2 + 13 | 4 | heliu1 | 15 | 4 | heliu3heliu3 + 13 | 4 | heliu1 | 16 | 4 | heliu4heliu4 + 14 | 4 | heliu2 | 13 | 4 | heliu1heliu1 + 14 | 4 | heliu2 | 14 | 4 | heliu2heliu2 + 14 | 4 | heliu2 | 15 | 4 | heliu3heliu3 + 14 | 4 | heliu2 | 16 | 4 | heliu4heliu4 + 15 | 4 | heliu3 | 13 | 4 | heliu1heliu1 + 15 | 4 | heliu3 | 14 | 4 | heliu2heliu2 + 15 | 4 | heliu3 | 15 | 4 | heliu3heliu3 + 15 | 4 | heliu3 | 16 | 4 | heliu4heliu4 + 16 | 4 | heliu4 | 13 | 4 | heliu1heliu1 + 16 | 4 | heliu4 | 14 | 4 | heliu2heliu2 + 16 | 4 | heliu4 | 15 | 4 | heliu3heliu3 + 16 | 4 | heliu4 | 16 | 4 | heliu4heliu4 + 17 | 5 | chenqi1 | 17 | 5 | chenqi1chenqi1 + 17 | 5 | chenqi1 | 18 | 5 | chenqi2chenqi2 + 17 | 5 | chenqi1 | 19 | 5 | chenqi3chenqi3 + 17 | 5 | chenqi1 | 20 | 5 | chenqi4chenqi4 + 18 | 5 | chenqi2 | 17 | 5 | chenqi1chenqi1 + 18 | 5 | chenqi2 | 18 | 5 | chenqi2chenqi2 + 18 | 5 | chenqi2 | 19 | 5 | chenqi3chenqi3 + 18 | 5 | chenqi2 | 20 | 5 | chenqi4chenqi4 + 19 | 5 | chenqi3 | 17 | 5 | chenqi1chenqi1 + 19 | 5 | chenqi3 | 18 | 5 | chenqi2chenqi2 + 19 | 5 | chenqi3 | 19 | 5 | chenqi3chenqi3 + 19 | 5 | chenqi3 | 20 | 5 | chenqi4chenqi4 + 20 | 5 | chenqi4 | 17 | 5 | chenqi1chenqi1 + 20 | 5 | chenqi4 | 18 | 5 | chenqi2chenqi2 + 20 | 5 | chenqi4 | 19 | 5 | chenqi3chenqi3 + 20 | 5 | chenqi4 | 20 | 5 | chenqi4chenqi4 +(80 rows) + +SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +-- view +create view v1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; +create view v2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; +create view v3 as select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + +create view v4 as select count(*) from tenk1 a, lateral generate_series(1,two) g; +select * from v1; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from v2; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from v3; + vx | vy +-------------------+------------------- + 123 | + 456 | + 123 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 123 | 4567890123456789 + 4567890123456789 | 123 + 123 | 456 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | + -4567890123456789 | +(20 rows) + +select * from v4; + count +------- + 5000 +(1 row) + +-- plsql +create or replace procedure plpgsql_1 (param1 varchar, param2 varchar, param3 varchar, param4 varchar ) +IS + BEGIN + create table tt1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN (param1, param2, param3) + ORDER BY d.department_name, v.employee_id; +END; +/ +create or replace procedure plpgsql_2 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d OUTER APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN (param1, param2, param3) + ORDER BY d.department_name, v.employee_id; +END; +/ +create or replace procedure plpgsql_3 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt3 as select id from (values (0), (1)) v(id), + lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; +END; +/ +create or replace procedure plpgsql_4 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt4 as select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; +END; +/ +call plpgsql_1(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + plpgsql_1 +----------- + +(1 row) + +call plpgsql_2(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + plpgsql_2 +----------- + +(1 row) + +call plpgsql_3(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + plpgsql_3 +----------- + +(1 row) + +call plpgsql_4(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + plpgsql_4 +----------- + +(1 row) + +select * from tt1; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from tt2; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from tt3; + id +---- + 0 + 0 + 0 +(3 rows) + +select * from tt4; + q1 | q2 | bq1 | cq1 | least +------------------+-------------------+------------------+------------------+------------------ + 123 | 456 | | | + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | | | +(42 rows) + +drop PROCEDURE plpgsql_1; +drop PROCEDURE plpgsql_2; +drop PROCEDURE plpgsql_3; +drop PROCEDURE plpgsql_4; +-- cursor expression +create or replace procedure test_cursor_1 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT e.last_name, CURSOR(SELECT d.department_name FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') ORDER BY d.department_name, v.employee_id) abc FROM employees e; +begin + OPEN c1; + loop + fetch c1 into company_name, my_cur; + exit when c1%notfound; + raise notice 'company_name : % %',company_name, my_cur; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_1(); +NOTICE: company_name : zhangsan1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations + test_cursor_1 +--------------- + +(1 row) + +drop procedure test_cursor_1; +create or replace procedure test_cursor_2 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT e.last_name, CURSOR(SELECT d.department_name FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') ORDER BY d.department_name, v.employee_id) abc FROM employees e; +begin + OPEN c1; + loop + fetch c1 into company_name, my_cur; + exit when c1%notfound; + raise notice 'company_name : % %',company_name, my_cur; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_2(); +NOTICE: company_name : zhangsan1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations + test_cursor_2 +--------------- + +(1 row) + +drop procedure test_cursor_2; +create or replace procedure test_cursor_3 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT CURSOR(select xq1::varchar from int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2)) abc; + begin + OPEN c1; + loop + fetch c1 into my_cur; + exit when c1%notfound; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_3(); +NOTICE: last_name_name : 123 +NOTICE: last_name_name : 123 +NOTICE: last_name_name : 123 +NOTICE: last_name_name : 123 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 + test_cursor_3 +--------------- + +(1 row) + +drop procedure test_cursor_3; +drop table tt1; +drop table tt2; +drop table tt3; +drop table tt4; +drop function fn_salar; +drop view v1; +drop view v2; +drop view v3; +drop view v4; +drop table tenk1; +drop table INT4_TBL; +drop table INT8_TBL; +drop table INT2_TBL; +drop table departments; +drop table employees; +-- clean +drop schema if exists lateral_test cascade; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table join_pt1 +drop cascades to table join_ut1 diff --git a/src/test/regress/output/lateral_dump.source b/src/test/regress/output/lateral_dump.source new file mode 100644 index 000000000..7ab9efbf1 --- /dev/null +++ b/src/test/regress/output/lateral_dump.source @@ -0,0 +1,220 @@ +create database lateral_dump; +\c lateral_dump +create schema lateral_dump; +set search_path to lateral_dump; +CREATE TABLE tenk1 ( + unique1 int4, + unique2 int4, + two int4, + four int4, + ten int4, + twenty int4, + hundred int4, + thousand int4, + twothousand int4, + fivethous int4, + tenthous int4, + odd int4, + even int4, + stringu1 name, + stringu2 name, + string4 name +); +CREATE TABLE INT4_TBL(f1 int4); +INSERT INTO INT4_TBL(f1) VALUES + (' 0 '), + ('123456 '), + (' -123456'), + ('2147483647'), -- largest and smallest values + ('-2147483647'); +VACUUM INT4_TBL; +CREATE TABLE INT8_TBL(q1 int8, q2 int8); +INSERT INTO INT8_TBL VALUES + (' 123 ',' 456'), + ('123 ','4567890123456789'), + ('4567890123456789','123'), + (+4567890123456789,'4567890123456789'), + ('+4567890123456789','-4567890123456789'); +VACUUM INT8_TBL; +CREATE TABLE INT2_TBL(f1 int2); +INSERT INTO INT2_TBL(f1) VALUES + ('0 '), + (' 1234 '), + (' -1234'), + ('32767'), -- largest and smallest values + ('-32767'); +VACUUM INT2_TBL; +create table departments(department_name varchar(50), department_id int); +create table employees(employee_id int, department_id int, last_name varchar(50)); +insert into departments values ('Marketing', 1); +insert into departments values ('Public Relations', 2); +insert into departments values ('Operations', 3); +insert into departments values ('Develop', 4); +insert into departments values ('Research', 5); +insert into departments values ('CEO', 6); +insert into departments values ('CFO', 7); +insert into employees values(1, 1, 'zhangsan1'); +insert into employees values(2, 1, 'zhangsan2'); +insert into employees values(3, 1, 'zhangsan3'); +insert into employees values(4, 1, 'zhangsan4'); +insert into employees values(5, 2, 'lisi1'); +insert into employees values(6, 2, 'lisi2'); +insert into employees values(7, 2, 'lisi3'); +insert into employees values(8, 2, 'lisi4'); +insert into employees values(9, 3, 'wangwu1'); +insert into employees values(10, 3, 'wangwu2'); +insert into employees values(11, 3, 'wangwu3'); +insert into employees values(12, 3, 'wangwu4'); +insert into employees values(13, 4, 'heliu1'); +insert into employees values(14, 4, 'heliu2'); +insert into employees values(15, 4, 'heliu3'); +insert into employees values(16, 4, 'heliu4'); +insert into employees values(17, 5, 'chenqi1'); +insert into employees values(18, 5, 'chenqi2'); +insert into employees values(19, 5, 'chenqi3'); +insert into employees values(20, 5, 'chenqi4'); +create view v1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; +create view v2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; +create view v3 as select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + +create view v4 as select count(*) from tenk1 a, lateral generate_series(1,two) g; +\! @abs_bindir@/gs_dump lateral_dump -p @portstring@ -f @abs_bindir@/lateral_dump.sql -n lateral_dump -w >/dev/null 2>&1; echo $? +0 +drop schema lateral_dump cascade; +NOTICE: drop cascades to 10 other objects +DETAIL: drop cascades to table tenk1 +drop cascades to table int4_tbl +drop cascades to table int8_tbl +drop cascades to table int2_tbl +drop cascades to table departments +drop cascades to table employees +drop cascades to view v1 +drop cascades to view v2 +drop cascades to view v3 +drop cascades to view v4 +\! @abs_bindir@/gsql -dlateral_dump -p @portstring@ -f "@abs_bindir@/lateral_dump.sql"; +SET +SET +SET +SET +SET +SET +SET +SET +SET +SET +CREATE SCHEMA +ALTER SCHEMA +SET +SET +SET +CREATE TABLE +ALTER TABLE +CREATE TABLE +ALTER TABLE +CREATE TABLE +ALTER TABLE +CREATE TABLE +ALTER TABLE +CREATE TABLE +ALTER TABLE +CREATE TABLE +ALTER TABLE +CREATE VIEW +ALTER VIEW +CREATE VIEW +ALTER VIEW +CREATE VIEW +ALTER VIEW +CREATE VIEW +ALTER VIEW +--?.* +-- lateral dump check +select * from v1; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from v2; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from v3; + vx | vy +-------------------+------------------- + 123 | + 456 | + 123 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 123 | 4567890123456789 + 4567890123456789 | 123 + 123 | 456 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | + -4567890123456789 | +(20 rows) + +select * from v4; + count +------- + 0 +(1 row) + +drop schema lateral_dump cascade; +--?.* +--?.* +--?.* +--?.* +--?.* +--?.* +--?.* +--?.* +--?.* +--?.* +--?.* +\c postgres +drop database lateral_dump; diff --git a/src/test/regress/output/lateral_with_dop.source b/src/test/regress/output/lateral_with_dop.source new file mode 100644 index 000000000..f9a824484 --- /dev/null +++ b/src/test/regress/output/lateral_with_dop.source @@ -0,0 +1,2669 @@ +drop schema if exists lateral_with_dop_test; +NOTICE: schema "lateral_with_dop_test" does not exist, skipping +create schema lateral_with_dop_test; +set search_path=lateral_with_dop_test; +SET query_dop = 4; +CREATE TABLE tenk1 ( + unique1 int4, + unique2 int4, + two int4, + four int4, + ten int4, + twenty int4, + hundred int4, + thousand int4, + twothousand int4, + fivethous int4, + tenthous int4, + odd int4, + even int4, + stringu1 name, + stringu2 name, + string4 name +); +COPY tenk1 FROM '@abs_srcdir@/data/tenk.data'; +CREATE TABLE INT4_TBL(f1 int4); +INSERT INTO INT4_TBL(f1) VALUES + (' 0 '), + ('123456 '), + (' -123456'), + ('2147483647'), -- largest and smallest values + ('-2147483647'); +VACUUM INT4_TBL; +CREATE TABLE INT8_TBL(q1 int8, q2 int8); +INSERT INTO INT8_TBL VALUES + (' 123 ',' 456'), + ('123 ','4567890123456789'), + ('4567890123456789','123'), + (+4567890123456789,'4567890123456789'), + ('+4567890123456789','-4567890123456789'); +VACUUM INT8_TBL; +CREATE TABLE INT2_TBL(f1 int2); +INSERT INTO INT2_TBL(f1) VALUES + ('0 '), + (' 1234 '), + (' -1234'), + ('32767'), -- largest and smallest values + ('-32767'); +VACUUM INT2_TBL; +-- +-- Test LATERAL +-- +select unique2, x.* +from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; + unique2 | f1 +---------+---- + 9998 | 0 +(1 row) + +explain (costs off) + select unique2, x.* + from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; + QUERY PLAN +------------------------------------ + Hash Join + Hash Cond: (a.unique1 = b.f1) + -> Seq Scan on tenk1 a + -> Hash + -> Seq Scan on int4_tbl b +(5 rows) + +select unique2, x.* +from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; + unique2 | f1 +---------+---- + 9998 | 0 +(1 row) + +explain (costs off) + select unique2, x.* + from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; + QUERY PLAN +------------------------------------- + Hash Join + Hash Cond: (tenk1.unique1 = x.f1) + -> Seq Scan on tenk1 + -> Hash + -> Seq Scan on int4_tbl x +(5 rows) + +explain (costs off) + select unique2, x.* + from int4_tbl x cross join lateral (select unique2 from tenk1 where f1 = unique1) ss; + QUERY PLAN +------------------------------------- + Hash Join + Hash Cond: (tenk1.unique1 = x.f1) + -> Seq Scan on tenk1 + -> Hash + -> Seq Scan on int4_tbl x +(5 rows) + +select unique2, x.* +from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; + unique2 | f1 +---------+------------- + 9998 | 0 + | 123456 + | -2147483647 + | 2147483647 + | -123456 +(5 rows) + +explain (costs off) + select unique2, x.* + from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; + QUERY PLAN +------------------------------------- + Hash Right Join + Hash Cond: (tenk1.unique1 = x.f1) + -> Seq Scan on tenk1 + -> Hash + -> Seq Scan on int4_tbl x +(5 rows) + +-- check scoping of lateral versus parent references +-- the first of these should return int8_tbl.q2, the second int8_tbl.q1 +select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl; + q1 | q2 | r +------------------+-------------------+------------------- + 123 | 456 | 456 + 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | -4567890123456789 +(5 rows) + +select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl; + q1 | q2 | r +------------------+-------------------+------------------ + 123 | 456 | 123 + 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 +(5 rows) + +-- lateral with function in FROM +select count(*) from tenk1 a, lateral generate_series(1,two) g; + count +------- + 5000 +(1 row) + +explain (costs off) + select count(*) from tenk1 a, lateral generate_series(1,two) g; + QUERY PLAN +------------------------------------------------ + Aggregate + -> Nested Loop + -> Seq Scan on tenk1 a + -> Function Scan on generate_series g +(4 rows) + +explain (costs off) + select count(*) from tenk1 a cross join lateral generate_series(1,two) g; + QUERY PLAN +------------------------------------------------ + Aggregate + -> Nested Loop + -> Seq Scan on tenk1 a + -> Function Scan on generate_series g +(4 rows) + +-- don't need the explicit LATERAL keyword for functions +explain (costs off) + select count(*) from tenk1 a, generate_series(1,two) g; + QUERY PLAN +------------------------------------------------ + Aggregate + -> Nested Loop + -> Seq Scan on tenk1 a + -> Function Scan on generate_series g +(4 rows) + +-- lateral with UNION ALL subselect +explain (costs off) + select * from generate_series(100,200) g, + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss; + QUERY PLAN +------------------------------------------ + Nested Loop + -> Function Scan on generate_series g + -> Append + -> Seq Scan on int8_tbl a + Filter: (g.g = q1) + -> Seq Scan on int8_tbl b + Filter: (g.g = q2) +(7 rows) + +select * from generate_series(100,200) g, + lateral (select * from int8_tbl a where g = q1 union all + select * from int8_tbl b where g = q2) ss; + g | q1 | q2 +-----+------------------+------------------ + 123 | 123 | 456 + 123 | 123 | 4567890123456789 + 123 | 4567890123456789 | 123 +(3 rows) + +-- lateral with VALUES +explain (costs off) + select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; + QUERY PLAN +----------------------------------------------------- + Aggregate + -> Hash Join + Hash Cond: ("*VALUES*".column1 = b.unique2) + -> Nested Loop + -> Seq Scan on tenk1 a + -> Values Scan on "*VALUES*" + -> Hash + -> Seq Scan on tenk1 b +(8 rows) + +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; + count +------- + 10000 +(1 row) + +-- lateral with VALUES, no flattening possible +explain (costs off) + select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; + QUERY PLAN +----------------------------------------------------------------- + Aggregate + -> Streaming(type: LOCAL GATHER dop: 1/4) + -> Aggregate + -> Hash Join + Hash Cond: ("*VALUES*".column1 = b.unique2) + -> Streaming(type: BROADCAST dop: 4/1) + -> Nested Loop + -> Seq Scan on tenk1 a + -> Values Scan on "*VALUES*" + -> Hash + -> Seq Scan on tenk1 b +(11 rows) + +select count(*) from tenk1 a, + tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; + count +------- + 10000 +(1 row) + +-- lateral injecting a strange outer join condition +explain (costs off) + select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; + QUERY PLAN +------------------------------------------------ + Sort + Sort Key: a.q1, a.q2, x.q1, x.q2, ($0) + -> Nested Loop + -> Seq Scan on int8_tbl a + -> Hash Left Join + Hash Cond: (x.q2 = ($0)) + -> Seq Scan on int8_tbl x + -> Hash + -> Seq Scan on int4_tbl y +(9 rows) + +select * from int8_tbl a, + int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) + on x.q2 = ss.z + order by a.q1, a.q2, x.q1, x.q2, ss.z; + q1 | q2 | q1 | q2 | z +------------------+-------------------+------------------+-------------------+------------------ + 123 | 456 | 123 | 456 | + 123 | 456 | 123 | 4567890123456789 | + 123 | 456 | 4567890123456789 | -4567890123456789 | + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 123 | 123 + 123 | 456 | 4567890123456789 | 4567890123456789 | + 123 | 4567890123456789 | 123 | 456 | + 123 | 4567890123456789 | 123 | 4567890123456789 | + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | + 4567890123456789 | -4567890123456789 | 123 | 456 | + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 | + 4567890123456789 | -4567890123456789 | 4567890123456789 | 123 | + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | -4567890123456789 | + 4567890123456789 | 123 | 4567890123456789 | 123 | + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 456 | + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 +(57 rows) + +-- lateral reference to a join alias variable +select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (select x) ss2(y); + x | f1 | y +---+----+--- + 0 | 0 | 0 +(1 row) + +select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, + lateral (values(x)) ss2(y); + x | f1 | y +-------------+-------------+------------- + 0 | 0 | 0 + 123456 | 123456 | 123456 + -123456 | -123456 | -123456 + 2147483647 | 2147483647 | 2147483647 + -2147483647 | -2147483647 | -2147483647 +(5 rows) + +select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j, + lateral (select x) ss2(y); + x | f1 | y +---+----+--- + 0 | 0 | 0 +(1 row) + +-- lateral references requiring pullup +select * from (values(1)) x(lb), + lateral generate_series(lb,4) x4; + lb | x4 +----+---- + 1 | 1 + 1 | 2 + 1 | 3 + 1 | 4 +(4 rows) + +select * from (select f1/1000000000 from int4_tbl) x(lb), + lateral generate_series(lb,4) x4; + lb | x4 +--------------+---- + 0 | 0 + 0 | 1 + 0 | 2 + 0 | 3 + 0 | 4 + .000123456 | 0 + .000123456 | 1 + .000123456 | 2 + .000123456 | 3 + .000123456 | 4 + -.000123456 | 0 + -.000123456 | 1 + -.000123456 | 2 + -.000123456 | 3 + -.000123456 | 4 + 2.147483647 | 2 + 2.147483647 | 3 + 2.147483647 | 4 + -2.147483647 | -2 + -2.147483647 | -1 + -2.147483647 | 0 + -2.147483647 | 1 + -2.147483647 | 2 + -2.147483647 | 3 + -2.147483647 | 4 +(25 rows) + +select * from (values(1)) x(lb), + lateral (values(lb)) y(lbcopy); + lb | lbcopy +----+-------- + 1 | 1 +(1 row) + +select * from (values(1)) x(lb), + lateral (select lb from int4_tbl) y(lbcopy); + lb | lbcopy +----+-------- + 1 | 1 + 1 | 1 + 1 | 1 + 1 | 1 + 1 | 1 +(5 rows) + +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2); + q1 | q2 | q1 | q2 | xq1 | yq1 | yq2 +------------------+-------------------+------------------+-------------------+------------------+------------------+------------------- + 123 | 456 | | | 123 | | + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123 + 4567890123456789 | -4567890123456789 | | | 4567890123456789 | | +(10 rows) + +select * from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); + q1 | q2 | q1 | q2 | xq1 | yq1 | yq2 +------------------+-------------------+------------------+-------------------+------------------+------------------+------------------- + 123 | 456 | | | 123 | | + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123 + 4567890123456789 | -4567890123456789 | | | 4567890123456789 | | +(10 rows) + +select x.* from + int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, + lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); + q1 | q2 +------------------+------------------- + 123 | 456 + 123 | 4567890123456789 + 123 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 +(10 rows) + +select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + vx | vy +-------------------+------------------- + 123 | + 456 | + 123 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 123 | 4567890123456789 + 4567890123456789 | 123 + 123 | 456 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | + -4567890123456789 | +(20 rows) + +select v.* from + (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + vx | vy +-------------------+------------------- + 123 | + 456 | + 123 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 123 | 4567890123456789 + 4567890123456789 | 123 + 123 | 456 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | + -4567890123456789 | +(20 rows) + +explain (verbose, costs off) +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; + QUERY PLAN +---------------------------------------------------- + Nested Loop Left Join + Output: a.q1, a.q2, b.q1, b.q2, ($0) + -> Seq Scan on lateral_with_dop_test.int8_tbl a + Output: a.q1, a.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl b + Output: b.q1, b.q2, a.q2 + Filter: (a.q2 = b.q1) +(7 rows) + +select * from + int8_tbl a left join + lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; + q1 | q2 | q1 | q2 | x +------------------+-------------------+------------------+-------------------+------------------ + 123 | 456 | | | + 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | | | +(10 rows) + +explain (verbose, costs off) +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; + QUERY PLAN +-------------------------------------------------------------- + Nested Loop Left Join + Output: a.q1, a.q2, b.q1, b.q2, (COALESCE($0, 42::bigint)) + -> Seq Scan on lateral_with_dop_test.int8_tbl a + Output: a.q1, a.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl b + Output: b.q1, b.q2, COALESCE(a.q2, 42::bigint) + Filter: (a.q2 = b.q1) +(7 rows) + +select * from + int8_tbl a left join + lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; + q1 | q2 | q1 | q2 | x +------------------+-------------------+------------------+-------------------+------------------ + 123 | 456 | | | + 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 + 4567890123456789 | 123 | 123 | 456 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | | | +(10 rows) + +-- lateral can result in join conditions appearing below their +-- real semantic level +explain (verbose, costs off) +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; + QUERY PLAN +---------------------------------------------------------- + Hash Left Join + Output: i.f1, j.f1 + Hash Cond: (i.f1 = j.f1) + -> Seq Scan on lateral_with_dop_test.int4_tbl i + Output: i.f1 + -> Hash + Output: j.f1 + -> Seq Scan on lateral_with_dop_test.int2_tbl j + Output: j.f1 +(9 rows) + +select * from int4_tbl i left join + lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; + f1 | f1 +-------------+---- + 0 | 0 + 123456 | + -123456 | + 2147483647 | + -2147483647 | +(5 rows) + +explain (verbose, costs off) +select * from int4_tbl i left join + lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; + QUERY PLAN +---------------------------------------------------- + Nested Loop Left Join + Output: i.f1, (COALESCE($0)) + -> Seq Scan on lateral_with_dop_test.int4_tbl i + Output: i.f1, i.* + -> Seq Scan on lateral_with_dop_test.int2_tbl j + Output: j.f1, COALESCE(i.*) + Filter: (i.f1 = j.f1) +(7 rows) + +select * from int4_tbl i left join + lateral (select coalesce(i) from int2_tbl j where i.f1 = j.f1) k on true; + f1 | coalesce +-------------+---------- + 0 | (0) + 123456 | + -123456 | + 2147483647 | + -2147483647 | +(5 rows) + +explain (verbose, costs off) +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; + QUERY PLAN +---------------------------------------------------------------- + Nested Loop + Output: a.f1, b.f1, c.q1, c.q2 + -> Seq Scan on lateral_with_dop_test.int4_tbl a + Output: a.f1 + -> Hash Left Join + Output: b.f1, c.q1, c.q2 + Hash Cond: (b.f1 = c.q1) + -> Seq Scan on lateral_with_dop_test.int4_tbl b + Output: b.f1 + -> Hash + Output: c.q1, c.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl c + Output: c.q1, c.q2 + Filter: (a.f1 = c.q2) +(14 rows) + +select * from int4_tbl a, + lateral ( + select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) + ) ss; + f1 | f1 | q1 | q2 +-------------+-------------+----+---- + 0 | 0 | | + 0 | 123456 | | + 0 | -123456 | | + 0 | 2147483647 | | + 0 | -2147483647 | | + 123456 | 0 | | + 123456 | 123456 | | + 123456 | -123456 | | + 123456 | 2147483647 | | + 123456 | -2147483647 | | + -123456 | 0 | | + -123456 | 123456 | | + -123456 | -123456 | | + -123456 | 2147483647 | | + -123456 | -2147483647 | | + 2147483647 | 0 | | + 2147483647 | 123456 | | + 2147483647 | -123456 | | + 2147483647 | 2147483647 | | + 2147483647 | -2147483647 | | + -2147483647 | 0 | | + -2147483647 | 123456 | | + -2147483647 | -123456 | | + -2147483647 | 2147483647 | | + -2147483647 | -2147483647 | | +(25 rows) + +-- lateral reference in a PlaceHolderVar evaluated at join level +explain (verbose, costs off) +select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; + QUERY PLAN +---------------------------------------------------------------- + Nested Loop Left Join + Output: a.q1, a.q2, b.q1, c.q1, (LEAST($0, b.q1, c.q1)) + -> Seq Scan on lateral_with_dop_test.int8_tbl a + Output: a.q1, a.q2 + -> Nested Loop + Output: b.q1, c.q1, LEAST(a.q1, b.q1, c.q1) + Join Filter: (a.q2 = b.q1) + -> Seq Scan on lateral_with_dop_test.int8_tbl b + Output: b.q1, b.q2 + -> Materialize + Output: c.q1 + -> Seq Scan on lateral_with_dop_test.int8_tbl c + Output: c.q1 +(13 rows) + +select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; + q1 | q2 | bq1 | cq1 | least +------------------+-------------------+------------------+------------------+------------------ + 123 | 456 | | | + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | | | +(42 rows) + +-- case requiring nested PlaceHolderVars +explain (verbose, costs off) +select * from + int8_tbl c left join ( + int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select ss2.y offset 0) ss3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Nested Loop + Output: c.q1, c.q2, a.q1, a.q2, b.q1, (COALESCE(b.q2, 42::bigint)), d.q1, (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)), ($0) + -> Hash Right Join + Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, 42::bigint)), (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)) + Hash Cond: (d.q1 = c.q2) + -> Nested Loop + Output: a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, 42::bigint)), (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)) + -> Hash Left Join + Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, 42::bigint)) + Hash Cond: (a.q2 = b.q1) + -> Seq Scan on lateral_with_dop_test.int8_tbl a + Output: a.q1, a.q2 + -> Hash + Output: b.q1, (COALESCE(b.q2, 42::bigint)) + -> Seq Scan on lateral_with_dop_test.int8_tbl b + Output: b.q1, COALESCE(b.q2, 42::bigint) + -> Materialize + Output: d.q1, (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)) + -> Seq Scan on lateral_with_dop_test.int8_tbl d + Output: d.q1, COALESCE(COALESCE(b.q2, 42::bigint), d.q2) + -> Hash + Output: c.q1, c.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl c + Output: c.q1, c.q2 + -> Limit + Output: ($0) + -> Result + Output: (COALESCE(COALESCE(b.q2, 42::bigint), d.q2)) +(28 rows) + +-- case that breaks the old ph_may_need optimization +explain (verbose, costs off) +select c.*,a.*,ss1.q1,ss2.q1,ss3.* from + int8_tbl c left join ( + int8_tbl a left join + (select q1, coalesce(q2,f1) as x from int8_tbl b, int4_tbl b2 + where q1 < f1) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select * from int4_tbl i where ss2.y > f1) ss3; + QUERY PLAN +------------------------------------------------------------------------------- + Hash Right Join + Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, i.f1 + Hash Cond: (d.q1 = c.q2) + Filter: ((COALESCE($0, d.q2)) > i.f1) + -> Nested Loop + Output: a.q1, a.q2, b.q1, d.q1, (COALESCE($0, d.q2)) + -> Hash Right Join + Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, (b2.f1)::bigint)) + Hash Cond: (b.q1 = a.q2) + -> Nested Loop + Output: b.q1, COALESCE(b.q2, (b2.f1)::bigint) + Join Filter: (b.q1 < b2.f1) + -> Seq Scan on lateral_with_dop_test.int8_tbl b + Output: b.q1, b.q2 + -> Materialize + Output: b2.f1 + -> Seq Scan on lateral_with_dop_test.int4_tbl b2 + Output: b2.f1 + -> Hash + Output: a.q1, a.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl a + Output: a.q1, a.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl d + Output: d.q1, COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2) + -> Hash + Output: c.q1, c.q2, i.f1 + -> Nested Loop + Output: c.q1, c.q2, i.f1 + -> Seq Scan on lateral_with_dop_test.int8_tbl c + Output: c.q1, c.q2 + -> Materialize + Output: i.f1 + -> Seq Scan on lateral_with_dop_test.int4_tbl i + Output: i.f1 +(34 rows) + +-- check processing of postponed quals (bug #9041) +explain (verbose, costs off) +select * from + (select 1 as x offset 0) x cross join (select 2 as y offset 0) y + left join lateral ( + select * from (select 3 as z offset 0) z where z.z = x.x + ) zz on zz.z = y.y; + QUERY PLAN +---------------------------------------------- + Nested Loop Left Join + Output: (1), (2), (3) + Join Filter: (((3) = (1)) AND ((3) = (2))) + -> Nested Loop + Output: (1), (2) + -> Limit + Output: (1) + -> Result + Output: 1 + -> Limit + Output: (2) + -> Result + Output: 2 + -> Limit + Output: (3) + -> Result + Output: 3 +(17 rows) + +-- a new postponed-quals issue (bug #17768) +explain (costs off) +select * from int4_tbl t1, + lateral (select * from int4_tbl t2 inner join int4_tbl t3 on t1.f1 = 1 + inner join (int4_tbl t4 left join int4_tbl t5 on true) on true) ss; + QUERY PLAN +------------------------------------------------- + Nested Loop Left Join + -> Nested Loop + -> Nested Loop + -> Nested Loop + -> Seq Scan on int4_tbl t1 + Filter: (f1 = 1) + -> Seq Scan on int4_tbl t2 + -> Materialize + -> Seq Scan on int4_tbl t3 + -> Materialize + -> Seq Scan on int4_tbl t4 + -> Materialize + -> Seq Scan on int4_tbl t5 +(13 rows) + +-- check dummy rels with lateral references (bug #15694) +explain (verbose, costs off) +select * from int8_tbl i8 left join lateral + (select *, i8.q2 from int4_tbl where false) ss on true; + QUERY PLAN +-------------------------------------------------------- + Nested Loop Left Join + Output: i8.q1, i8.q2, int4_tbl.f1, ($0) + -> Seq Scan on lateral_with_dop_test.int8_tbl i8 + Output: i8.q1, i8.q2 + -> Result + Output: int4_tbl.f1, ($0) + One-Time Filter: false + -> Seq Scan on lateral_with_dop_test.int4_tbl + Output: int4_tbl.f1, i8.q2 +(9 rows) + +explain (verbose, costs off) +select * from int8_tbl i8 left join lateral + (select *, i8.q2 from int4_tbl i1, int4_tbl i2 where false) ss on true; + QUERY PLAN +----------------------------------------------------- + Nested Loop Left Join + Output: i8.q1, i8.q2, i1.f1, i2.f1, i8.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl i8 + Output: i8.q1, i8.q2 + -> Result + Output: i1.f1, i2.f1, i8.q2 + One-Time Filter: false +(7 rows) + +-- check handling of nested appendrels inside LATERAL +select * from + ((select 2 as v) union all (select 3 as v)) as q1 + cross join lateral + ((select * from + ((select 4 as v) union all (select 5 as v)) as q3) + union all + (select q1.v) + ) as q2; + v | v +---+--- + 2 | 4 + 2 | 5 + 2 | 2 + 3 | 4 + 3 | 5 + 3 | 3 +(6 rows) + +-- check the number of columns specified +SELECT * FROM (int8_tbl i cross join int4_tbl j) ss(a,b,c,d); +ERROR: column alias list for "ss" has too many entries +-- check we don't try to do a unique-ified semijoin with LATERAL +explain (verbose, costs off) +select * from + (values (0,9998), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; + QUERY PLAN +------------------------------------------------------------------ + Nested Loop + Output: "*VALUES*".column1, "*VALUES*".column2, int4_tbl.f1 + -> Values Scan on "*VALUES*" + Output: "*VALUES*".column1, "*VALUES*".column2 + -> Hash Right Semi Join + Output: int4_tbl.f1 + Hash Cond: (tenk1.unique1 = int4_tbl.f1) + -> Limit + Output: tenk1.unique1 + -> Seq Scan on lateral_with_dop_test.tenk1 + Output: tenk1.unique1 + Filter: (tenk1.unique2 = "*VALUES*".column2) + -> Hash + Output: int4_tbl.f1 + -> Seq Scan on lateral_with_dop_test.int4_tbl + Output: int4_tbl.f1 +(16 rows) + +select * from + (values (0,9998), (1,1000)) v(id,x), + lateral (select f1 from int4_tbl + where f1 = any (select unique1 from tenk1 + where unique2 = v.x offset 0)) ss; + id | x | f1 +----+------+---- + 0 | 9998 | 0 +(1 row) + +-- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, +-- but we can make the test case much more compact with LATERAL) +explain (verbose, costs off) +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + QUERY PLAN +------------------------------------------------------------------------------------- + Nested Loop + Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl t1 + Output: t1.q1, t1.q2 + -> Nested Loop + Output: "*VALUES*".column1, ss2.q1, ss2.q2 + -> Values Scan on "*VALUES*" + Output: "*VALUES*".column1 + -> Subquery Scan on ss2 + Output: ss2.q1, ss2.q2 + Filter: (t1.q1 = ss2.q2) + -> Limit + Output: t2.q1, t2.q2 + -> Seq Scan on lateral_with_dop_test.int8_tbl t2 + Output: t2.q1, t2.q2 + Filter: (SubPlan 3) + SubPlan 3 + -> Result + Output: t3.q2 + One-Time Filter: $4 + InitPlan 1 (returns $2) + -> Result + Output: GREATEST($0, t2.q2) + InitPlan 2 (returns $4) + -> Result + Output: ($3 = 0) + -> Seq Scan on lateral_with_dop_test.int8_tbl t3 + Output: t3.q1, t3.q2 + Filter: (t3.q2 = $2) +(29 rows) + +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + id | q1 | q2 | q1 | q2 +----+------------------+-------------------+------------------+------------------ + 0 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 0 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 0 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 +(3 rows) + +-- test some error cases where LATERAL should have been used but wasn't +select f1,g from int4_tbl a, (select f1 as g) ss; +ERROR: column "f1" does not exist +LINE 1: select f1,g from int4_tbl a, (select f1 as g) ss; + ^ +HINT: There is a column named "f1" in table "a", but it cannot be referenced from this part of the query. +CONTEXT: referenced column: g +select f1,g from int4_tbl a, (select a.f1 as g) ss; +ERROR: invalid reference to FROM-clause entry for table "a" +LINE 1: select f1,g from int4_tbl a, (select a.f1 as g) ss; + ^ +HINT: There is an entry for table "a", but it cannot be referenced from this part of the query. +CONTEXT: referenced column: g +select f1,g from int4_tbl a cross join (select f1 as g) ss; +ERROR: column "f1" does not exist +LINE 1: select f1,g from int4_tbl a cross join (select f1 as g) ss; + ^ +HINT: There is a column named "f1" in table "a", but it cannot be referenced from this part of the query. +CONTEXT: referenced column: g +select f1,g from int4_tbl a cross join (select a.f1 as g) ss; +ERROR: invalid reference to FROM-clause entry for table "a" +LINE 1: select f1,g from int4_tbl a cross join (select a.f1 as g) ss... + ^ +HINT: There is an entry for table "a", but it cannot be referenced from this part of the query. +CONTEXT: referenced column: g +-- SQL:2008 says the left table is in scope but illegal to access here +select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true; +ERROR: invalid reference to FROM-clause entry for table "a" +LINE 1: ... int4_tbl a right join lateral generate_series(0, a.f1) g on... + ^ +DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. +select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true; +ERROR: invalid reference to FROM-clause entry for table "a" +LINE 1: ...m int4_tbl a full join lateral generate_series(0, a.f1) g on... + ^ +DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. +-- check we complain about ambiguous table references +select * from + int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss); +ERROR: table reference "x" is ambiguous +LINE 2: ...cross join (int4_tbl x cross join lateral (select x.f1) ss); + ^ +CONTEXT: referenced column: f1 +-- LATERAL can be used to put an aggregate into the FROM clause of its query +select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss; +ERROR: aggregates not allowed in FROM clause +LINE 1: select 1 from tenk1 a, lateral (select max(a.unique1) from i... + ^ +CONTEXT: referenced column: max +-- check behavior of LATERAL in UPDATE/DELETE +create temp table xx1 as select f1 as x1, -f1 as x2 from int4_tbl; +-- error, can't do this: +update xx1 set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; +ERROR: column "x1" does not exist +LINE 1: ... set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; + ^ +HINT: There is a column named "x1" in table "xx1", but it cannot be referenced from this part of the query. +update xx1 set x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss; +ERROR: invalid reference to FROM-clause entry for table "xx1" +LINE 1: ...t x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss... + ^ +HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. +-- can't do it even with LATERAL: +update xx1 set x2 = f1 from lateral (select * from int4_tbl where f1 = x1) ss; +ERROR: invalid reference to FROM-clause entry for table "xx1" +LINE 1: ...= f1 from lateral (select * from int4_tbl where f1 = x1) ss; + ^ +DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. +-- we might in future allow something like this, but for now it's an error: +update xx1 set x2 = f1 from xx1, lateral (select * from int4_tbl where f1 = x1) ss; +ERROR: table name "xx1" specified more than once +-- also errors: +delete from xx1 using (select * from int4_tbl where f1 = x1) ss; +ERROR: column "x1" does not exist +LINE 1: ...te from xx1 using (select * from int4_tbl where f1 = x1) ss; + ^ +HINT: There is a column named "x1" in table "xx1", but it cannot be referenced from this part of the query. +delete from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss; +ERROR: invalid reference to FROM-clause entry for table "xx1" +LINE 1: ...from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss... + ^ +HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. +delete from xx1 using lateral (select * from int4_tbl where f1 = x1) ss; +ERROR: invalid reference to FROM-clause entry for table "xx1" +LINE 1: ...xx1 using lateral (select * from int4_tbl where f1 = x1) ss; + ^ +DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. +-- +-- test LATERAL reference propagation down a multi-level inheritance hierarchy +-- produced for a multi-level partitioned table hierarchy. +-- +create table join_pt1 (a int, b int, c varchar) partition by range(a)( +PARTITION P1 VALUES LESS THAN(100), PARTITION P2 VALUES LESS THAN(200), PARTITION P3 VALUES LESS THAN(MAXVALUE)); +insert into join_pt1 values (1, 1, 'x'), (101, 101, 'y'); +create table join_ut1 (a int, b int, c varchar); +insert into join_ut1 values (101, 101, 'y'), (2, 2, 'z'); +explain (verbose, costs off) +select t1.b, ss.phv from join_ut1 t1 left join lateral + (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv + from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss + on t1.a = ss.t2a order by t1.a; + QUERY PLAN +----------------------------------------------------------------------------------- + Sort + Output: t1.b, (LEAST($2, t2.a, t3.a)), t1.a + Sort Key: t1.a + -> Nested Loop Left Join + Output: t1.b, (LEAST($2, t2.a, t3.a)), t1.a + -> Seq Scan on lateral_with_dop_test.join_ut1 t1 + Output: t1.a, t1.b, t1.c + -> Hash Join + Output: t2.a, LEAST(t1.a, t2.a, t3.a) + Hash Cond: (t2.a = t3.b) + Join Filter: (t1.a = t2.a) + -> Partition Iterator + Output: t2.a, t2.b, t2.c + Iterations: 3 + -> Partitioned Seq Scan on lateral_with_dop_test.join_pt1 t2 + Output: t2.a, t2.b, t2.c + Selected Partitions: 1..3 + -> Hash + Output: t3.b, t3.a + -> Seq Scan on lateral_with_dop_test.join_ut1 t3 + Output: t3.b, t3.a +(21 rows) + +select t1.b, ss.phv from join_ut1 t1 left join lateral + (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv + from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss + on t1.a = ss.t2a order by t1.a; + b | phv +-----+----- + 2 | + 101 | 101 +(2 rows) + +-- cross apply +create table departments(department_name varchar(50), department_id int); +create table employees(employee_id int, department_id int, last_name varchar(50)); +insert into departments values ('Marketing', 1); +insert into departments values ('Public Relations', 2); +insert into departments values ('Operations', 3); +insert into departments values ('Develop', 4); +insert into departments values ('Research', 5); +insert into departments values ('CEO', 6); +insert into departments values ('CFO', 7); +insert into employees values(1, 1, 'zhangsan1'); +insert into employees values(2, 1, 'zhangsan2'); +insert into employees values(3, 1, 'zhangsan3'); +insert into employees values(4, 1, 'zhangsan4'); +insert into employees values(5, 2, 'lisi1'); +insert into employees values(6, 2, 'lisi2'); +insert into employees values(7, 2, 'lisi3'); +insert into employees values(8, 2, 'lisi4'); +insert into employees values(9, 3, 'wangwu1'); +insert into employees values(10, 3, 'wangwu2'); +insert into employees values(11, 3, 'wangwu3'); +insert into employees values(12, 3, 'wangwu4'); +insert into employees values(13, 4, 'heliu1'); +insert into employees values(14, 4, 'heliu2'); +insert into employees values(15, 4, 'heliu3'); +insert into employees values(16, 4, 'heliu4'); +insert into employees values(17, 5, 'chenqi1'); +insert into employees values(18, 5, 'chenqi2'); +insert into employees values(19, 5, 'chenqi3'); +insert into employees values(20, 5, 'chenqi4'); +create function fn_salar (departmentid int) returns table (employee_id int, department_id int, last_name varchar) language sql as 'select employee_id, department_id, concat(last_name,last_name) as last_name2 from employees WHERE department_id = departmentid'; +select* from departments d cross apply employees e where e.department_id = d.department_id; + department_name | department_id | employee_id | department_id | last_name +------------------+---------------+-------------+---------------+----------- + Marketing | 1 | 4 | 1 | zhangsan4 + Marketing | 1 | 3 | 1 | zhangsan3 + Marketing | 1 | 2 | 1 | zhangsan2 + Marketing | 1 | 1 | 1 | zhangsan1 + Public Relations | 2 | 8 | 2 | lisi4 + Public Relations | 2 | 7 | 2 | lisi3 + Public Relations | 2 | 6 | 2 | lisi2 + Public Relations | 2 | 5 | 2 | lisi1 + Operations | 3 | 12 | 3 | wangwu4 + Operations | 3 | 11 | 3 | wangwu3 + Operations | 3 | 10 | 3 | wangwu2 + Operations | 3 | 9 | 3 | wangwu1 + Develop | 4 | 16 | 4 | heliu4 + Develop | 4 | 15 | 4 | heliu3 + Develop | 4 | 14 | 4 | heliu2 + Develop | 4 | 13 | 4 | heliu1 + Research | 5 | 20 | 5 | chenqi4 + Research | 5 | 19 | 5 | chenqi3 + Research | 5 | 18 | 5 | chenqi2 + Research | 5 | 17 | 5 | chenqi1 +(20 rows) + +select* from departments d cross apply (select d.department_id from employees x) e where e.department_id = d.department_id; + department_name | department_id | department_id +------------------+---------------+--------------- + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 +(140 rows) + +SELECT * FROM employees AS e CROSS APPLY fn_Salar(e.department_id) AS f; + employee_id | department_id | last_name | employee_id | department_id | last_name +-------------+---------------+-----------+-------------+---------------+-------------------- + 1 | 1 | zhangsan1 | 1 | 1 | zhangsan1zhangsan1 + 1 | 1 | zhangsan1 | 2 | 1 | zhangsan2zhangsan2 + 1 | 1 | zhangsan1 | 3 | 1 | zhangsan3zhangsan3 + 1 | 1 | zhangsan1 | 4 | 1 | zhangsan4zhangsan4 + 2 | 1 | zhangsan2 | 1 | 1 | zhangsan1zhangsan1 + 2 | 1 | zhangsan2 | 2 | 1 | zhangsan2zhangsan2 + 2 | 1 | zhangsan2 | 3 | 1 | zhangsan3zhangsan3 + 2 | 1 | zhangsan2 | 4 | 1 | zhangsan4zhangsan4 + 3 | 1 | zhangsan3 | 1 | 1 | zhangsan1zhangsan1 + 3 | 1 | zhangsan3 | 2 | 1 | zhangsan2zhangsan2 + 3 | 1 | zhangsan3 | 3 | 1 | zhangsan3zhangsan3 + 3 | 1 | zhangsan3 | 4 | 1 | zhangsan4zhangsan4 + 4 | 1 | zhangsan4 | 1 | 1 | zhangsan1zhangsan1 + 4 | 1 | zhangsan4 | 2 | 1 | zhangsan2zhangsan2 + 4 | 1 | zhangsan4 | 3 | 1 | zhangsan3zhangsan3 + 4 | 1 | zhangsan4 | 4 | 1 | zhangsan4zhangsan4 + 5 | 2 | lisi1 | 5 | 2 | lisi1lisi1 + 5 | 2 | lisi1 | 6 | 2 | lisi2lisi2 + 5 | 2 | lisi1 | 7 | 2 | lisi3lisi3 + 5 | 2 | lisi1 | 8 | 2 | lisi4lisi4 + 6 | 2 | lisi2 | 5 | 2 | lisi1lisi1 + 6 | 2 | lisi2 | 6 | 2 | lisi2lisi2 + 6 | 2 | lisi2 | 7 | 2 | lisi3lisi3 + 6 | 2 | lisi2 | 8 | 2 | lisi4lisi4 + 7 | 2 | lisi3 | 5 | 2 | lisi1lisi1 + 7 | 2 | lisi3 | 6 | 2 | lisi2lisi2 + 7 | 2 | lisi3 | 7 | 2 | lisi3lisi3 + 7 | 2 | lisi3 | 8 | 2 | lisi4lisi4 + 8 | 2 | lisi4 | 5 | 2 | lisi1lisi1 + 8 | 2 | lisi4 | 6 | 2 | lisi2lisi2 + 8 | 2 | lisi4 | 7 | 2 | lisi3lisi3 + 8 | 2 | lisi4 | 8 | 2 | lisi4lisi4 + 9 | 3 | wangwu1 | 9 | 3 | wangwu1wangwu1 + 9 | 3 | wangwu1 | 10 | 3 | wangwu2wangwu2 + 9 | 3 | wangwu1 | 11 | 3 | wangwu3wangwu3 + 9 | 3 | wangwu1 | 12 | 3 | wangwu4wangwu4 + 10 | 3 | wangwu2 | 9 | 3 | wangwu1wangwu1 + 10 | 3 | wangwu2 | 10 | 3 | wangwu2wangwu2 + 10 | 3 | wangwu2 | 11 | 3 | wangwu3wangwu3 + 10 | 3 | wangwu2 | 12 | 3 | wangwu4wangwu4 + 11 | 3 | wangwu3 | 9 | 3 | wangwu1wangwu1 + 11 | 3 | wangwu3 | 10 | 3 | wangwu2wangwu2 + 11 | 3 | wangwu3 | 11 | 3 | wangwu3wangwu3 + 11 | 3 | wangwu3 | 12 | 3 | wangwu4wangwu4 + 12 | 3 | wangwu4 | 9 | 3 | wangwu1wangwu1 + 12 | 3 | wangwu4 | 10 | 3 | wangwu2wangwu2 + 12 | 3 | wangwu4 | 11 | 3 | wangwu3wangwu3 + 12 | 3 | wangwu4 | 12 | 3 | wangwu4wangwu4 + 13 | 4 | heliu1 | 13 | 4 | heliu1heliu1 + 13 | 4 | heliu1 | 14 | 4 | heliu2heliu2 + 13 | 4 | heliu1 | 15 | 4 | heliu3heliu3 + 13 | 4 | heliu1 | 16 | 4 | heliu4heliu4 + 14 | 4 | heliu2 | 13 | 4 | heliu1heliu1 + 14 | 4 | heliu2 | 14 | 4 | heliu2heliu2 + 14 | 4 | heliu2 | 15 | 4 | heliu3heliu3 + 14 | 4 | heliu2 | 16 | 4 | heliu4heliu4 + 15 | 4 | heliu3 | 13 | 4 | heliu1heliu1 + 15 | 4 | heliu3 | 14 | 4 | heliu2heliu2 + 15 | 4 | heliu3 | 15 | 4 | heliu3heliu3 + 15 | 4 | heliu3 | 16 | 4 | heliu4heliu4 + 16 | 4 | heliu4 | 13 | 4 | heliu1heliu1 + 16 | 4 | heliu4 | 14 | 4 | heliu2heliu2 + 16 | 4 | heliu4 | 15 | 4 | heliu3heliu3 + 16 | 4 | heliu4 | 16 | 4 | heliu4heliu4 + 17 | 5 | chenqi1 | 17 | 5 | chenqi1chenqi1 + 17 | 5 | chenqi1 | 18 | 5 | chenqi2chenqi2 + 17 | 5 | chenqi1 | 19 | 5 | chenqi3chenqi3 + 17 | 5 | chenqi1 | 20 | 5 | chenqi4chenqi4 + 18 | 5 | chenqi2 | 17 | 5 | chenqi1chenqi1 + 18 | 5 | chenqi2 | 18 | 5 | chenqi2chenqi2 + 18 | 5 | chenqi2 | 19 | 5 | chenqi3chenqi3 + 18 | 5 | chenqi2 | 20 | 5 | chenqi4chenqi4 + 19 | 5 | chenqi3 | 17 | 5 | chenqi1chenqi1 + 19 | 5 | chenqi3 | 18 | 5 | chenqi2chenqi2 + 19 | 5 | chenqi3 | 19 | 5 | chenqi3chenqi3 + 19 | 5 | chenqi3 | 20 | 5 | chenqi4chenqi4 + 20 | 5 | chenqi4 | 17 | 5 | chenqi1chenqi1 + 20 | 5 | chenqi4 | 18 | 5 | chenqi2chenqi2 + 20 | 5 | chenqi4 | 19 | 5 | chenqi3chenqi3 + 20 | 5 | chenqi4 | 20 | 5 | chenqi4chenqi4 +(80 rows) + + +SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + + +select* from departments d outer apply employees e where e.department_id = d.department_id; + department_name | department_id | employee_id | department_id | last_name +------------------+---------------+-------------+---------------+----------- + Marketing | 1 | 4 | 1 | zhangsan4 + Marketing | 1 | 3 | 1 | zhangsan3 + Marketing | 1 | 2 | 1 | zhangsan2 + Marketing | 1 | 1 | 1 | zhangsan1 + Public Relations | 2 | 8 | 2 | lisi4 + Public Relations | 2 | 7 | 2 | lisi3 + Public Relations | 2 | 6 | 2 | lisi2 + Public Relations | 2 | 5 | 2 | lisi1 + Operations | 3 | 12 | 3 | wangwu4 + Operations | 3 | 11 | 3 | wangwu3 + Operations | 3 | 10 | 3 | wangwu2 + Operations | 3 | 9 | 3 | wangwu1 + Develop | 4 | 16 | 4 | heliu4 + Develop | 4 | 15 | 4 | heliu3 + Develop | 4 | 14 | 4 | heliu2 + Develop | 4 | 13 | 4 | heliu1 + Research | 5 | 20 | 5 | chenqi4 + Research | 5 | 19 | 5 | chenqi3 + Research | 5 | 18 | 5 | chenqi2 + Research | 5 | 17 | 5 | chenqi1 +(20 rows) + +select* from departments d cross apply (select d.department_id from employees x) e where e.department_id = d.department_id; + department_name | department_id | department_id +------------------+---------------+--------------- + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Marketing | 1 | 1 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Public Relations | 2 | 2 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Operations | 3 | 3 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Develop | 4 | 4 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + Research | 5 | 5 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CEO | 6 | 6 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 + CFO | 7 | 7 +(140 rows) + +SELECT * FROM employees AS e outer APPLY fn_Salar(e.department_id) AS f; + employee_id | department_id | last_name | employee_id | department_id | last_name +-------------+---------------+-----------+-------------+---------------+-------------------- + 1 | 1 | zhangsan1 | 1 | 1 | zhangsan1zhangsan1 + 1 | 1 | zhangsan1 | 2 | 1 | zhangsan2zhangsan2 + 1 | 1 | zhangsan1 | 3 | 1 | zhangsan3zhangsan3 + 1 | 1 | zhangsan1 | 4 | 1 | zhangsan4zhangsan4 + 2 | 1 | zhangsan2 | 1 | 1 | zhangsan1zhangsan1 + 2 | 1 | zhangsan2 | 2 | 1 | zhangsan2zhangsan2 + 2 | 1 | zhangsan2 | 3 | 1 | zhangsan3zhangsan3 + 2 | 1 | zhangsan2 | 4 | 1 | zhangsan4zhangsan4 + 3 | 1 | zhangsan3 | 1 | 1 | zhangsan1zhangsan1 + 3 | 1 | zhangsan3 | 2 | 1 | zhangsan2zhangsan2 + 3 | 1 | zhangsan3 | 3 | 1 | zhangsan3zhangsan3 + 3 | 1 | zhangsan3 | 4 | 1 | zhangsan4zhangsan4 + 4 | 1 | zhangsan4 | 1 | 1 | zhangsan1zhangsan1 + 4 | 1 | zhangsan4 | 2 | 1 | zhangsan2zhangsan2 + 4 | 1 | zhangsan4 | 3 | 1 | zhangsan3zhangsan3 + 4 | 1 | zhangsan4 | 4 | 1 | zhangsan4zhangsan4 + 5 | 2 | lisi1 | 5 | 2 | lisi1lisi1 + 5 | 2 | lisi1 | 6 | 2 | lisi2lisi2 + 5 | 2 | lisi1 | 7 | 2 | lisi3lisi3 + 5 | 2 | lisi1 | 8 | 2 | lisi4lisi4 + 6 | 2 | lisi2 | 5 | 2 | lisi1lisi1 + 6 | 2 | lisi2 | 6 | 2 | lisi2lisi2 + 6 | 2 | lisi2 | 7 | 2 | lisi3lisi3 + 6 | 2 | lisi2 | 8 | 2 | lisi4lisi4 + 7 | 2 | lisi3 | 5 | 2 | lisi1lisi1 + 7 | 2 | lisi3 | 6 | 2 | lisi2lisi2 + 7 | 2 | lisi3 | 7 | 2 | lisi3lisi3 + 7 | 2 | lisi3 | 8 | 2 | lisi4lisi4 + 8 | 2 | lisi4 | 5 | 2 | lisi1lisi1 + 8 | 2 | lisi4 | 6 | 2 | lisi2lisi2 + 8 | 2 | lisi4 | 7 | 2 | lisi3lisi3 + 8 | 2 | lisi4 | 8 | 2 | lisi4lisi4 + 9 | 3 | wangwu1 | 9 | 3 | wangwu1wangwu1 + 9 | 3 | wangwu1 | 10 | 3 | wangwu2wangwu2 + 9 | 3 | wangwu1 | 11 | 3 | wangwu3wangwu3 + 9 | 3 | wangwu1 | 12 | 3 | wangwu4wangwu4 + 10 | 3 | wangwu2 | 9 | 3 | wangwu1wangwu1 + 10 | 3 | wangwu2 | 10 | 3 | wangwu2wangwu2 + 10 | 3 | wangwu2 | 11 | 3 | wangwu3wangwu3 + 10 | 3 | wangwu2 | 12 | 3 | wangwu4wangwu4 + 11 | 3 | wangwu3 | 9 | 3 | wangwu1wangwu1 + 11 | 3 | wangwu3 | 10 | 3 | wangwu2wangwu2 + 11 | 3 | wangwu3 | 11 | 3 | wangwu3wangwu3 + 11 | 3 | wangwu3 | 12 | 3 | wangwu4wangwu4 + 12 | 3 | wangwu4 | 9 | 3 | wangwu1wangwu1 + 12 | 3 | wangwu4 | 10 | 3 | wangwu2wangwu2 + 12 | 3 | wangwu4 | 11 | 3 | wangwu3wangwu3 + 12 | 3 | wangwu4 | 12 | 3 | wangwu4wangwu4 + 13 | 4 | heliu1 | 13 | 4 | heliu1heliu1 + 13 | 4 | heliu1 | 14 | 4 | heliu2heliu2 + 13 | 4 | heliu1 | 15 | 4 | heliu3heliu3 + 13 | 4 | heliu1 | 16 | 4 | heliu4heliu4 + 14 | 4 | heliu2 | 13 | 4 | heliu1heliu1 + 14 | 4 | heliu2 | 14 | 4 | heliu2heliu2 + 14 | 4 | heliu2 | 15 | 4 | heliu3heliu3 + 14 | 4 | heliu2 | 16 | 4 | heliu4heliu4 + 15 | 4 | heliu3 | 13 | 4 | heliu1heliu1 + 15 | 4 | heliu3 | 14 | 4 | heliu2heliu2 + 15 | 4 | heliu3 | 15 | 4 | heliu3heliu3 + 15 | 4 | heliu3 | 16 | 4 | heliu4heliu4 + 16 | 4 | heliu4 | 13 | 4 | heliu1heliu1 + 16 | 4 | heliu4 | 14 | 4 | heliu2heliu2 + 16 | 4 | heliu4 | 15 | 4 | heliu3heliu3 + 16 | 4 | heliu4 | 16 | 4 | heliu4heliu4 + 17 | 5 | chenqi1 | 17 | 5 | chenqi1chenqi1 + 17 | 5 | chenqi1 | 18 | 5 | chenqi2chenqi2 + 17 | 5 | chenqi1 | 19 | 5 | chenqi3chenqi3 + 17 | 5 | chenqi1 | 20 | 5 | chenqi4chenqi4 + 18 | 5 | chenqi2 | 17 | 5 | chenqi1chenqi1 + 18 | 5 | chenqi2 | 18 | 5 | chenqi2chenqi2 + 18 | 5 | chenqi2 | 19 | 5 | chenqi3chenqi3 + 18 | 5 | chenqi2 | 20 | 5 | chenqi4chenqi4 + 19 | 5 | chenqi3 | 17 | 5 | chenqi1chenqi1 + 19 | 5 | chenqi3 | 18 | 5 | chenqi2chenqi2 + 19 | 5 | chenqi3 | 19 | 5 | chenqi3chenqi3 + 19 | 5 | chenqi3 | 20 | 5 | chenqi4chenqi4 + 20 | 5 | chenqi4 | 17 | 5 | chenqi1chenqi1 + 20 | 5 | chenqi4 | 18 | 5 | chenqi2chenqi2 + 20 | 5 | chenqi4 | 19 | 5 | chenqi3chenqi3 + 20 | 5 | chenqi4 | 20 | 5 | chenqi4chenqi4 +(80 rows) + +SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +-- view +create view v1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; +create view v2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') + ORDER BY d.department_name, v.employee_id; +create view v3 as select v.* from + (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) + left join int4_tbl z on z.f1 = x.q2, + lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); + +create view v4 as select count(*) from tenk1 a, lateral generate_series(1,two) g; +select * from v1; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from v2; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from v3; + vx | vy +-------------------+------------------- + 123 | + 456 | + 123 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 123 + 123 | 4567890123456789 + 4567890123456789 | 123 + 123 | 456 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | + -4567890123456789 | +(20 rows) + +select * from v4; + count +------- + 5000 +(1 row) + +-- plsql +create or replace procedure plpgsql_1 (param1 varchar, param2 varchar, param3 varchar, param4 varchar ) +IS + BEGIN + create table tt1 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN (param1, param2, param3) + ORDER BY d.department_name, v.employee_id; +END; +/ +create or replace procedure plpgsql_2 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt2 as SELECT d.department_name, v.employee_id, v.last_name + FROM departments d OUTER APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v + WHERE d.department_name IN (param1, param2, param3) + ORDER BY d.department_name, v.employee_id; +END; +/ +create or replace procedure plpgsql_3 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt3 as select id from (values (0), (1)) v(id), + lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; +END; +/ +create or replace procedure plpgsql_4 (param1 varchar, param2 varchar, param3 varchar, param4 varchar) +IS + BEGIN + create table tt4 as select * from + int8_tbl a left join lateral + (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from + int8_tbl b cross join int8_tbl c) ss + on a.q2 = ss.bq1; +END; +/ +call plpgsql_1(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + plpgsql_1 +----------- + +(1 row) + +call plpgsql_2(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + plpgsql_2 +----------- + +(1 row) + +call plpgsql_3(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + plpgsql_3 +----------- + +(1 row) + +call plpgsql_4(param1:='Marketing', param2:='Operations', param3:='Public Relations', param4:='CEO'); + plpgsql_4 +----------- + +(1 row) + +select * from tt1; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from tt2; + department_name | employee_id | last_name +------------------+-------------+----------- + Marketing | 1 | zhangsan1 + Marketing | 2 | zhangsan2 + Marketing | 3 | zhangsan3 + Marketing | 4 | zhangsan4 + Operations | 9 | wangwu1 + Operations | 10 | wangwu2 + Operations | 11 | wangwu3 + Operations | 12 | wangwu4 + Public Relations | 5 | lisi1 + Public Relations | 6 | lisi2 + Public Relations | 7 | lisi3 + Public Relations | 8 | lisi4 +(12 rows) + +select * from tt3; + id +---- + 0 + 0 + 0 +(3 rows) + +select * from tt4; + q1 | q2 | bq1 | cq1 | least +------------------+-------------------+------------------+------------------+------------------ + 123 | 456 | | | + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 123 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 123 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 123 | 123 | 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 | | | +(42 rows) + +drop PROCEDURE plpgsql_1; +drop PROCEDURE plpgsql_2; +drop PROCEDURE plpgsql_3; +drop PROCEDURE plpgsql_4; +-- cursor expression +create or replace procedure test_cursor_1 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT e.last_name, CURSOR(SELECT d.department_name FROM departments d CROSS APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') ORDER BY d.department_name, v.employee_id) abc FROM employees e; +begin + OPEN c1; + loop + fetch c1 into company_name, my_cur; + exit when c1%notfound; + raise notice 'company_name : % %',company_name, my_cur; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_1(); +NOTICE: company_name : zhangsan1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations + test_cursor_1 +--------------- + +(1 row) + +drop procedure test_cursor_1; +create or replace procedure test_cursor_2 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT e.last_name, CURSOR(SELECT d.department_name FROM departments d outer APPLY (SELECT * FROM employees e WHERE e.department_id = d.department_id) v WHERE d.department_name IN ('Marketing', 'Operations', 'Public Relations') ORDER BY d.department_name, v.employee_id) abc FROM employees e; +begin + OPEN c1; + loop + fetch c1 into company_name, my_cur; + exit when c1%notfound; + raise notice 'company_name : % %',company_name, my_cur; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_2(); +NOTICE: company_name : zhangsan1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : zhangsan4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : lisi4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : wangwu4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : heliu4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi1 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi2 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi3 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: company_name : chenqi4 +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Marketing +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Operations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations +NOTICE: last_name_name : Public Relations + test_cursor_2 +--------------- + +(1 row) + +drop procedure test_cursor_2; +create or replace procedure test_cursor_3 +as + company_name varchar(100); + last_name_name varchar(100); + type ref_cur_type is ref cursor; + my_cur ref_cur_type; + cursor c1 is SELECT CURSOR(select xq1::varchar from int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2)) abc; + begin + OPEN c1; + loop + fetch c1 into my_cur; + exit when c1%notfound; + loop + fetch my_cur into last_name_name; + exit when my_cur%notfound; + raise notice ' last_name_name : %',last_name_name; + end loop; + end loop; +end; +/ +call test_cursor_3(); +NOTICE: last_name_name : 123 +NOTICE: last_name_name : 123 +NOTICE: last_name_name : 123 +NOTICE: last_name_name : 123 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 +NOTICE: last_name_name : 4567890123456789 + test_cursor_3 +--------------- + +(1 row) + +drop procedure test_cursor_3; +drop table tt1; +drop table tt2; +drop table tt3; +drop table tt4; +drop function fn_salar; +drop view v1; +drop view v2; +drop view v3; +drop view v4; +drop table tenk1; +drop table INT4_TBL; +drop table INT8_TBL; +drop table INT2_TBL; +drop table departments; +drop table employees; +set query_dop = 1; +-- clean +drop schema if exists lateral_with_dop_test cascade; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table join_pt1 +drop cascades to table join_ut1 diff --git a/src/test/regress/parallel_schedule0A b/src/test/regress/parallel_schedule0A index 73c26a174..7174a3a22 100644 --- a/src/test/regress/parallel_schedule0A +++ b/src/test/regress/parallel_schedule0A @@ -483,6 +483,8 @@ test: alter_table_modify alter_table_modify_ustore alter_table_modify_ltt alter_ # test for empty string in A format database test: accept_empty_str not_accept_empty_str pg_empty_str accept_empty_copy not_accept_empty_copy +test: lateral lateral_with_dop lateral_dump + #test: gin/cgin test: cgin_select ignore_keyword_list test: gin_select