• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revision454f72a07c2bfa37816be9b223de17e01bdb23c8 (tree)
Time2021-10-06 11:56:12
AuthorKyotaro Horiguchi <horikyota.ntt@gmai...>
CommiterKyotaro Horiguchi

Log Message

Support PostgreSQL 14

In PostgreSQL 14, planner was restructured in regard to query string
passing. This change allows us to remove fair amount of lines and
complexity that had been added as a struggle for retrieving proper
query string.

Two callbacks, post_parse_analyze_hook and ProcessUtility_hook are no
longer used. get_query_string is removed altogether. Also
current_hint_retrieved juggling, which was complex and fragile,
disappears. Now pg_hint_plan_planner alone can retrieve hint string by
looking into the given string.

In PostgreSQL 14, a new optimization item "memoize" is added. It is
not handled in this commit and leave it for a later commit.

Change Summary

Incremental Difference

--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@
55 #
66
77 MODULES = pg_hint_plan
8-HINTPLANVER = 1.3.7
8+HINTPLANVER = 1.4
99
1010 REGRESS = init base_plan pg_hint_plan ut-init ut-A ut-S ut-J ut-L ut-G ut-R ut-fdw ut-W ut-T ut-fini
1111
@@ -22,8 +22,8 @@ PG_CONFIG = pg_config
2222 PGXS := $(shell $(PG_CONFIG) --pgxs)
2323 include $(PGXS)
2424
25-STARBALL13 = pg_hint_plan13-$(HINTPLANVER).tar.gz
26-STARBALLS = $(STARBALL13)
25+STARBALL14 = pg_hint_plan14-$(HINTPLANVER).tar.gz
26+STARBALLS = $(STARBALL14)
2727
2828 TARSOURCES = Makefile *.c *.h COPYRIGHT* \
2929 pg_hint_plan--*.sql \
@@ -37,7 +37,7 @@ endif
3737
3838 installcheck: $(REGRESSION_EXPECTED)
3939
40-rpms: rpm13
40+rpms: rpm14
4141
4242 # pg_hint_plan.c includes core.c and make_join_rel.c
4343 pg_hint_plan.o: core.c make_join_rel.c # pg_stat_statements.c
@@ -52,5 +52,5 @@ $(STARBALLS): $(TARSOURCES)
5252 tar -chzf $@ $(addprefix $(subst .tar.gz,,$@)/, $^)
5353 rm $(subst .tar.gz,,$@)
5454
55-rpm13: $(STARBALL13)
56- MAKE_ROOT=`pwd` rpmbuild -bb SPECS/pg_hint_plan13.spec
55+rpm14: $(STARBALL14)
56+ MAKE_ROOT=`pwd` rpmbuild -bb SPECS/pg_hint_plan14.spec
--- a/SPECS/pg_hint_plan13.spec
+++ b/SPECS/pg_hint_plan14.spec
@@ -1,7 +1,7 @@
11 # SPEC file for pg_store_plans
22 # Copyright(C) 2020 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
33
4-%define _pgdir /usr/pgsql-13
4+%define _pgdir /usr/pgsql-14
55 %define _bindir %{_pgdir}/bin
66 %define _libdir %{_pgdir}/lib
77 %define _datadir %{_pgdir}/share
@@ -15,8 +15,8 @@
1515
1616 ## Set general information for pg_store_plans.
1717 Summary: Optimizer hint on PostgreSQL 12
18-Name: pg_hint_plan13
19-Version: 1.3.7
18+Name: pg_hint_plan14
19+Version: 1.4
2020 Release: 1%{?dist}
2121 License: BSD
2222 Group: Applications/Databases
@@ -26,8 +26,8 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
2626 Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION
2727
2828 ## We use postgresql-devel package
29-BuildRequires: postgresql13-devel
30-Requires: postgresql13-server
29+BuildRequires: postgresql14-devel
30+Requires: postgresql14-server
3131
3232 ## Description for "pg_hint_plan"
3333 %description
@@ -35,19 +35,19 @@ Requires: postgresql13-server
3535 pg_hint_plan provides capability to tweak execution plans to be
3636 executed on PostgreSQL.
3737
38-Note that this package is available for only PostgreSQL 13.
38+Note that this package is available for only PostgreSQL 14.
3939
4040 %package llvmjit
41-Requires: postgresql13-server, postgresql13-llvmjit
42-Requires: pg_hint_plan13 = 1.3.7
43-Summary: Just-in-time compilation support for pg_hint_plan13
41+Requires: postgresql14-server, postgresql14-llvmjit
42+Requires: pg_hint_plan14 = 1.4
43+Summary: Just-in-time compilation support for pg_hint_plan14
4444
4545 %description llvmjit
46-Just-in-time compilation support for pg_hint_plan13
46+Just-in-time compilation support for pg_hint_plan14
4747
4848 ## pre work for build pg_hint_plan
4949 %prep
50-PATH=/usr/pgsql-13/bin:$PATH
50+PATH=/usr/pgsql-14/bin:$PATH
5151 if [ "${MAKE_ROOT}" != "" ]; then
5252 pushd ${MAKE_ROOT}
5353 make clean %{name}-%{version}.tar.gz
@@ -58,7 +58,7 @@ if [ ! -d %{_rpmdir} ]; then mkdir -p %{_rpmdir}; fi
5858
5959 ## Set variables for build environment
6060 %build
61-PATH=/usr/pgsql-13/bin:$PATH
61+PATH=/usr/pgsql-14/bin:$PATH
6262 make USE_PGXS=1 %{?_smp_mflags}
6363
6464 ## Set variables for install
@@ -73,7 +73,7 @@ rm -rf %{buildroot}
7373 %defattr(0755,root,root)
7474 %{_libdir}/pg_hint_plan.so
7575 %defattr(0644,root,root)
76-%{_datadir}/extension/pg_hint_plan--1.3.7.sql
76+%{_datadir}/extension/pg_hint_plan--1.4.sql
7777 %{_datadir}/extension/pg_hint_plan.control
7878
7979 %files llvmjit
@@ -85,5 +85,5 @@ rm -rf %{buildroot}
8585
8686 # History of pg_hint_plan.
8787 %changelog
88-* Thu Oct 29 2020 Kyotaro Horiguchi
89-- First release of pg_hint_plan13.
88+* Tue Oct 05 2021 Kyotaro Horiguchi
89+- Support PostgreSQL 14.
--- a/core.c
+++ b/core.c
@@ -20,8 +20,14 @@
2020 * function.
2121 *
2222 * static functions:
23+ * set_rel_pathlist()
2324 * set_plain_rel_pathlist()
25+ * set_tablesample_rel_pathlist
26+ * set_foreign_pathlist()
2427 * set_append_rel_pathlist()
28+ * set_function_pathlist()
29+ * set_values_pathlist()
30+ * set_tablefunc_pathlist()
2531 * create_plain_partial_paths()
2632 *
2733 * src/backend/optimizer/path/joinrels.c
@@ -47,9 +53,9 @@
4753 *-------------------------------------------------------------------------
4854 */
4955
50-static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
51- RelOptInfo *rel2, RelOptInfo *joinrel,
52- SpecialJoinInfo *sjinfo, List *restrictlist);
56+#include "access/tsmapi.h"
57+#include "catalog/pg_operator.h"
58+#include "foreign/fdwapi.h"
5359
5460 /*
5561 * set_plain_rel_pathlist
@@ -83,6 +89,283 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
8389
8490
8591 /*
92+ * set_tablesample_rel_pathlist
93+ * Build access paths for a sampled relation
94+ */
95+static void
96+set_tablesample_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
97+{
98+ Relids required_outer;
99+ Path *path;
100+
101+ /*
102+ * We don't support pushing join clauses into the quals of a samplescan,
103+ * but it could still have required parameterization due to LATERAL refs
104+ * in its tlist or TABLESAMPLE arguments.
105+ */
106+ required_outer = rel->lateral_relids;
107+
108+ /* Consider sampled scan */
109+ path = create_samplescan_path(root, rel, required_outer);
110+
111+ /*
112+ * If the sampling method does not support repeatable scans, we must avoid
113+ * plans that would scan the rel multiple times. Ideally, we'd simply
114+ * avoid putting the rel on the inside of a nestloop join; but adding such
115+ * a consideration to the planner seems like a great deal of complication
116+ * to support an uncommon usage of second-rate sampling methods. Instead,
117+ * if there is a risk that the query might perform an unsafe join, just
118+ * wrap the SampleScan in a Materialize node. We can check for joins by
119+ * counting the membership of all_baserels (note that this correctly
120+ * counts inheritance trees as single rels). If we're inside a subquery,
121+ * we can't easily check whether a join might occur in the outer query, so
122+ * just assume one is possible.
123+ *
124+ * GetTsmRoutine is relatively expensive compared to the other tests here,
125+ * so check repeatable_across_scans last, even though that's a bit odd.
126+ */
127+ if ((root->query_level > 1 ||
128+ bms_membership(root->all_baserels) != BMS_SINGLETON) &&
129+ !(GetTsmRoutine(rte->tablesample->tsmhandler)->repeatable_across_scans))
130+ {
131+ path = (Path *) create_material_path(rel, path);
132+ }
133+
134+ add_path(rel, path);
135+
136+ /* For the moment, at least, there are no other paths to consider */
137+}
138+
139+
140+/*
141+ * set_foreign_pathlist
142+ * Build access paths for a foreign table RTE
143+ */
144+static void
145+set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
146+{
147+ /* Call the FDW's GetForeignPaths function to generate path(s) */
148+ rel->fdwroutine->GetForeignPaths(root, rel, rte->relid);
149+}
150+
151+
152+/*
153+ * set_function_pathlist
154+ * Build the (single) access path for a function RTE
155+ */
156+static void
157+set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
158+{
159+ Relids required_outer;
160+ List *pathkeys = NIL;
161+
162+ /*
163+ * We don't support pushing join clauses into the quals of a function
164+ * scan, but it could still have required parameterization due to LATERAL
165+ * refs in the function expression.
166+ */
167+ required_outer = rel->lateral_relids;
168+
169+ /*
170+ * The result is considered unordered unless ORDINALITY was used, in which
171+ * case it is ordered by the ordinal column (the last one). See if we
172+ * care, by checking for uses of that Var in equivalence classes.
173+ */
174+ if (rte->funcordinality)
175+ {
176+ AttrNumber ordattno = rel->max_attr;
177+ Var *var = NULL;
178+ ListCell *lc;
179+
180+ /*
181+ * Is there a Var for it in rel's targetlist? If not, the query did
182+ * not reference the ordinality column, or at least not in any way
183+ * that would be interesting for sorting.
184+ */
185+ foreach(lc, rel->reltarget->exprs)
186+ {
187+ Var *node = (Var *) lfirst(lc);
188+
189+ /* checking varno/varlevelsup is just paranoia */
190+ if (IsA(node, Var) &&
191+ node->varattno == ordattno &&
192+ node->varno == rel->relid &&
193+ node->varlevelsup == 0)
194+ {
195+ var = node;
196+ break;
197+ }
198+ }
199+
200+ /*
201+ * Try to build pathkeys for this Var with int8 sorting. We tell
202+ * build_expression_pathkey not to build any new equivalence class; if
203+ * the Var isn't already mentioned in some EC, it means that nothing
204+ * cares about the ordering.
205+ */
206+ if (var)
207+ pathkeys = build_expression_pathkey(root,
208+ (Expr *) var,
209+ NULL, /* below outer joins */
210+ Int8LessOperator,
211+ rel->relids,
212+ false);
213+ }
214+
215+ /* Generate appropriate path */
216+ add_path(rel, create_functionscan_path(root, rel,
217+ pathkeys, required_outer));
218+}
219+
220+
221+/*
222+ * set_values_pathlist
223+ * Build the (single) access path for a VALUES RTE
224+ */
225+static void
226+set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
227+{
228+ Relids required_outer;
229+
230+ /*
231+ * We don't support pushing join clauses into the quals of a values scan,
232+ * but it could still have required parameterization due to LATERAL refs
233+ * in the values expressions.
234+ */
235+ required_outer = rel->lateral_relids;
236+
237+ /* Generate appropriate path */
238+ add_path(rel, create_valuesscan_path(root, rel, required_outer));
239+}
240+
241+/*
242+ * set_tablefunc_pathlist
243+ * Build the (single) access path for a table func RTE
244+ */
245+static void
246+set_tablefunc_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
247+{
248+ Relids required_outer;
249+
250+ /*
251+ * We don't support pushing join clauses into the quals of a tablefunc
252+ * scan, but it could still have required parameterization due to LATERAL
253+ * refs in the function expression.
254+ */
255+ required_outer = rel->lateral_relids;
256+
257+ /* Generate appropriate path */
258+ add_path(rel, create_tablefuncscan_path(root, rel,
259+ required_outer));
260+}
261+
262+
263+/*
264+ * set_rel_pathlist
265+ * Build access paths for a base relation
266+ */
267+static void
268+set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
269+ Index rti, RangeTblEntry *rte)
270+{
271+ if (IS_DUMMY_REL(rel))
272+ {
273+ /* We already proved the relation empty, so nothing more to do */
274+ }
275+ else if (rte->inh)
276+ {
277+ /* It's an "append relation", process accordingly */
278+ set_append_rel_pathlist(root, rel, rti, rte);
279+ }
280+ else
281+ {
282+ switch (rel->rtekind)
283+ {
284+ case RTE_RELATION:
285+ if (rte->relkind == RELKIND_FOREIGN_TABLE)
286+ {
287+ /* Foreign table */
288+ set_foreign_pathlist(root, rel, rte);
289+ }
290+ else if (rte->tablesample != NULL)
291+ {
292+ /* Sampled relation */
293+ set_tablesample_rel_pathlist(root, rel, rte);
294+ }
295+ else
296+ {
297+ /* Plain relation */
298+ set_plain_rel_pathlist(root, rel, rte);
299+ }
300+ break;
301+ case RTE_SUBQUERY:
302+ /* Subquery --- fully handled during set_rel_size */
303+ break;
304+ case RTE_FUNCTION:
305+ /* RangeFunction */
306+ set_function_pathlist(root, rel, rte);
307+ break;
308+ case RTE_TABLEFUNC:
309+ /* Table Function */
310+ set_tablefunc_pathlist(root, rel, rte);
311+ break;
312+ case RTE_VALUES:
313+ /* Values list */
314+ set_values_pathlist(root, rel, rte);
315+ break;
316+ case RTE_CTE:
317+ /* CTE reference --- fully handled during set_rel_size */
318+ break;
319+ case RTE_NAMEDTUPLESTORE:
320+ /* tuplestore reference --- fully handled during set_rel_size */
321+ break;
322+ case RTE_RESULT:
323+ /* simple Result --- fully handled during set_rel_size */
324+ break;
325+ default:
326+ elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
327+ break;
328+ }
329+ }
330+
331+ /*
332+ * Allow a plugin to editorialize on the set of Paths for this base
333+ * relation. It could add new paths (such as CustomPaths) by calling
334+ * add_path(), or add_partial_path() if parallel aware. It could also
335+ * delete or modify paths added by the core code.
336+ */
337+ if (set_rel_pathlist_hook)
338+ (*set_rel_pathlist_hook) (root, rel, rti, rte);
339+
340+ /*
341+ * If this is a baserel, we should normally consider gathering any partial
342+ * paths we may have created for it. We have to do this after calling the
343+ * set_rel_pathlist_hook, else it cannot add partial paths to be included
344+ * here.
345+ *
346+ * However, if this is an inheritance child, skip it. Otherwise, we could
347+ * end up with a very large number of gather nodes, each trying to grab
348+ * its own pool of workers. Instead, we'll consider gathering partial
349+ * paths for the parent appendrel.
350+ *
351+ * Also, if this is the topmost scan/join rel (that is, the only baserel),
352+ * we postpone gathering until the final scan/join targetlist is available
353+ * (see grouping_planner).
354+ */
355+ if (rel->reloptkind == RELOPT_BASEREL &&
356+ bms_membership(root->all_baserels) != BMS_SINGLETON)
357+ generate_useful_gather_paths(root, rel, false);
358+
359+ /* Now find the cheapest of the paths for this rel */
360+ set_cheapest(rel);
361+
362+#ifdef OPTIMIZER_DEBUG
363+ debug_print_rel(root, rel);
364+#endif
365+}
366+
367+
368+/*
86369 * set_append_rel_pathlist
87370 * Build access paths for an "append relation"
88371 */
@@ -134,12 +417,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
134417 if (IS_DUMMY_REL(childrel))
135418 continue;
136419
137- /* Bubble up childrel's partitioned children. */
138- if (rel->part_scheme)
139- rel->partitioned_child_rels =
140- list_concat(rel->partitioned_child_rels,
141- childrel->partitioned_child_rels);
142-
143420 /*
144421 * Child is live, so add it to the live_childrels list for use below.
145422 */
@@ -219,10 +496,11 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
219496 join_search_one_level(root, lev);
220497
221498 /*
222- * Run generate_partitionwise_join_paths() and generate_gather_paths()
223- * for each just-processed joinrel. We could not do this earlier
224- * because both regular and partial paths can get added to a
225- * particular joinrel at multiple times within join_search_one_level.
499+ * Run generate_partitionwise_join_paths() and
500+ * generate_useful_gather_paths() for each just-processed joinrel. We
501+ * could not do this earlier because both regular and partial paths
502+ * can get added to a particular joinrel at multiple times within
503+ * join_search_one_level.
226504 *
227505 * After that, we're done creating paths for the joinrel, so run
228506 * set_cheapest().
--- a/expected/init.out
+++ b/expected/init.out
@@ -172,7 +172,6 @@ SELECT * FROM settings;
172172 constraint_exclusion | partition | Query Tuning / Other Planner Options
173173 cursor_tuple_fraction | 0.1 | Query Tuning / Other Planner Options
174174 default_statistics_target | 100 | Query Tuning / Other Planner Options
175- force_parallel_mode | off | Query Tuning / Other Planner Options
176175 from_collapse_limit | 8 | Query Tuning / Other Planner Options
177176 jit | on | Query Tuning / Other Planner Options
178177 join_collapse_limit | 8 | Query Tuning / Other Planner Options
@@ -190,6 +189,7 @@ SELECT * FROM settings;
190189 parallel_tuple_cost | 0.1 | Query Tuning / Planner Cost Constants
191190 random_page_cost | 4 | Query Tuning / Planner Cost Constants
192191 seq_page_cost | 1 | Query Tuning / Planner Cost Constants
192+ enable_async_append | on | Query Tuning / Planner Method Configuration
193193 enable_bitmapscan | on | Query Tuning / Planner Method Configuration
194194 enable_gathermerge | on | Query Tuning / Planner Method Configuration
195195 enable_hashagg | on | Query Tuning / Planner Method Configuration
@@ -198,6 +198,7 @@ SELECT * FROM settings;
198198 enable_indexonlyscan | on | Query Tuning / Planner Method Configuration
199199 enable_indexscan | on | Query Tuning / Planner Method Configuration
200200 enable_material | on | Query Tuning / Planner Method Configuration
201+ enable_memoize | on | Query Tuning / Planner Method Configuration
201202 enable_mergejoin | on | Query Tuning / Planner Method Configuration
202203 enable_nestloop | on | Query Tuning / Planner Method Configuration
203204 enable_parallel_append | on | Query Tuning / Planner Method Configuration
@@ -208,6 +209,6 @@ SELECT * FROM settings;
208209 enable_seqscan | on | Query Tuning / Planner Method Configuration
209210 enable_sort | on | Query Tuning / Planner Method Configuration
210211 enable_tidscan | on | Query Tuning / Planner Method Configuration
211-(47 rows)
212+(48 rows)
212213
213214 ANALYZE;
--- a/expected/pg_hint_plan.out
+++ b/expected/pg_hint_plan.out
@@ -11,14 +11,15 @@ EXPLAIN (COSTS false) SELECT * FROM t1, t2 WHERE t1.id = t2.id;
1111 (4 rows)
1212
1313 EXPLAIN (COSTS false) SELECT * FROM t1, t2 WHERE t1.val = t2.val;
14- QUERY PLAN
15---------------------------------
16- Hash Join
17- Hash Cond: (t2.val = t1.val)
14+ QUERY PLAN
15+-------------------------------------------
16+ Nested Loop
1817 -> Seq Scan on t2
19- -> Hash
20- -> Seq Scan on t1
21-(5 rows)
18+ -> Memoize
19+ Cache Key: t2.val
20+ -> Index Scan using t1_val on t1
21+ Index Cond: (val = t2.val)
22+(6 rows)
2223
2324 LOAD 'pg_hint_plan';
2425 SET pg_hint_plan.debug_print TO on;
@@ -32,14 +33,15 @@ EXPLAIN (COSTS false) SELECT * FROM t1, t2 WHERE t1.id = t2.id;
3233 (4 rows)
3334
3435 EXPLAIN (COSTS false) SELECT * FROM t1, t2 WHERE t1.val = t2.val;
35- QUERY PLAN
36---------------------------------
37- Hash Join
38- Hash Cond: (t2.val = t1.val)
36+ QUERY PLAN
37+-------------------------------------------
38+ Nested Loop
3939 -> Seq Scan on t2
40- -> Hash
41- -> Seq Scan on t1
42-(5 rows)
40+ -> Memoize
41+ Cache Key: t2.val
42+ -> Index Scan using t1_val on t1
43+ Index Cond: (val = t2.val)
44+(6 rows)
4345
4446 /*+ Test (t1 t2) */
4547 EXPLAIN (COSTS false) SELECT * FROM t1, t2 WHERE t1.id = t2.id;
@@ -5276,39 +5278,39 @@ error hint:
52765278 Index Cond: (id = $1)
52775279 (51 rows)
52785280
5279--- ambigous error
5281+-- ambiguous error
52805282 EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
5281- QUERY PLAN
5282--------------------------------------------------
5283- Merge Join
5284- Merge Cond: (t1.id = t2.id)
5285- -> Merge Join
5286- Merge Cond: (t1.id = t1_1.id)
5283+ QUERY PLAN
5284+--------------------------------------------
5285+ Nested Loop
5286+ -> Nested Loop
5287+ -> Seq Scan on t1 t1_1
52875288 -> Index Scan using t1_pkey on t1
5288- -> Index Scan using t1_pkey on t1 t1_1
5289+ Index Cond: (id = t1_1.id)
52895290 -> Index Scan using t2_pkey on t2
5291+ Index Cond: (id = t1.id)
52905292 (7 rows)
52915293
5292-/*+NestLoop(t1 t2)*/
5294+/*+MergeJoin(t1 t2)*/
52935295 EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
5294-INFO: pg_hint_plan: hint syntax error at or near "NestLoop(t1 t2)"
5296+INFO: pg_hint_plan: hint syntax error at or near "MergeJoin(t1 t2)"
52955297 DETAIL: Relation name "t1" is ambiguous.
52965298 LOG: pg_hint_plan:
52975299 used hint:
52985300 not used hint:
52995301 duplication hint:
53005302 error hint:
5301-NestLoop(t1 t2)
5303+MergeJoin(t1 t2)
53025304
5303- QUERY PLAN
5304--------------------------------------------------
5305- Merge Join
5306- Merge Cond: (t1.id = t2.id)
5307- -> Merge Join
5308- Merge Cond: (t1.id = t1_1.id)
5305+ QUERY PLAN
5306+--------------------------------------------
5307+ Nested Loop
5308+ -> Nested Loop
5309+ -> Seq Scan on t1 t1_1
53095310 -> Index Scan using t1_pkey on t1
5310- -> Index Scan using t1_pkey on t1 t1_1
5311+ Index Cond: (id = t1_1.id)
53115312 -> Index Scan using t2_pkey on t2
5313+ Index Cond: (id = t1.id)
53125314 (7 rows)
53135315
53145316 /*+Leading(t1 t2 t1)*/
@@ -5322,15 +5324,15 @@ duplication hint:
53225324 error hint:
53235325 Leading(t1 t2 t1)
53245326
5325- QUERY PLAN
5326--------------------------------------------------
5327- Merge Join
5328- Merge Cond: (t1.id = t2.id)
5329- -> Merge Join
5330- Merge Cond: (t1.id = t1_1.id)
5327+ QUERY PLAN
5328+--------------------------------------------
5329+ Nested Loop
5330+ -> Nested Loop
5331+ -> Seq Scan on t1 t1_1
53315332 -> Index Scan using t1_pkey on t1
5332- -> Index Scan using t1_pkey on t1 t1_1
5333+ Index Cond: (id = t1_1.id)
53335334 -> Index Scan using t2_pkey on t2
5335+ Index Cond: (id = t1.id)
53345336 (7 rows)
53355337
53365338 -- identifier length test
@@ -8057,7 +8059,7 @@ not used hint:
80578059 duplication hint:
80588060 error hint:
80598061
8060-CONTEXT: SQL statement "SELECT 1, /*+ SeqScan(t1) */ * from t1"
8062+CONTEXT: SQL statement "SELECT 1, /*+ SeqScan(t1) */ * from t1"
80618063 PL/pgSQL function testfunc() line 3 at PERFORM
80628064 testfunc
80638065 ----------
--- a/expected/ut-A.out
+++ b/expected/ut-A.out
@@ -3239,7 +3239,6 @@ SELECT name, setting FROM settings;
32393239 constraint_exclusion | partition
32403240 cursor_tuple_fraction | 0.1
32413241 default_statistics_target | 100
3242- force_parallel_mode | off
32433242 from_collapse_limit | 8
32443243 jit | on
32453244 join_collapse_limit | 8
@@ -3257,6 +3256,7 @@ SELECT name, setting FROM settings;
32573256 parallel_tuple_cost | 0.1
32583257 random_page_cost | 4
32593258 seq_page_cost | 1
3259+ enable_async_append | on
32603260 enable_bitmapscan | on
32613261 enable_gathermerge | on
32623262 enable_hashagg | on
@@ -3265,6 +3265,7 @@ SELECT name, setting FROM settings;
32653265 enable_indexonlyscan | on
32663266 enable_indexscan | on
32673267 enable_material | on
3268+ enable_memoize | on
32683269 enable_mergejoin | on
32693270 enable_nestloop | on
32703271 enable_parallel_append | on
@@ -3275,7 +3276,7 @@ SELECT name, setting FROM settings;
32753276 enable_seqscan | on
32763277 enable_sort | on
32773278 enable_tidscan | on
3278-(47 rows)
3279+(48 rows)
32793280
32803281 SET pg_hint_plan.parse_messages TO error;
32813282 /*+Set(enable_seqscan off)Set(geqo_threshold 100)SeqScan(t1)MergeJoin(t1 t2)NestLoop(t1 t1)*/
@@ -3296,7 +3297,6 @@ SELECT name, setting FROM settings;
32963297 constraint_exclusion | partition
32973298 cursor_tuple_fraction | 0.1
32983299 default_statistics_target | 100
3299- force_parallel_mode | off
33003300 from_collapse_limit | 8
33013301 jit | on
33023302 join_collapse_limit | 8
@@ -3314,6 +3314,7 @@ SELECT name, setting FROM settings;
33143314 parallel_tuple_cost | 0.1
33153315 random_page_cost | 4
33163316 seq_page_cost | 1
3317+ enable_async_append | on
33173318 enable_bitmapscan | on
33183319 enable_gathermerge | on
33193320 enable_hashagg | on
@@ -3322,6 +3323,7 @@ SELECT name, setting FROM settings;
33223323 enable_indexonlyscan | on
33233324 enable_indexscan | on
33243325 enable_material | on
3326+ enable_memoize | on
33253327 enable_mergejoin | on
33263328 enable_nestloop | on
33273329 enable_parallel_append | on
@@ -3332,7 +3334,7 @@ SELECT name, setting FROM settings;
33323334 enable_seqscan | on
33333335 enable_sort | on
33343336 enable_tidscan | on
3335-(47 rows)
3337+(48 rows)
33363338
33373339 /*+Set(enable_seqscan off)Set(geqo_threshold 100)SeqScan(t1)MergeJoin(t1 t2)*/
33383340 EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
@@ -3372,7 +3374,6 @@ SELECT name, setting FROM settings;
33723374 constraint_exclusion | partition
33733375 cursor_tuple_fraction | 0.1
33743376 default_statistics_target | 100
3375- force_parallel_mode | off
33763377 from_collapse_limit | 8
33773378 jit | on
33783379 join_collapse_limit | 8
@@ -3390,6 +3391,7 @@ SELECT name, setting FROM settings;
33903391 parallel_tuple_cost | 0.1
33913392 random_page_cost | 4
33923393 seq_page_cost | 1
3394+ enable_async_append | on
33933395 enable_bitmapscan | on
33943396 enable_gathermerge | on
33953397 enable_hashagg | on
@@ -3398,6 +3400,7 @@ SELECT name, setting FROM settings;
33983400 enable_indexonlyscan | on
33993401 enable_indexscan | on
34003402 enable_material | on
3403+ enable_memoize | on
34013404 enable_mergejoin | on
34023405 enable_nestloop | on
34033406 enable_parallel_append | on
@@ -3408,7 +3411,7 @@ SELECT name, setting FROM settings;
34083411 enable_seqscan | on
34093412 enable_sort | on
34103413 enable_tidscan | on
3411-(47 rows)
3414+(48 rows)
34123415
34133416 SET pg_hint_plan.parse_messages TO error;
34143417 /*+Set(enable_seqscan off)Set(geqo_threshold 100)SeqScan(t1)MergeJoin(t1 t2)NestLoop(t1 t1)*/
@@ -3429,7 +3432,6 @@ SELECT name, setting FROM settings;
34293432 constraint_exclusion | partition
34303433 cursor_tuple_fraction | 0.1
34313434 default_statistics_target | 100
3432- force_parallel_mode | off
34333435 from_collapse_limit | 8
34343436 jit | on
34353437 join_collapse_limit | 8
@@ -3447,6 +3449,7 @@ SELECT name, setting FROM settings;
34473449 parallel_tuple_cost | 0.1
34483450 random_page_cost | 4
34493451 seq_page_cost | 1
3452+ enable_async_append | on
34503453 enable_bitmapscan | on
34513454 enable_gathermerge | on
34523455 enable_hashagg | on
@@ -3455,6 +3458,7 @@ SELECT name, setting FROM settings;
34553458 enable_indexonlyscan | on
34563459 enable_indexscan | on
34573460 enable_material | on
3461+ enable_memoize | on
34583462 enable_mergejoin | on
34593463 enable_nestloop | on
34603464 enable_parallel_append | on
@@ -3465,7 +3469,7 @@ SELECT name, setting FROM settings;
34653469 enable_seqscan | on
34663470 enable_sort | on
34673471 enable_tidscan | on
3468-(47 rows)
3472+(48 rows)
34693473
34703474 EXPLAIN (COSTS false) EXECUTE p1;
34713475 QUERY PLAN
@@ -3494,7 +3498,6 @@ SELECT name, setting FROM settings;
34943498 constraint_exclusion | partition
34953499 cursor_tuple_fraction | 0.1
34963500 default_statistics_target | 100
3497- force_parallel_mode | off
34983501 from_collapse_limit | 8
34993502 jit | on
35003503 join_collapse_limit | 8
@@ -3512,6 +3515,7 @@ SELECT name, setting FROM settings;
35123515 parallel_tuple_cost | 0.1
35133516 random_page_cost | 4
35143517 seq_page_cost | 1
3518+ enable_async_append | on
35153519 enable_bitmapscan | on
35163520 enable_gathermerge | on
35173521 enable_hashagg | on
@@ -3520,6 +3524,7 @@ SELECT name, setting FROM settings;
35203524 enable_indexonlyscan | on
35213525 enable_indexscan | on
35223526 enable_material | on
3527+ enable_memoize | on
35233528 enable_mergejoin | on
35243529 enable_nestloop | on
35253530 enable_parallel_append | on
@@ -3530,7 +3535,7 @@ SELECT name, setting FROM settings;
35303535 enable_seqscan | on
35313536 enable_sort | on
35323537 enable_tidscan | on
3533-(47 rows)
3538+(48 rows)
35343539
35353540 SET pg_hint_plan.parse_messages TO error;
35363541 EXPLAIN (COSTS false) EXECUTE p2;
@@ -3582,7 +3587,6 @@ SELECT name, setting FROM settings;
35823587 constraint_exclusion | partition
35833588 cursor_tuple_fraction | 0.1
35843589 default_statistics_target | 100
3585- force_parallel_mode | off
35863590 from_collapse_limit | 8
35873591 jit | on
35883592 join_collapse_limit | 8
@@ -3600,6 +3604,7 @@ SELECT name, setting FROM settings;
36003604 parallel_tuple_cost | 0.1
36013605 random_page_cost | 4
36023606 seq_page_cost | 1
3607+ enable_async_append | on
36033608 enable_bitmapscan | on
36043609 enable_gathermerge | on
36053610 enable_hashagg | on
@@ -3608,6 +3613,7 @@ SELECT name, setting FROM settings;
36083613 enable_indexonlyscan | on
36093614 enable_indexscan | on
36103615 enable_material | on
3616+ enable_memoize | on
36113617 enable_mergejoin | on
36123618 enable_nestloop | on
36133619 enable_parallel_append | on
@@ -3618,7 +3624,7 @@ SELECT name, setting FROM settings;
36183624 enable_seqscan | on
36193625 enable_sort | on
36203626 enable_tidscan | on
3621-(47 rows)
3627+(48 rows)
36223628
36233629 -- No. A-12-1-4
36243630 -- No. A-12-2-4
@@ -3636,7 +3642,6 @@ SELECT name, setting FROM settings;
36363642 constraint_exclusion | partition
36373643 cursor_tuple_fraction | 0.1
36383644 default_statistics_target | 100
3639- force_parallel_mode | off
36403645 from_collapse_limit | 8
36413646 jit | on
36423647 join_collapse_limit | 8
@@ -3654,6 +3659,7 @@ SELECT name, setting FROM settings;
36543659 parallel_tuple_cost | 0.1
36553660 random_page_cost | 4
36563661 seq_page_cost | 1
3662+ enable_async_append | on
36573663 enable_bitmapscan | on
36583664 enable_gathermerge | on
36593665 enable_hashagg | on
@@ -3662,6 +3668,7 @@ SELECT name, setting FROM settings;
36623668 enable_indexonlyscan | on
36633669 enable_indexscan | on
36643670 enable_material | on
3671+ enable_memoize | on
36653672 enable_mergejoin | on
36663673 enable_nestloop | on
36673674 enable_parallel_append | on
@@ -3672,7 +3679,7 @@ SELECT name, setting FROM settings;
36723679 enable_seqscan | on
36733680 enable_sort | on
36743681 enable_tidscan | on
3675-(47 rows)
3682+(48 rows)
36763683
36773684 SET pg_hint_plan.parse_messages TO error;
36783685 EXPLAIN (COSTS false) EXECUTE p2;
@@ -3702,7 +3709,6 @@ SELECT name, setting FROM settings;
37023709 constraint_exclusion | partition
37033710 cursor_tuple_fraction | 0.1
37043711 default_statistics_target | 100
3705- force_parallel_mode | off
37063712 from_collapse_limit | 8
37073713 jit | on
37083714 join_collapse_limit | 8
@@ -3720,6 +3726,7 @@ SELECT name, setting FROM settings;
37203726 parallel_tuple_cost | 0.1
37213727 random_page_cost | 4
37223728 seq_page_cost | 1
3729+ enable_async_append | on
37233730 enable_bitmapscan | on
37243731 enable_gathermerge | on
37253732 enable_hashagg | on
@@ -3728,6 +3735,7 @@ SELECT name, setting FROM settings;
37283735 enable_indexonlyscan | on
37293736 enable_indexscan | on
37303737 enable_material | on
3738+ enable_memoize | on
37313739 enable_mergejoin | on
37323740 enable_nestloop | on
37333741 enable_parallel_append | on
@@ -3738,7 +3746,7 @@ SELECT name, setting FROM settings;
37383746 enable_seqscan | on
37393747 enable_sort | on
37403748 enable_tidscan | on
3741-(47 rows)
3749+(48 rows)
37423750
37433751 DEALLOCATE p1;
37443752 SET pg_hint_plan.parse_messages TO LOG;
@@ -3783,7 +3791,6 @@ SELECT name, setting FROM settings;
37833791 constraint_exclusion | partition
37843792 cursor_tuple_fraction | 0.1
37853793 default_statistics_target | 100
3786- force_parallel_mode | off
37873794 from_collapse_limit | 8
37883795 jit | on
37893796 join_collapse_limit | 8
@@ -3801,6 +3808,7 @@ SELECT name, setting FROM settings;
38013808 parallel_tuple_cost | 0.1
38023809 random_page_cost | 4
38033810 seq_page_cost | 1
3811+ enable_async_append | on
38043812 enable_bitmapscan | on
38053813 enable_gathermerge | on
38063814 enable_hashagg | on
@@ -3809,6 +3817,7 @@ SELECT name, setting FROM settings;
38093817 enable_indexonlyscan | on
38103818 enable_indexscan | off
38113819 enable_material | on
3820+ enable_memoize | on
38123821 enable_mergejoin | off
38133822 enable_nestloop | on
38143823 enable_parallel_append | on
@@ -3819,7 +3828,7 @@ SELECT name, setting FROM settings;
38193828 enable_seqscan | on
38203829 enable_sort | on
38213830 enable_tidscan | on
3822-(47 rows)
3831+(48 rows)
38233832
38243833 /*+Set(enable_indexscan on)Set(geqo_threshold 100)IndexScan(t2)MergeJoin(t1 t2)Leading(t2 t1)*/
38253834 EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
@@ -3856,7 +3865,6 @@ SELECT name, setting FROM settings;
38563865 constraint_exclusion | partition
38573866 cursor_tuple_fraction | 0.1
38583867 default_statistics_target | 100
3859- force_parallel_mode | off
38603868 from_collapse_limit | 8
38613869 jit | on
38623870 join_collapse_limit | 8
@@ -3874,6 +3882,7 @@ SELECT name, setting FROM settings;
38743882 parallel_tuple_cost | 0.1
38753883 random_page_cost | 4
38763884 seq_page_cost | 1
3885+ enable_async_append | on
38773886 enable_bitmapscan | on
38783887 enable_gathermerge | on
38793888 enable_hashagg | on
@@ -3882,6 +3891,7 @@ SELECT name, setting FROM settings;
38823891 enable_indexonlyscan | on
38833892 enable_indexscan | off
38843893 enable_material | on
3894+ enable_memoize | on
38853895 enable_mergejoin | off
38863896 enable_nestloop | on
38873897 enable_parallel_append | on
@@ -3892,7 +3902,7 @@ SELECT name, setting FROM settings;
38923902 enable_seqscan | on
38933903 enable_sort | on
38943904 enable_tidscan | on
3895-(47 rows)
3905+(48 rows)
38963906
38973907 EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
38983908 QUERY PLAN
@@ -3931,7 +3941,6 @@ SELECT name, setting FROM settings;
39313941 constraint_exclusion | partition
39323942 cursor_tuple_fraction | 0.1
39333943 default_statistics_target | 100
3934- force_parallel_mode | off
39353944 from_collapse_limit | 8
39363945 jit | on
39373946 join_collapse_limit | 8
@@ -3949,6 +3958,7 @@ SELECT name, setting FROM settings;
39493958 parallel_tuple_cost | 0.1
39503959 random_page_cost | 4
39513960 seq_page_cost | 1
3961+ enable_async_append | on
39523962 enable_bitmapscan | on
39533963 enable_gathermerge | on
39543964 enable_hashagg | on
@@ -3957,6 +3967,7 @@ SELECT name, setting FROM settings;
39573967 enable_indexonlyscan | on
39583968 enable_indexscan | off
39593969 enable_material | on
3970+ enable_memoize | on
39603971 enable_mergejoin | off
39613972 enable_nestloop | on
39623973 enable_parallel_append | on
@@ -3967,7 +3978,7 @@ SELECT name, setting FROM settings;
39673978 enable_seqscan | on
39683979 enable_sort | on
39693980 enable_tidscan | on
3970-(47 rows)
3981+(48 rows)
39713982
39723983 BEGIN;
39733984 /*+Set(enable_indexscan on)Set(geqo_threshold 100)IndexScan(t2)MergeJoin(t1 t2)Leading(t2 t1)*/
@@ -4007,7 +4018,6 @@ SELECT name, setting FROM settings;
40074018 constraint_exclusion | partition
40084019 cursor_tuple_fraction | 0.1
40094020 default_statistics_target | 100
4010- force_parallel_mode | off
40114021 from_collapse_limit | 8
40124022 jit | on
40134023 join_collapse_limit | 8
@@ -4025,6 +4035,7 @@ SELECT name, setting FROM settings;
40254035 parallel_tuple_cost | 0.1
40264036 random_page_cost | 4
40274037 seq_page_cost | 1
4038+ enable_async_append | on
40284039 enable_bitmapscan | on
40294040 enable_gathermerge | on
40304041 enable_hashagg | on
@@ -4033,6 +4044,7 @@ SELECT name, setting FROM settings;
40334044 enable_indexonlyscan | on
40344045 enable_indexscan | off
40354046 enable_material | on
4047+ enable_memoize | on
40364048 enable_mergejoin | off
40374049 enable_nestloop | on
40384050 enable_parallel_append | on
@@ -4043,7 +4055,7 @@ SELECT name, setting FROM settings;
40434055 enable_seqscan | on
40444056 enable_sort | on
40454057 enable_tidscan | on
4046-(47 rows)
4058+(48 rows)
40474059
40484060 EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
40494061 QUERY PLAN
@@ -4083,7 +4095,6 @@ SELECT name, setting FROM settings;
40834095 constraint_exclusion | partition
40844096 cursor_tuple_fraction | 0.1
40854097 default_statistics_target | 100
4086- force_parallel_mode | off
40874098 from_collapse_limit | 8
40884099 jit | on
40894100 join_collapse_limit | 8
@@ -4101,6 +4112,7 @@ SELECT name, setting FROM settings;
41014112 parallel_tuple_cost | 0.1
41024113 random_page_cost | 4
41034114 seq_page_cost | 1
4115+ enable_async_append | on
41044116 enable_bitmapscan | on
41054117 enable_gathermerge | on
41064118 enable_hashagg | on
@@ -4109,6 +4121,7 @@ SELECT name, setting FROM settings;
41094121 enable_indexonlyscan | on
41104122 enable_indexscan | off
41114123 enable_material | on
4124+ enable_memoize | on
41124125 enable_mergejoin | off
41134126 enable_nestloop | on
41144127 enable_parallel_append | on
@@ -4119,7 +4132,7 @@ SELECT name, setting FROM settings;
41194132 enable_seqscan | on
41204133 enable_sort | on
41214134 enable_tidscan | on
4122-(47 rows)
4135+(48 rows)
41234136
41244137 /*+Set(enable_indexscan on)Set(geqo_threshold 100)IndexScan(t2)MergeJoin(t1 t2)Leading(t2 t1)*/
41254138 EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
@@ -4160,7 +4173,6 @@ SELECT name, setting FROM settings;
41604173 constraint_exclusion | partition
41614174 cursor_tuple_fraction | 0.1
41624175 default_statistics_target | 100
4163- force_parallel_mode | off
41644176 from_collapse_limit | 8
41654177 jit | on
41664178 join_collapse_limit | 8
@@ -4178,6 +4190,7 @@ SELECT name, setting FROM settings;
41784190 parallel_tuple_cost | 0.1
41794191 random_page_cost | 4
41804192 seq_page_cost | 1
4193+ enable_async_append | on
41814194 enable_bitmapscan | on
41824195 enable_gathermerge | on
41834196 enable_hashagg | on
@@ -4186,6 +4199,7 @@ SELECT name, setting FROM settings;
41864199 enable_indexonlyscan | on
41874200 enable_indexscan | off
41884201 enable_material | on
4202+ enable_memoize | on
41894203 enable_mergejoin | off
41904204 enable_nestloop | on
41914205 enable_parallel_append | on
@@ -4196,7 +4210,7 @@ SELECT name, setting FROM settings;
41964210 enable_seqscan | on
41974211 enable_sort | on
41984212 enable_tidscan | on
4199-(47 rows)
4213+(48 rows)
42004214
42014215 EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
42024216 QUERY PLAN
--- a/expected/ut-S.out
+++ b/expected/ut-S.out
@@ -5050,16 +5050,17 @@ error hint:
50505050
50515051 -- No. S-3-8-4
50525052 EXPLAIN (COSTS false) UPDATE s1.p1 SET c4 = c4 WHERE c1 = 1;
5053- QUERY PLAN
5054------------------------------
5053+ QUERY PLAN
5054+-----------------------------------
50555055 Update on p1
5056- Update on p1
5057- Update on p1c1 p1_1
5058- -> Seq Scan on p1
5059- Filter: (c1 = 1)
5060- -> Seq Scan on p1c1 p1_1
5061- Filter: (c1 = 1)
5062-(7 rows)
5056+ Update on p1 p1_1
5057+ Update on p1c1 p1_2
5058+ -> Append
5059+ -> Seq Scan on p1 p1_1
5060+ Filter: (c1 = 1)
5061+ -> Seq Scan on p1c1 p1_2
5062+ Filter: (c1 = 1)
5063+(8 rows)
50635064
50645065 /*+IndexScan(p1)*/
50655066 EXPLAIN (COSTS false) UPDATE s1.p1 SET c4 = c4 WHERE c1 = 1;
@@ -5070,23 +5071,22 @@ not used hint:
50705071 duplication hint:
50715072 error hint:
50725073
5073- QUERY PLAN
5074---------------------------------------------
5074+ QUERY PLAN
5075+--------------------------------------------------
50755076 Update on p1
5076- Update on p1
5077- Update on p1c1 p1_1
5078- -> Index Scan using p1_i on p1
5079- Index Cond: (c1 = 1)
5080- -> Index Scan using p1c1_i on p1c1 p1_1
5081- Index Cond: (c1 = 1)
5082-(7 rows)
5077+ Update on p1 p1_1
5078+ Update on p1c1 p1_2
5079+ -> Append
5080+ -> Index Scan using p1_i on p1 p1_1
5081+ Index Cond: (c1 = 1)
5082+ -> Index Scan using p1c1_i on p1c1 p1_2
5083+ Index Cond: (c1 = 1)
5084+(8 rows)
50835085
50845086 /*+IndexScan(p1 p1_pkey)*/
50855087 EXPLAIN (COSTS false) UPDATE s1.p1 SET c4 = c4 WHERE c1 = 1;
50865088 LOG: available indexes for IndexScan(p1): p1_pkey
50875089 LOG: available indexes for IndexScan(p1c1): p1c1_pkey
5088-LOG: available indexes for IndexScan(p1): p1_pkey
5089-LOG: available indexes for IndexScan(p1c1): p1c1_pkey
50905090 LOG: pg_hint_plan:
50915091 used hint:
50925092 IndexScan(p1 p1_pkey)
@@ -5094,16 +5094,17 @@ not used hint:
50945094 duplication hint:
50955095 error hint:
50965096
5097- QUERY PLAN
5098------------------------------------------------
5097+ QUERY PLAN
5098+-----------------------------------------------------
50995099 Update on p1
5100- Update on p1
5101- Update on p1c1 p1_1
5102- -> Index Scan using p1_pkey on p1
5103- Index Cond: (c1 = 1)
5104- -> Index Scan using p1c1_pkey on p1c1 p1_1
5105- Index Cond: (c1 = 1)
5106-(7 rows)
5100+ Update on p1 p1_1
5101+ Update on p1c1 p1_2
5102+ -> Append
5103+ -> Index Scan using p1_pkey on p1 p1_1
5104+ Index Cond: (c1 = 1)
5105+ -> Index Scan using p1c1_pkey on p1c1 p1_2
5106+ Index Cond: (c1 = 1)
5107+(8 rows)
51075108
51085109 ----
51095110 ---- No. S-3-9 inheritance table number
--- a/make_join_rel.c
+++ b/make_join_rel.c
@@ -126,7 +126,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
126126 joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,
127127 &restrictlist);
128128
129- /* !!! START: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
129+ /* !!! START: HERE IS THE PART WHICH IS ADDED FOR PG_HINT_PLAN !!! */
130130 {
131131 RowsHint *rows_hint = NULL;
132132 int i;
@@ -202,7 +202,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
202202
203203 }
204204 }
205- /* !!! END: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
205+ /* !!! END: HERE IS THE PART WHICH IS ADDED FOR PG_HINT_PLAN !!! */
206206
207207 /*
208208 * If we've already proven this join is empty, we needn't consider any
--- a/normalize_query.h
+++ b/normalize_query.h
@@ -11,45 +11,7 @@
1111 #ifndef NORMALIZE_QUERY_H
1212 #define NORMALIZE_QUERY_H
1313
14-/*
15- * Struct for tracking locations/lengths of constants during normalization
16- */
17-typedef struct pgssLocationLen
18-{
19- int location; /* start offset in query text */
20- int length; /* length in bytes, or -1 to ignore */
21-} pgssLocationLen;
22-
23-/*
24- * Working state for computing a query jumble and producing a normalized
25- * query string
26- */
27-typedef struct pgssJumbleState
28-{
29- /* Jumble of current query tree */
30- unsigned char *jumble;
31-
32- /* Number of bytes used in jumble[] */
33- Size jumble_len;
34-
35- /* Array of locations of constants that should be removed */
36- pgssLocationLen *clocations;
37-
38- /* Allocated length of clocations array */
39- int clocations_buf_size;
40-
41- /* Current number of valid entries in clocations array */
42- int clocations_count;
43-
44- /* highest Param id we've seen, in order to start normalization correctly */
45- int highest_extern_param_id;
46-} pgssJumbleState;
47-
4814 static char *
49-generate_normalized_query(pgssJumbleState *jstate, const char *query,
50- int query_loc, int *query_len_p, int encoding);
51-static void JumbleQuery(pgssJumbleState *jstate, Query *query);
52-
53-#define JUMBLE_SIZE 1024
54-
15+generate_normalized_query(JumbleState *jstate, const char *query,
16+ int query_loc, int *query_len_p);
5517 #endif /* NORMALIZE_QUERY_H */
--- a/pg_hint_plan--1.3.7.sql
+++ b/pg_hint_plan--1.4.sql
@@ -1,4 +1,4 @@
1-/* pg_hint_plan/pg_hint_plan--1.3.7.sql */
1+/* pg_hint_plan/pg_hint_plan--1.4.sql */
22
33 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
44 \echo Use "CREATE EXTENSION pg_hint_plan" to load this file. \quit
--- a/pg_hint_plan.c
+++ b/pg_hint_plan.c
@@ -230,14 +230,6 @@ static unsigned int msgqno = 0;
230230 static char qnostr[32];
231231 static const char *current_hint_str = NULL;
232232
233-/*
234- * However we usually take a hint stirng in post_parse_analyze_hook, we still
235- * need to do so in planner_hook when client starts query execution from the
236- * bind message on a prepared query. This variable prevent duplicate and
237- * sometimes harmful hint string retrieval.
238- */
239-static bool current_hint_retrieved = false;
240-
241233 /* common data for all hints. */
242234 struct Hint
243235 {
@@ -400,12 +392,6 @@ void _PG_fini(void);
400392 static void push_hint(HintState *hstate);
401393 static void pop_hint(void);
402394
403-static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
404-static void pg_hint_plan_ProcessUtility(PlannedStmt *pstmt,
405- const char *queryString,
406- ProcessUtilityContext context,
407- ParamListInfo params, QueryEnvironment *queryEnv,
408- DestReceiver *dest, QueryCompletion *qc);
409395 static PlannedStmt *pg_hint_plan_planner(Query *parse, const char *query_string,
410396 int cursorOptions,
411397 ParamListInfo boundParams);
@@ -489,8 +475,6 @@ static void make_rels_by_clauseless_joins(PlannerInfo *root,
489475 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
490476 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
491477 RangeTblEntry *rte);
492-static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
493- Index rti, RangeTblEntry *rte);
494478 RelOptInfo *pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1,
495479 RelOptInfo *rel2);
496480
@@ -562,11 +546,9 @@ static const struct config_enum_entry parse_debug_level_options[] = {
562546 };
563547
564548 /* Saved hook values in case of unload */
565-static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
566549 static planner_hook_type prev_planner = NULL;
567550 static join_search_hook_type prev_join_search = NULL;
568551 static set_rel_pathlist_hook_type prev_set_rel_pathlist = NULL;
569-static ProcessUtility_hook_type prev_ProcessUtility_hook = NULL;
570552
571553 /* Hold reference to currently active hint */
572554 static HintState *current_hint_state = NULL;
@@ -688,16 +670,12 @@ _PG_init(void)
688670 NULL);
689671
690672 /* Install hooks. */
691- prev_post_parse_analyze_hook = post_parse_analyze_hook;
692- post_parse_analyze_hook = pg_hint_plan_post_parse_analyze;
693673 prev_planner = planner_hook;
694674 planner_hook = pg_hint_plan_planner;
695675 prev_join_search = join_search_hook;
696676 join_search_hook = pg_hint_plan_join_search;
697677 prev_set_rel_pathlist = set_rel_pathlist_hook;
698678 set_rel_pathlist_hook = pg_hint_plan_set_rel_pathlist;
699- prev_ProcessUtility_hook = ProcessUtility_hook;
700- ProcessUtility_hook = pg_hint_plan_ProcessUtility;
701679
702680 /* setup PL/pgSQL plugin hook */
703681 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -716,11 +694,9 @@ _PG_fini(void)
716694 PLpgSQL_plugin **var_ptr;
717695
718696 /* Uninstall hooks. */
719- post_parse_analyze_hook = prev_post_parse_analyze_hook;
720697 planner_hook = prev_planner;
721698 join_search_hook = prev_join_search;
722699 set_rel_pathlist_hook = prev_set_rel_pathlist;
723- ProcessUtility_hook = prev_ProcessUtility_hook;
724700
725701 /* uninstall PL/pgSQL plugin hook */
726702 var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
@@ -1809,125 +1785,6 @@ get_hints_from_table(const char *client_query, const char *client_application)
18091785 }
18101786
18111787 /*
1812- * Get client-supplied query string. Addtion to that the jumbled query is
1813- * supplied if the caller requested. From the restriction of JumbleQuery, some
1814- * kind of query needs special amendments. Reutrns NULL if this query doesn't
1815- * change the current hint. This function returns NULL also when something
1816- * wrong has happend and let the caller continue using the current hints.
1817- */
1818-static const char *
1819-get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
1820-{
1821- const char *p = debug_query_string;
1822-
1823- /*
1824- * If debug_query_string is set, it is the top level statement. But in some
1825- * cases we reach here with debug_query_string set NULL for example in the
1826- * case of DESCRIBE message handling or EXECUTE command. We may still see a
1827- * candidate top-level query in pstate in the case.
1828- */
1829- if (pstate && pstate->p_sourcetext)
1830- p = pstate->p_sourcetext;
1831-
1832- /* We don't see a query string, return NULL */
1833- if (!p)
1834- return NULL;
1835-
1836- if (jumblequery != NULL)
1837- *jumblequery = query;
1838-
1839- if (query->commandType == CMD_UTILITY)
1840- {
1841- Query *target_query = (Query *)query->utilityStmt;
1842-
1843- /*
1844- * Some CMD_UTILITY statements have a subquery that we can hint on.
1845- * Since EXPLAIN can be placed before other kind of utility statements
1846- * and EXECUTE can be contained other kind of utility statements, these
1847- * conditions are not mutually exclusive and should be considered in
1848- * this order.
1849- */
1850- if (IsA(target_query, ExplainStmt))
1851- {
1852- ExplainStmt *stmt = (ExplainStmt *)target_query;
1853-
1854- Assert(IsA(stmt->query, Query));
1855- target_query = (Query *)stmt->query;
1856-
1857- /* strip out the top-level query for further processing */
1858- if (target_query->commandType == CMD_UTILITY &&
1859- target_query->utilityStmt != NULL)
1860- target_query = (Query *)target_query->utilityStmt;
1861- }
1862-
1863- if (IsA(target_query, DeclareCursorStmt))
1864- {
1865- DeclareCursorStmt *stmt = (DeclareCursorStmt *)target_query;
1866- Query *query = (Query *)stmt->query;
1867-
1868- /* the target must be CMD_SELECT in this case */
1869- Assert(IsA(query, Query) && query->commandType == CMD_SELECT);
1870- target_query = query;
1871- }
1872-
1873- if (IsA(target_query, CreateTableAsStmt))
1874- {
1875- CreateTableAsStmt *stmt = (CreateTableAsStmt *) target_query;
1876-
1877- Assert(IsA(stmt->query, Query));
1878- target_query = (Query *) stmt->query;
1879-
1880- /* strip out the top-level query for further processing */
1881- if (target_query->commandType == CMD_UTILITY &&
1882- target_query->utilityStmt != NULL)
1883- target_query = (Query *)target_query->utilityStmt;
1884- }
1885-
1886- if (IsA(target_query, ExecuteStmt))
1887- {
1888- /*
1889- * Use the prepared query for EXECUTE. The Query for jumble
1890- * also replaced with the corresponding one.
1891- */
1892- ExecuteStmt *stmt = (ExecuteStmt *)target_query;
1893- PreparedStatement *entry;
1894-
1895- entry = FetchPreparedStatement(stmt->name, true);
1896-
1897- if (entry->plansource->is_valid)
1898- {
1899- p = entry->plansource->query_string;
1900- target_query = (Query *) linitial (entry->plansource->query_list);
1901- }
1902- else
1903- {
1904- /* igonre the hint for EXECUTE if invalidated */
1905- p = NULL;
1906- target_query = NULL;
1907- }
1908- }
1909-
1910- /* JumbleQuery accespts only a non-utility Query */
1911- if (target_query &&
1912- (!IsA(target_query, Query) ||
1913- target_query->utilityStmt != NULL))
1914- target_query = NULL;
1915-
1916- if (jumblequery)
1917- *jumblequery = target_query;
1918- }
1919- /*
1920- * Return NULL if pstate is not of top-level query. We don't need this
1921- * when jumble info is not requested or cannot do this when pstate is NULL.
1922- */
1923- else if (!jumblequery && pstate && pstate->p_sourcetext != p &&
1924- strcmp(pstate->p_sourcetext, p) != 0)
1925- p = NULL;
1926-
1927- return p;
1928-}
1929-
1930-/*
19311788 * Get hints from the head block comment in client-supplied query string.
19321789 */
19331790 static const char *
@@ -2842,32 +2699,25 @@ pop_hint(void)
28422699 * Retrieve and store hint string from given query or from the hint table.
28432700 */
28442701 static void
2845-get_current_hint_string(ParseState *pstate, Query *query)
2702+get_current_hint_string(Query *query, const char *query_str)
28462703 {
2847- const char *query_str;
28482704 MemoryContext oldcontext;
28492705
2850- /* do nothing under hint table search */
2706+ /* do nothing while scanning hint table */
28512707 if (hint_inhibit_level > 0)
28522708 return;
28532709
2854- /* We alredy have one, don't parse it again. */
2855- if (current_hint_retrieved)
2856- return;
2857-
2858- /* Don't parse the current query hereafter */
2859- current_hint_retrieved = true;
2860-
2861- if (!pg_hint_plan_enable_hint)
2710+ /* Make sure trashing old hint string */
2711+ if (current_hint_str)
28622712 {
2863- if (current_hint_str)
2864- {
2865- pfree((void *)current_hint_str);
2866- current_hint_str = NULL;
2867- }
2868- return;
2713+ pfree((void *)current_hint_str);
2714+ current_hint_str = NULL;
28692715 }
28702716
2717+ /* Return if nothing to do. */
2718+ if (!pg_hint_plan_enable_hint || !query_str)
2719+ return;
2720+
28712721 /* increment the query number */
28722722 qnostr[0] = 0;
28732723 if (debug_level > 1)
@@ -2877,121 +2727,71 @@ get_current_hint_string(ParseState *pstate, Query *query)
28772727 /* search the hint table for a hint if requested */
28782728 if (pg_hint_plan_enable_hint_table)
28792729 {
2730+ JumbleState *jstate;
28802731 int query_len;
2881- pgssJumbleState jstate;
2882- Query *jumblequery;
2883- char *normalized_query = NULL;
2884-
2885- query_str = get_query_string(pstate, query, &jumblequery);
2886-
2887- /* If this query is not for hint, just return */
2888- if (!query_str)
2889- return;
2732+ char *normalized_query;
28902733
2891- /* clear the previous hint string */
2892- if (current_hint_str)
2893- {
2894- pfree((void *)current_hint_str);
2895- current_hint_str = NULL;
2896- }
2897-
2898- if (jumblequery)
2899- {
2900- /*
2901- * XXX: normalization code is copied from pg_stat_statements.c.
2902- * Make sure to keep up-to-date with it.
2903- */
2904- jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
2905- jstate.jumble_len = 0;
2906- jstate.clocations_buf_size = 32;
2907- jstate.clocations = (pgssLocationLen *)
2908- palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
2909- jstate.clocations_count = 0;
2734+ jstate = JumbleQuery(query, query_str);
29102735
2911- JumbleQuery(&jstate, jumblequery);
2912-
2913- /*
2914- * Normalize the query string by replacing constants with '?'
2915- */
2916- /*
2917- * Search hint string which is stored keyed by query string
2918- * and application name. The query string is normalized to allow
2919- * fuzzy matching.
2920- *
2921- * Adding 1 byte to query_len ensures that the returned string has
2922- * a terminating NULL.
2923- */
2924- query_len = strlen(query_str) + 1;
2925- normalized_query =
2926- generate_normalized_query(&jstate, query_str, 0, &query_len,
2927- GetDatabaseEncoding());
2736+ /*
2737+ * Normalize the query string by replacing constants with '?'
2738+ */
2739+ /*
2740+ * Search hint string which is stored keyed by query string
2741+ * and application name. The query string is normalized to allow
2742+ * fuzzy matching.
2743+ *
2744+ * Adding 1 byte to query_len ensures that the returned string has
2745+ * a terminating NULL.
2746+ */
2747+ query_len = strlen(query_str) + 1;
2748+ normalized_query =
2749+ generate_normalized_query(jstate, query_str, 0, &query_len);
29282750
2929- /*
2930- * find a hint for the normalized query. the result should be in
2931- * TopMemoryContext
2932- */
2933- oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2934- current_hint_str =
2935- get_hints_from_table(normalized_query, application_name);
2936- MemoryContextSwitchTo(oldcontext);
2751+ /*
2752+ * find a hint for the normalized query. the result should be in
2753+ * TopMemoryContext
2754+ */
2755+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2756+ current_hint_str =
2757+ get_hints_from_table(normalized_query, application_name);
2758+ MemoryContextSwitchTo(oldcontext);
29372759
2938- if (debug_level > 1)
2939- {
2940- if (current_hint_str)
2941- ereport(pg_hint_plan_debug_message_level,
2942- (errmsg("pg_hint_plan[qno=0x%x]: "
2943- "post_parse_analyze_hook: "
2944- "hints from table: \"%s\": "
2945- "normalized_query=\"%s\", "
2946- "application name =\"%s\"",
2947- qno, current_hint_str,
2948- normalized_query, application_name),
2949- errhidestmt(msgqno != qno),
2950- errhidecontext(msgqno != qno)));
2951- else
2952- ereport(pg_hint_plan_debug_message_level,
2953- (errmsg("pg_hint_plan[qno=0x%x]: "
2954- "no match found in table: "
2955- "application name = \"%s\", "
2956- "normalized_query=\"%s\"",
2957- qno, application_name,
2958- normalized_query),
2959- errhidestmt(msgqno != qno),
2960- errhidecontext(msgqno != qno)));
2961-
2962- msgqno = qno;
2963- }
2760+ if (debug_level > 1)
2761+ {
2762+ if (current_hint_str)
2763+ ereport(pg_hint_plan_debug_message_level,
2764+ (errmsg("pg_hint_plan[qno=0x%x]: "
2765+ "hints from table: \"%s\": "
2766+ "normalized_query=\"%s\", "
2767+ "application name =\"%s\"",
2768+ qno, current_hint_str,
2769+ normalized_query, application_name),
2770+ errhidestmt(msgqno != qno),
2771+ errhidecontext(msgqno != qno)));
2772+ else
2773+ ereport(pg_hint_plan_debug_message_level,
2774+ (errmsg("pg_hint_plan[qno=0x%x]: "
2775+ "no match found in table: "
2776+ "application name = \"%s\", "
2777+ "normalized_query=\"%s\"",
2778+ qno, application_name,
2779+ normalized_query),
2780+ errhidestmt(msgqno != qno),
2781+ errhidecontext(msgqno != qno)));
2782+
2783+ msgqno = qno;
29642784 }
29652785
2966- /* retrun if we have hint here */
2786+ /* retrun if we have hint string here */
29672787 if (current_hint_str)
29682788 return;
29692789 }
2970- else
2971- query_str = get_query_string(pstate, query, NULL);
2972-
2973- if (query_str)
2974- {
2975- /*
2976- * get hints from the comment. However we may have the same query
2977- * string with the previous call, but the extra comparison seems no
2978- * use..
2979- */
2980- if (current_hint_str)
2981- pfree((void *)current_hint_str);
29822790
2983- oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2984- current_hint_str = get_hints_from_comment(query_str);
2985- MemoryContextSwitchTo(oldcontext);
2986- }
2987- else
2988- {
2989- /*
2990- * Failed to get query. We would be in fetching invalidated
2991- * plancache. Try the next chance.
2992- */
2993- current_hint_retrieved = false;
2994- }
2791+ /* get hints from the comment */
2792+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
2793+ current_hint_str = get_hints_from_comment(query_str);
2794+ MemoryContextSwitchTo(oldcontext);
29952795
29962796 if (debug_level > 1)
29972797 {
@@ -3015,44 +2815,6 @@ get_current_hint_string(ParseState *pstate, Query *query)
30152815 }
30162816
30172817 /*
3018- * Retrieve hint string from the current query.
3019- */
3020-static void
3021-pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
3022-{
3023- if (prev_post_parse_analyze_hook)
3024- prev_post_parse_analyze_hook(pstate, query);
3025-
3026- /* always retrieve hint from the top-level query string */
3027- if (plpgsql_recurse_level == 0)
3028- current_hint_retrieved = false;
3029-
3030- get_current_hint_string(pstate, query);
3031-}
3032-
3033-/*
3034- * We need to reset current_hint_retrieved flag always when a command execution
3035- * is finished. This is true even for a pure utility command that doesn't
3036- * involve planning phase.
3037- */
3038-static void
3039-pg_hint_plan_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
3040- ProcessUtilityContext context,
3041- ParamListInfo params, QueryEnvironment *queryEnv,
3042- DestReceiver *dest, QueryCompletion *qc)
3043-{
3044- if (prev_ProcessUtility_hook)
3045- prev_ProcessUtility_hook(pstmt, queryString, context, params, queryEnv,
3046- dest, qc);
3047- else
3048- standard_ProcessUtility(pstmt, queryString, context, params, queryEnv,
3049- dest, qc);
3050-
3051- if (plpgsql_recurse_level == 0)
3052- current_hint_retrieved = false;
3053-}
3054-
3055-/*
30562818 * Read and set up hint information
30572819 */
30582820 static PlannedStmt *
@@ -3082,27 +2844,14 @@ pg_hint_plan_planner(Query *parse, const char *query_string, int cursorOptions,
30822844 goto standard_planner_proc;
30832845 }
30842846
3085- /*
3086- * Support for nested plpgsql functions. This is quite ugly but this is the
3087- * only point I could find where I can get the query string.
3088- */
3089- if (plpgsql_recurse_level > 0 &&
3090- error_context_stack && error_context_stack->arg)
2847+ /* always retrieve hint from the top-level query string */
2848+ if (plpgsql_recurse_level == 0 && current_hint_str)
30912849 {
3092- MemoryContext oldcontext;
3093-
3094- oldcontext = MemoryContextSwitchTo(TopMemoryContext);
3095- current_hint_str =
3096- get_hints_from_comment((char *)error_context_stack->arg);
3097- MemoryContextSwitchTo(oldcontext);
2850+ pfree((void *)current_hint_str);
2851+ current_hint_str = NULL;
30982852 }
30992853
3100- /*
3101- * Query execution in extended protocol can be started without the analyze
3102- * phase. In the case retrieve hint string here.
3103- */
3104- if (!current_hint_str)
3105- get_current_hint_string(NULL, parse);
2854+ get_current_hint_string(parse, query_string);
31062855
31072856 /* No hint, go the normal way */
31082857 if (!current_hint_str)
@@ -3111,9 +2860,18 @@ pg_hint_plan_planner(Query *parse, const char *query_string, int cursorOptions,
31112860 /* parse the hint into hint state struct */
31122861 hstate = create_hintstate(parse, pstrdup(current_hint_str));
31132862
3114- /* run standard planner if the statement has not valid hint */
2863+ /* run standard planner if we're given with no valid hints */
31152864 if (!hstate)
2865+ {
2866+ /* forget invalid hint string */
2867+ if (current_hint_str)
2868+ {
2869+ pfree((void *)current_hint_str);
2870+ current_hint_str = NULL;
2871+ }
2872+
31162873 goto standard_planner_proc;
2874+ }
31172875
31182876 /*
31192877 * Push new hint struct to the hint stack to disable previous hint context.
@@ -3201,7 +2959,6 @@ pg_hint_plan_planner(Query *parse, const char *query_string, int cursorOptions,
32012959 {
32022960 pfree((void *)current_hint_str);
32032961 current_hint_str = NULL;
3204- current_hint_retrieved = false;
32052962 }
32062963
32072964 /* Print hint in debug mode. */
@@ -3821,50 +3578,18 @@ setup_hint_enforcement(PlannerInfo *root, RelOptInfo *rel,
38213578 return 0;
38223579 }
38233580
3824- /*
3825- * Forget about the parent of another subquery, but don't forget if the
3826- * inhTargetkind of the root is not INHKIND_NONE, which signals the root
3827- * contains only appendrel members. See inheritance_planner for details.
3828- *
3829- * (PG12.0) 428b260f87 added one more planning cycle for updates on
3830- * partitioned tables and hints set up in the cycle are overriden by the
3831- * second cycle. Since I didn't find no apparent distinction between the
3832- * PlannerRoot of the cycle and that of ordinary CMD_SELECT, pg_hint_plan
3833- * accepts both cycles and the later one wins. In the second cycle root
3834- * doesn't have inheritance information at all so use the parent_relid set
3835- * in the first cycle.
3836- */
3837- if (root->inhTargetKind == INHKIND_NONE)
3581+ if (bms_num_members(rel->top_parent_relids) == 1)
38383582 {
3839- if (root != current_hint_state->current_root)
3840- current_hint_state->parent_relid = 0;
3841-
3842- /* Find the parent for this relation other than the registered parent */
3843- foreach (l, root->append_rel_list)
3844- {
3845- AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
3846-
3847- if (appinfo->child_relid == rel->relid)
3848- {
3849- if (current_hint_state->parent_relid != appinfo->parent_relid)
3850- {
3851- new_parent_relid = appinfo->parent_relid;
3852- current_hint_state->current_root = root;
3853- }
3854- break;
3855- }
3856- }
3857-
3858- if (!l)
3859- {
3860- /*
3861- * This relation doesn't have a parent. Cancel
3862- * current_hint_state.
3863- */
3864- current_hint_state->parent_relid = 0;
3865- current_hint_state->parent_scan_hint = NULL;
3866- current_hint_state->parent_parallel_hint = NULL;
3867- }
3583+ new_parent_relid = bms_next_member(rel->top_parent_relids, -1);
3584+ current_hint_state->current_root = root;
3585+ Assert(new_parent_relid > 0);
3586+ }
3587+ else
3588+ {
3589+ /* This relation doesn't have a parent. Cancel current_hint_state. */
3590+ current_hint_state->parent_relid = 0;
3591+ current_hint_state->parent_scan_hint = NULL;
3592+ current_hint_state->parent_parallel_hint = NULL;
38683593 }
38693594
38703595 if (new_parent_relid > 0)
@@ -4858,58 +4583,6 @@ pg_hint_plan_set_rel_pathlist(PlannerInfo * root, RelOptInfo *rel,
48584583 }
48594584
48604585 /*
4861- * set_rel_pathlist
4862- * Build access paths for a base relation
4863- *
4864- * This function was copied and edited from set_rel_pathlist() in
4865- * src/backend/optimizer/path/allpaths.c in order not to copy other static
4866- * functions not required here.
4867- */
4868-static void
4869-set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
4870- Index rti, RangeTblEntry *rte)
4871-{
4872- if (IS_DUMMY_REL(rel))
4873- {
4874- /* We already proved the relation empty, so nothing more to do */
4875- }
4876- else if (rte->inh)
4877- {
4878- /* It's an "append relation", process accordingly */
4879- set_append_rel_pathlist(root, rel, rti, rte);
4880- }
4881- else
4882- {
4883- if (rel->rtekind == RTE_RELATION)
4884- {
4885- if (rte->relkind == RELKIND_RELATION)
4886- {
4887- if(rte->tablesample != NULL)
4888- elog(ERROR, "sampled relation is not supported");
4889-
4890- /* Plain relation */
4891- set_plain_rel_pathlist(root, rel, rte);
4892- }
4893- else
4894- elog(ERROR, "unexpected relkind: %c", rte->relkind);
4895- }
4896- else
4897- elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
4898- }
4899-
4900- /*
4901- * Allow a plugin to editorialize on the set of Paths for this base
4902- * relation. It could add new paths (such as CustomPaths) by calling
4903- * add_path(), or delete or modify paths added by the core code.
4904- */
4905- if (set_rel_pathlist_hook)
4906- (*set_rel_pathlist_hook) (root, rel, rti, rte);
4907-
4908- /* Now find the cheapest of the paths for this rel */
4909- set_cheapest(rel);
4910-}
4911-
4912-/*
49134586 * stmt_beg callback is called when each query in PL/pgSQL function is about
49144587 * to be executed. At that timing, we save query string in the global variable
49154588 * plpgsql_query_string to use it in planner hook. It's safe to use one global
@@ -4945,6 +4618,14 @@ void plpgsql_query_erase_callback(ResourceReleasePhase phase,
49454618 plpgsql_recurse_level = 0;
49464619 }
49474620
4621+
4622+/* include core static functions */
4623+static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
4624+ RelOptInfo *rel2, RelOptInfo *joinrel,
4625+ SpecialJoinInfo *sjinfo, List *restrictlist);
4626+static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
4627+ Index rti, RangeTblEntry *rte);
4628+
49484629 #define standard_join_search pg_hint_plan_standard_join_search
49494630 #define join_search_one_level pg_hint_plan_join_search_one_level
49504631 #define make_join_rel make_join_rel_wrapper
--- a/pg_hint_plan.control
+++ b/pg_hint_plan.control
@@ -1,6 +1,6 @@
11 # pg_hint_plan extension
22
33 comment = ''
4-default_version = '1.3.7'
4+default_version = '1.4'
55 relocatable = false
66 schema = hint_plan
--- a/pg_stat_statements.c
+++ b/pg_stat_statements.c
@@ -10,673 +10,15 @@
1010 */
1111 #include "postgres.h"
1212
13-#include <sys/stat.h>
14-
15-#include "access/hash.h"
1613 #include "parser/scanner.h"
1714
18-static void AppendJumble(pgssJumbleState *jstate,
19- const unsigned char *item, Size size);
20-static void JumbleQuery(pgssJumbleState *jstate, Query *query);
21-static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable);
22-static void JumbleExpr(pgssJumbleState *jstate, Node *node);
23-static void RecordConstLocation(pgssJumbleState *jstate, int location);
24-static char *generate_normalized_query(pgssJumbleState *jstate, const char *query,
25- int query_loc, int *query_len_p, int encoding);
26-static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query,
15+static char *generate_normalized_query(JumbleState *jstate, const char *query,
16+ int query_loc, int *query_len_p);
17+static void fill_in_constant_lengths(JumbleState *jstate, const char *query,
2718 int query_loc);
2819 static int comp_location(const void *a, const void *b);
2920
30-/*
31- * AppendJumble: Append a value that is substantive in a given query to
32- * the current jumble.
33- */
34-static void
35-AppendJumble(pgssJumbleState *jstate, const unsigned char *item, Size size)
36-{
37- unsigned char *jumble = jstate->jumble;
38- Size jumble_len = jstate->jumble_len;
39-
40- /*
41- * Whenever the jumble buffer is full, we hash the current contents and
42- * reset the buffer to contain just that hash value, thus relying on the
43- * hash to summarize everything so far.
44- */
45- while (size > 0)
46- {
47- Size part_size;
48-
49- if (jumble_len >= JUMBLE_SIZE)
50- {
51- uint64 start_hash;
52-
53- start_hash = DatumGetUInt64(hash_any_extended(jumble,
54- JUMBLE_SIZE, 0));
55- memcpy(jumble, &start_hash, sizeof(start_hash));
56- jumble_len = sizeof(start_hash);
57- }
58- part_size = Min(size, JUMBLE_SIZE - jumble_len);
59- memcpy(jumble + jumble_len, item, part_size);
60- jumble_len += part_size;
61- item += part_size;
62- size -= part_size;
63- }
64- jstate->jumble_len = jumble_len;
65-}
66-
67-/*
68- * Wrappers around AppendJumble to encapsulate details of serialization
69- * of individual local variable elements.
70- */
71-#define APP_JUMB(item) \
72- AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
73-#define APP_JUMB_STRING(str) \
74- AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)
75-
76-/*
77- * JumbleQuery: Selectively serialize the query tree, appending significant
78- * data to the "query jumble" while ignoring nonsignificant data.
79- *
80- * Rule of thumb for what to include is that we should ignore anything not
81- * semantically significant (such as alias names) as well as anything that can
82- * be deduced from child nodes (else we'd just be double-hashing that piece
83- * of information).
84- */
85-static void
86-JumbleQuery(pgssJumbleState *jstate, Query *query)
87-{
88- Assert(IsA(query, Query));
89- Assert(query->utilityStmt == NULL);
90-
91- APP_JUMB(query->commandType);
92- /* resultRelation is usually predictable from commandType */
93- JumbleExpr(jstate, (Node *) query->cteList);
94- JumbleRangeTable(jstate, query->rtable);
95- JumbleExpr(jstate, (Node *) query->jointree);
96- JumbleExpr(jstate, (Node *) query->targetList);
97- JumbleExpr(jstate, (Node *) query->onConflict);
98- JumbleExpr(jstate, (Node *) query->returningList);
99- JumbleExpr(jstate, (Node *) query->groupClause);
100- JumbleExpr(jstate, (Node *) query->groupingSets);
101- JumbleExpr(jstate, query->havingQual);
102- JumbleExpr(jstate, (Node *) query->windowClause);
103- JumbleExpr(jstate, (Node *) query->distinctClause);
104- JumbleExpr(jstate, (Node *) query->sortClause);
105- JumbleExpr(jstate, query->limitOffset);
106- JumbleExpr(jstate, query->limitCount);
107- /* we ignore rowMarks */
108- JumbleExpr(jstate, query->setOperations);
109-}
110-
111-/*
112- * Jumble a range table
113- */
114-static void
115-JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
116-{
117- ListCell *lc;
118-
119- foreach(lc, rtable)
120- {
121- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
122-
123- APP_JUMB(rte->rtekind);
124- switch (rte->rtekind)
125- {
126- case RTE_RELATION:
127- APP_JUMB(rte->relid);
128- JumbleExpr(jstate, (Node *) rte->tablesample);
129- break;
130- case RTE_SUBQUERY:
131- JumbleQuery(jstate, rte->subquery);
132- break;
133- case RTE_JOIN:
134- APP_JUMB(rte->jointype);
135- break;
136- case RTE_FUNCTION:
137- JumbleExpr(jstate, (Node *) rte->functions);
138- break;
139- case RTE_TABLEFUNC:
140- JumbleExpr(jstate, (Node *) rte->tablefunc);
141- break;
142- case RTE_VALUES:
143- JumbleExpr(jstate, (Node *) rte->values_lists);
144- break;
145- case RTE_CTE:
146-
147- /*
148- * Depending on the CTE name here isn't ideal, but it's the
149- * only info we have to identify the referenced WITH item.
150- */
151- APP_JUMB_STRING(rte->ctename);
152- APP_JUMB(rte->ctelevelsup);
153- break;
154- case RTE_NAMEDTUPLESTORE:
155- APP_JUMB_STRING(rte->enrname);
156- break;
157- case RTE_RESULT:
158- break;
159- default:
160- elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
161- break;
162- }
163- }
164-}
165-
166-/*
167- * Jumble an expression tree
168- *
169- * In general this function should handle all the same node types that
170- * expression_tree_walker() does, and therefore it's coded to be as parallel
171- * to that function as possible. However, since we are only invoked on
172- * queries immediately post-parse-analysis, we need not handle node types
173- * that only appear in planning.
174- *
175- * Note: the reason we don't simply use expression_tree_walker() is that the
176- * point of that function is to support tree walkers that don't care about
177- * most tree node types, but here we care about all types. We should complain
178- * about any unrecognized node type.
179- */
180-static void
181-JumbleExpr(pgssJumbleState *jstate, Node *node)
182-{
183- ListCell *temp;
184-
185- if (node == NULL)
186- return;
187-
188- /* Guard against stack overflow due to overly complex expressions */
189- check_stack_depth();
190-
191- /*
192- * We always emit the node's NodeTag, then any additional fields that are
193- * considered significant, and then we recurse to any child nodes.
194- */
195- APP_JUMB(node->type);
196-
197- switch (nodeTag(node))
198- {
199- case T_Var:
200- {
201- Var *var = (Var *) node;
202-
203- APP_JUMB(var->varno);
204- APP_JUMB(var->varattno);
205- APP_JUMB(var->varlevelsup);
206- }
207- break;
208- case T_Const:
209- {
210- Const *c = (Const *) node;
211-
212- /* We jumble only the constant's type, not its value */
213- APP_JUMB(c->consttype);
214- /* Also, record its parse location for query normalization */
215- RecordConstLocation(jstate, c->location);
216- }
217- break;
218- case T_Param:
219- {
220- Param *p = (Param *) node;
221-
222- APP_JUMB(p->paramkind);
223- APP_JUMB(p->paramid);
224- APP_JUMB(p->paramtype);
225- /* Also, track the highest external Param id */
226- if (p->paramkind == PARAM_EXTERN &&
227- p->paramid > jstate->highest_extern_param_id)
228- jstate->highest_extern_param_id = p->paramid;
229- }
230- break;
231- case T_Aggref:
232- {
233- Aggref *expr = (Aggref *) node;
234-
235- APP_JUMB(expr->aggfnoid);
236- JumbleExpr(jstate, (Node *) expr->aggdirectargs);
237- JumbleExpr(jstate, (Node *) expr->args);
238- JumbleExpr(jstate, (Node *) expr->aggorder);
239- JumbleExpr(jstate, (Node *) expr->aggdistinct);
240- JumbleExpr(jstate, (Node *) expr->aggfilter);
241- }
242- break;
243- case T_GroupingFunc:
244- {
245- GroupingFunc *grpnode = (GroupingFunc *) node;
246-
247- JumbleExpr(jstate, (Node *) grpnode->refs);
248- }
249- break;
250- case T_WindowFunc:
251- {
252- WindowFunc *expr = (WindowFunc *) node;
253-
254- APP_JUMB(expr->winfnoid);
255- APP_JUMB(expr->winref);
256- JumbleExpr(jstate, (Node *) expr->args);
257- JumbleExpr(jstate, (Node *) expr->aggfilter);
258- }
259- break;
260- case T_SubscriptingRef:
261- {
262- SubscriptingRef *sbsref = (SubscriptingRef *) node;
263-
264- JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
265- JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
266- JumbleExpr(jstate, (Node *) sbsref->refexpr);
267- JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
268- }
269- break;
270- case T_FuncExpr:
271- {
272- FuncExpr *expr = (FuncExpr *) node;
273-
274- APP_JUMB(expr->funcid);
275- JumbleExpr(jstate, (Node *) expr->args);
276- }
277- break;
278- case T_NamedArgExpr:
279- {
280- NamedArgExpr *nae = (NamedArgExpr *) node;
281-
282- APP_JUMB(nae->argnumber);
283- JumbleExpr(jstate, (Node *) nae->arg);
284- }
285- break;
286- case T_OpExpr:
287- case T_DistinctExpr: /* struct-equivalent to OpExpr */
288- case T_NullIfExpr: /* struct-equivalent to OpExpr */
289- {
290- OpExpr *expr = (OpExpr *) node;
291-
292- APP_JUMB(expr->opno);
293- JumbleExpr(jstate, (Node *) expr->args);
294- }
295- break;
296- case T_ScalarArrayOpExpr:
297- {
298- ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
299-
300- APP_JUMB(expr->opno);
301- APP_JUMB(expr->useOr);
302- JumbleExpr(jstate, (Node *) expr->args);
303- }
304- break;
305- case T_BoolExpr:
306- {
307- BoolExpr *expr = (BoolExpr *) node;
308-
309- APP_JUMB(expr->boolop);
310- JumbleExpr(jstate, (Node *) expr->args);
311- }
312- break;
313- case T_SubLink:
314- {
315- SubLink *sublink = (SubLink *) node;
316-
317- APP_JUMB(sublink->subLinkType);
318- APP_JUMB(sublink->subLinkId);
319- JumbleExpr(jstate, (Node *) sublink->testexpr);
320- JumbleQuery(jstate, castNode(Query, sublink->subselect));
321- }
322- break;
323- case T_FieldSelect:
324- {
325- FieldSelect *fs = (FieldSelect *) node;
326-
327- APP_JUMB(fs->fieldnum);
328- JumbleExpr(jstate, (Node *) fs->arg);
329- }
330- break;
331- case T_FieldStore:
332- {
333- FieldStore *fstore = (FieldStore *) node;
334-
335- JumbleExpr(jstate, (Node *) fstore->arg);
336- JumbleExpr(jstate, (Node *) fstore->newvals);
337- }
338- break;
339- case T_RelabelType:
340- {
341- RelabelType *rt = (RelabelType *) node;
342-
343- APP_JUMB(rt->resulttype);
344- JumbleExpr(jstate, (Node *) rt->arg);
345- }
346- break;
347- case T_CoerceViaIO:
348- {
349- CoerceViaIO *cio = (CoerceViaIO *) node;
350-
351- APP_JUMB(cio->resulttype);
352- JumbleExpr(jstate, (Node *) cio->arg);
353- }
354- break;
355- case T_ArrayCoerceExpr:
356- {
357- ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node;
358-
359- APP_JUMB(acexpr->resulttype);
360- JumbleExpr(jstate, (Node *) acexpr->arg);
361- JumbleExpr(jstate, (Node *) acexpr->elemexpr);
362- }
363- break;
364- case T_ConvertRowtypeExpr:
365- {
366- ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node;
367-
368- APP_JUMB(crexpr->resulttype);
369- JumbleExpr(jstate, (Node *) crexpr->arg);
370- }
371- break;
372- case T_CollateExpr:
373- {
374- CollateExpr *ce = (CollateExpr *) node;
375-
376- APP_JUMB(ce->collOid);
377- JumbleExpr(jstate, (Node *) ce->arg);
378- }
379- break;
380- case T_CaseExpr:
381- {
382- CaseExpr *caseexpr = (CaseExpr *) node;
383-
384- JumbleExpr(jstate, (Node *) caseexpr->arg);
385- foreach(temp, caseexpr->args)
386- {
387- CaseWhen *when = lfirst_node(CaseWhen, temp);
388-
389- JumbleExpr(jstate, (Node *) when->expr);
390- JumbleExpr(jstate, (Node *) when->result);
391- }
392- JumbleExpr(jstate, (Node *) caseexpr->defresult);
393- }
394- break;
395- case T_CaseTestExpr:
396- {
397- CaseTestExpr *ct = (CaseTestExpr *) node;
398-
399- APP_JUMB(ct->typeId);
400- }
401- break;
402- case T_ArrayExpr:
403- JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements);
404- break;
405- case T_RowExpr:
406- JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args);
407- break;
408- case T_RowCompareExpr:
409- {
410- RowCompareExpr *rcexpr = (RowCompareExpr *) node;
411-
412- APP_JUMB(rcexpr->rctype);
413- JumbleExpr(jstate, (Node *) rcexpr->largs);
414- JumbleExpr(jstate, (Node *) rcexpr->rargs);
415- }
416- break;
417- case T_CoalesceExpr:
418- JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args);
419- break;
420- case T_MinMaxExpr:
421- {
422- MinMaxExpr *mmexpr = (MinMaxExpr *) node;
423-
424- APP_JUMB(mmexpr->op);
425- JumbleExpr(jstate, (Node *) mmexpr->args);
426- }
427- break;
428- case T_SQLValueFunction:
429- {
430- SQLValueFunction *svf = (SQLValueFunction *) node;
431-
432- APP_JUMB(svf->op);
433- /* type is fully determined by op */
434- APP_JUMB(svf->typmod);
435- }
436- break;
437- case T_XmlExpr:
438- {
439- XmlExpr *xexpr = (XmlExpr *) node;
440-
441- APP_JUMB(xexpr->op);
442- JumbleExpr(jstate, (Node *) xexpr->named_args);
443- JumbleExpr(jstate, (Node *) xexpr->args);
444- }
445- break;
446- case T_NullTest:
447- {
448- NullTest *nt = (NullTest *) node;
449-
450- APP_JUMB(nt->nulltesttype);
451- JumbleExpr(jstate, (Node *) nt->arg);
452- }
453- break;
454- case T_BooleanTest:
455- {
456- BooleanTest *bt = (BooleanTest *) node;
45721
458- APP_JUMB(bt->booltesttype);
459- JumbleExpr(jstate, (Node *) bt->arg);
460- }
461- break;
462- case T_CoerceToDomain:
463- {
464- CoerceToDomain *cd = (CoerceToDomain *) node;
465-
466- APP_JUMB(cd->resulttype);
467- JumbleExpr(jstate, (Node *) cd->arg);
468- }
469- break;
470- case T_CoerceToDomainValue:
471- {
472- CoerceToDomainValue *cdv = (CoerceToDomainValue *) node;
473-
474- APP_JUMB(cdv->typeId);
475- }
476- break;
477- case T_SetToDefault:
478- {
479- SetToDefault *sd = (SetToDefault *) node;
480-
481- APP_JUMB(sd->typeId);
482- }
483- break;
484- case T_CurrentOfExpr:
485- {
486- CurrentOfExpr *ce = (CurrentOfExpr *) node;
487-
488- APP_JUMB(ce->cvarno);
489- if (ce->cursor_name)
490- APP_JUMB_STRING(ce->cursor_name);
491- APP_JUMB(ce->cursor_param);
492- }
493- break;
494- case T_NextValueExpr:
495- {
496- NextValueExpr *nve = (NextValueExpr *) node;
497-
498- APP_JUMB(nve->seqid);
499- APP_JUMB(nve->typeId);
500- }
501- break;
502- case T_InferenceElem:
503- {
504- InferenceElem *ie = (InferenceElem *) node;
505-
506- APP_JUMB(ie->infercollid);
507- APP_JUMB(ie->inferopclass);
508- JumbleExpr(jstate, ie->expr);
509- }
510- break;
511- case T_TargetEntry:
512- {
513- TargetEntry *tle = (TargetEntry *) node;
514-
515- APP_JUMB(tle->resno);
516- APP_JUMB(tle->ressortgroupref);
517- JumbleExpr(jstate, (Node *) tle->expr);
518- }
519- break;
520- case T_RangeTblRef:
521- {
522- RangeTblRef *rtr = (RangeTblRef *) node;
523-
524- APP_JUMB(rtr->rtindex);
525- }
526- break;
527- case T_JoinExpr:
528- {
529- JoinExpr *join = (JoinExpr *) node;
530-
531- APP_JUMB(join->jointype);
532- APP_JUMB(join->isNatural);
533- APP_JUMB(join->rtindex);
534- JumbleExpr(jstate, join->larg);
535- JumbleExpr(jstate, join->rarg);
536- JumbleExpr(jstate, join->quals);
537- }
538- break;
539- case T_FromExpr:
540- {
541- FromExpr *from = (FromExpr *) node;
542-
543- JumbleExpr(jstate, (Node *) from->fromlist);
544- JumbleExpr(jstate, from->quals);
545- }
546- break;
547- case T_OnConflictExpr:
548- {
549- OnConflictExpr *conf = (OnConflictExpr *) node;
550-
551- APP_JUMB(conf->action);
552- JumbleExpr(jstate, (Node *) conf->arbiterElems);
553- JumbleExpr(jstate, conf->arbiterWhere);
554- JumbleExpr(jstate, (Node *) conf->onConflictSet);
555- JumbleExpr(jstate, conf->onConflictWhere);
556- APP_JUMB(conf->constraint);
557- APP_JUMB(conf->exclRelIndex);
558- JumbleExpr(jstate, (Node *) conf->exclRelTlist);
559- }
560- break;
561- case T_List:
562- foreach(temp, (List *) node)
563- {
564- JumbleExpr(jstate, (Node *) lfirst(temp));
565- }
566- break;
567- case T_IntList:
568- foreach(temp, (List *) node)
569- {
570- APP_JUMB(lfirst_int(temp));
571- }
572- break;
573- case T_SortGroupClause:
574- {
575- SortGroupClause *sgc = (SortGroupClause *) node;
576-
577- APP_JUMB(sgc->tleSortGroupRef);
578- APP_JUMB(sgc->eqop);
579- APP_JUMB(sgc->sortop);
580- APP_JUMB(sgc->nulls_first);
581- }
582- break;
583- case T_GroupingSet:
584- {
585- GroupingSet *gsnode = (GroupingSet *) node;
586-
587- JumbleExpr(jstate, (Node *) gsnode->content);
588- }
589- break;
590- case T_WindowClause:
591- {
592- WindowClause *wc = (WindowClause *) node;
593-
594- APP_JUMB(wc->winref);
595- APP_JUMB(wc->frameOptions);
596- JumbleExpr(jstate, (Node *) wc->partitionClause);
597- JumbleExpr(jstate, (Node *) wc->orderClause);
598- JumbleExpr(jstate, wc->startOffset);
599- JumbleExpr(jstate, wc->endOffset);
600- }
601- break;
602- case T_CommonTableExpr:
603- {
604- CommonTableExpr *cte = (CommonTableExpr *) node;
605-
606- /* we store the string name because RTE_CTE RTEs need it */
607- APP_JUMB_STRING(cte->ctename);
608- APP_JUMB(cte->ctematerialized);
609- JumbleQuery(jstate, castNode(Query, cte->ctequery));
610- }
611- break;
612- case T_SetOperationStmt:
613- {
614- SetOperationStmt *setop = (SetOperationStmt *) node;
615-
616- APP_JUMB(setop->op);
617- APP_JUMB(setop->all);
618- JumbleExpr(jstate, setop->larg);
619- JumbleExpr(jstate, setop->rarg);
620- }
621- break;
622- case T_RangeTblFunction:
623- {
624- RangeTblFunction *rtfunc = (RangeTblFunction *) node;
625-
626- JumbleExpr(jstate, rtfunc->funcexpr);
627- }
628- break;
629- case T_TableFunc:
630- {
631- TableFunc *tablefunc = (TableFunc *) node;
632-
633- JumbleExpr(jstate, tablefunc->docexpr);
634- JumbleExpr(jstate, tablefunc->rowexpr);
635- JumbleExpr(jstate, (Node *) tablefunc->colexprs);
636- }
637- break;
638- case T_TableSampleClause:
639- {
640- TableSampleClause *tsc = (TableSampleClause *) node;
641-
642- APP_JUMB(tsc->tsmhandler);
643- JumbleExpr(jstate, (Node *) tsc->args);
644- JumbleExpr(jstate, (Node *) tsc->repeatable);
645- }
646- break;
647- default:
648- /* Only a warning, since we can stumble along anyway */
649- elog(WARNING, "unrecognized node type: %d",
650- (int) nodeTag(node));
651- break;
652- }
653-}
654-
655-/*
656- * Record location of constant within query string of query tree
657- * that is currently being walked.
658- */
659-static void
660-RecordConstLocation(pgssJumbleState *jstate, int location)
661-{
662- /* -1 indicates unknown or undefined location */
663- if (location >= 0)
664- {
665- /* enlarge array if needed */
666- if (jstate->clocations_count >= jstate->clocations_buf_size)
667- {
668- jstate->clocations_buf_size *= 2;
669- jstate->clocations = (pgssLocationLen *)
670- repalloc(jstate->clocations,
671- jstate->clocations_buf_size *
672- sizeof(pgssLocationLen));
673- }
674- jstate->clocations[jstate->clocations_count].location = location;
675- /* initialize lengths to -1 to simplify fill_in_constant_lengths */
676- jstate->clocations[jstate->clocations_count].length = -1;
677- jstate->clocations_count++;
678- }
679-}
68022
68123 /*
68224 * Generate a normalized version of the query string that will be used to
@@ -698,8 +40,8 @@ RecordConstLocation(pgssJumbleState *jstate, int location)
69840 * Returns a palloc'd string.
69941 */
70042 static char *
701-generate_normalized_query(pgssJumbleState *jstate, const char *query,
702- int query_loc, int *query_len_p, int encoding)
43+generate_normalized_query(JumbleState *jstate, const char *query,
44+ int query_loc, int *query_len_p)
70345 {
70446 char *norm_query;
70547 int query_len = *query_len_p;
@@ -751,12 +93,11 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query,
75193 memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
75294 n_quer_loc += len_to_wrt;
75395
754- /*
755- * PG_HINT_PLAN: DON'T TAKE IN a6f22e8356 so that the designed behavior
756- * is kept stable.
757- */
758- /* And insert a '?' in place of the constant token */
759- norm_query[n_quer_loc++] = '?';
96+ /* And insert a param symbol in place of the constant token */
97+
98+ /* !!! START: HERE IS THE PART WHICH IS MODIFIED FOR PG_HINT_PLAN !!! */
99+ n_quer_loc += sprintf(norm_query + n_quer_loc, "?");
100+ /* !!! END: HERE IS THE PART WHICH IS MODIFIED FOR PG_HINT_PLAN !!! */
760101
761102 quer_loc = off + tok_len;
762103 last_off = off;
@@ -808,10 +149,10 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query,
808149 * reason for a constant to start with a '-'.
809150 */
810151 static void
811-fill_in_constant_lengths(pgssJumbleState *jstate, const char *query,
152+fill_in_constant_lengths(JumbleState *jstate, const char *query,
812153 int query_loc)
813154 {
814- pgssLocationLen *locs;
155+ LocationLen *locs;
815156 core_yyscan_t yyscanner;
816157 core_yy_extra_type yyextra;
817158 core_YYSTYPE yylval;
@@ -825,7 +166,7 @@ fill_in_constant_lengths(pgssJumbleState *jstate, const char *query,
825166 */
826167 if (jstate->clocations_count > 1)
827168 qsort(jstate->clocations, jstate->clocations_count,
828- sizeof(pgssLocationLen), comp_location);
169+ sizeof(LocationLen), comp_location);
829170 locs = jstate->clocations;
830171
831172 /* initialize the flex scanner --- should match raw_parser() */
@@ -905,13 +246,13 @@ fill_in_constant_lengths(pgssJumbleState *jstate, const char *query,
905246 }
906247
907248 /*
908- * comp_location: comparator for qsorting pgssLocationLen structs by location
249+ * comp_location: comparator for qsorting LocationLen structs by location
909250 */
910251 static int
911252 comp_location(const void *a, const void *b)
912253 {
913- int l = ((const pgssLocationLen *) a)->location;
914- int r = ((const pgssLocationLen *) b)->location;
254+ int l = ((const LocationLen *) a)->location;
255+ int r = ((const LocationLen *) b)->location;
915256
916257 if (l < r)
917258 return -1;
--- a/sql/pg_hint_plan.sql
+++ b/sql/pg_hint_plan.sql
@@ -592,9 +592,9 @@ SELECT t1_3.id FROM t1 t1_3, t2 t2_3, t3 t3_3 WHERE t1_3.id = t2_3.id AND t2_3.i
592592 SELECT max(t1_4.id) FROM t1 t1_4, t2 t2_4, t3 t3_4 WHERE t1_4.id = t2_4.id AND t2_4.id = t3_4.id
593593 );
594594
595--- ambigous error
595+-- ambiguous error
596596 EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
597-/*+NestLoop(t1 t2)*/
597+/*+MergeJoin(t1 t2)*/
598598 EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
599599 /*+Leading(t1 t2 t1)*/
600600 EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
Show on old repository browser