|
22 | 22 |
|
23 | 23 | #include "access/sysattr.h"
|
24 | 24 | #include "nodes/nodeFuncs.h"
|
| 25 | +#include "optimizer/clauses.h" |
25 | 26 | #include "optimizer/optimizer.h"
|
26 | 27 | #include "optimizer/placeholder.h"
|
27 | 28 | #include "optimizer/prep.h"
|
@@ -83,6 +84,8 @@ static Node *flatten_join_alias_vars_mutator(Node *node,
|
83 | 84 | flatten_join_alias_vars_context *context);
|
84 | 85 | static Node *flatten_group_exprs_mutator(Node *node,
|
85 | 86 | flatten_join_alias_vars_context *context);
|
| 87 | +static Node *mark_nullable_by_grouping(PlannerInfo *root, Node *newnode, |
| 88 | + Var *oldvar); |
86 | 89 | static Node *add_nullingrels_if_needed(PlannerInfo *root, Node *newnode,
|
87 | 90 | Var *oldvar);
|
88 | 91 | static bool is_standard_join_alias_expression(Node *newnode, Var *oldvar);
|
@@ -909,6 +912,18 @@ flatten_join_alias_vars_mutator(Node *node,
|
909 | 912 | * flatten_group_exprs
|
910 | 913 | * Replace Vars that reference GROUP outputs with the underlying grouping
|
911 | 914 | * expressions.
|
| 915 | + * |
| 916 | + * We have to preserve any varnullingrels info attached to the group Vars we're |
| 917 | + * replacing. If the replacement expression is a Var or PlaceHolderVar or |
| 918 | + * constructed from those, we can just add the varnullingrels bits to the |
| 919 | + * existing nullingrels field(s); otherwise we have to add a PlaceHolderVar |
| 920 | + * wrapper. |
| 921 | + * |
| 922 | + * NOTE: this is also used by ruleutils.c, to deparse one query parsetree back |
| 923 | + * to source text. For that use-case, root will be NULL, which is why we have |
| 924 | + * to pass the Query separately. We need the root itself only for preserving |
| 925 | + * varnullingrels. We can avoid preserving varnullingrels in the ruleutils.c's |
| 926 | + * usage because it does not make any difference to the deparsed source text. |
912 | 927 | */
|
913 | 928 | Node *
|
914 | 929 | flatten_group_exprs(PlannerInfo *root, Query *query, Node *node)
|
@@ -973,7 +988,8 @@ flatten_group_exprs_mutator(Node *node,
|
973 | 988 | if (context->possible_sublink && !context->inserted_sublink)
|
974 | 989 | context->inserted_sublink = checkExprHasSubLink(newvar);
|
975 | 990 |
|
976 |
| - return newvar; |
| 991 | + /* Lastly, add any varnullingrels to the replacement expression */ |
| 992 | + return mark_nullable_by_grouping(context->root, newvar, var); |
977 | 993 | }
|
978 | 994 |
|
979 | 995 | if (IsA(node, Aggref))
|
@@ -1040,6 +1056,76 @@ flatten_group_exprs_mutator(Node *node,
|
1040 | 1056 | (void *) context);
|
1041 | 1057 | }
|
1042 | 1058 |
|
| 1059 | +/* |
| 1060 | + * Add oldvar's varnullingrels, if any, to a flattened grouping expression. |
| 1061 | + * The newnode has been copied, so we can modify it freely. |
| 1062 | + */ |
| 1063 | +static Node * |
| 1064 | +mark_nullable_by_grouping(PlannerInfo *root, Node *newnode, Var *oldvar) |
| 1065 | +{ |
| 1066 | + Relids relids; |
| 1067 | + |
| 1068 | + if (root == NULL) |
| 1069 | + return newnode; |
| 1070 | + if (oldvar->varnullingrels == NULL) |
| 1071 | + return newnode; /* nothing to do */ |
| 1072 | + |
| 1073 | + Assert(bms_equal(oldvar->varnullingrels, |
| 1074 | + bms_make_singleton(root->group_rtindex))); |
| 1075 | + |
| 1076 | + relids = pull_varnos_of_level(root, newnode, oldvar->varlevelsup); |
| 1077 | + |
| 1078 | + if (!bms_is_empty(relids)) |
| 1079 | + { |
| 1080 | + /* |
| 1081 | + * If the newnode is not variable-free, we set the nullingrels of Vars |
| 1082 | + * or PHVs that are contained in the expression. This is not really |
| 1083 | + * 'correct' in theory, because it is the whole expression that can be |
| 1084 | + * nullable by grouping sets, not its individual vars. But it works |
| 1085 | + * in practice, because what we need is that the expression can be |
| 1086 | + * somehow distinguished from the same expression in ECs, and marking |
| 1087 | + * its vars is sufficient for this purpose. |
| 1088 | + */ |
| 1089 | + newnode = add_nulling_relids(newnode, |
| 1090 | + relids, |
| 1091 | + oldvar->varnullingrels); |
| 1092 | + } |
| 1093 | + else /* variable-free? */ |
| 1094 | + { |
| 1095 | + /* |
| 1096 | + * If the newnode is variable-free and does not contain volatile |
| 1097 | + * functions or set-returning functions, it can be treated as a member |
| 1098 | + * of EC that is redundant. So wrap it in a new PlaceHolderVar to |
| 1099 | + * carry the nullingrels. Otherwise we do not bother to make any |
| 1100 | + * changes. |
| 1101 | + * |
| 1102 | + * Aggregate functions and window functions are not allowed in |
| 1103 | + * grouping expressions. |
| 1104 | + */ |
| 1105 | + Assert(!contain_agg_clause(newnode)); |
| 1106 | + Assert(!contain_window_function(newnode)); |
| 1107 | + |
| 1108 | + if (!contain_volatile_functions(newnode) && |
| 1109 | + !expression_returns_set(newnode)) |
| 1110 | + { |
| 1111 | + PlaceHolderVar *newphv; |
| 1112 | + Relids phrels; |
| 1113 | + |
| 1114 | + phrels = get_relids_in_jointree((Node *) root->parse->jointree, |
| 1115 | + true, false); |
| 1116 | + Assert(!bms_is_empty(phrels)); |
| 1117 | + |
| 1118 | + newphv = make_placeholder_expr(root, (Expr *) newnode, phrels); |
| 1119 | + /* newphv has zero phlevelsup and NULL phnullingrels; fix it */ |
| 1120 | + newphv->phlevelsup = oldvar->varlevelsup; |
| 1121 | + newphv->phnullingrels = bms_copy(oldvar->varnullingrels); |
| 1122 | + newnode = (Node *) newphv; |
| 1123 | + } |
| 1124 | + } |
| 1125 | + |
| 1126 | + return newnode; |
| 1127 | +} |
| 1128 | + |
1043 | 1129 | /*
|
1044 | 1130 | * Add oldvar's varnullingrels, if any, to a flattened join alias expression.
|
1045 | 1131 | * The newnode has been copied, so we can modify it freely.
|
|
0 commit comments