#
MODULES = pg_hint_plan
-HINTPLANVER = 1.3.7
+HINTPLANVER = 1.4
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
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
-STARBALL13 = pg_hint_plan13-$(HINTPLANVER).tar.gz
-STARBALLS = $(STARBALL13)
+STARBALL14 = pg_hint_plan14-$(HINTPLANVER).tar.gz
+STARBALLS = $(STARBALL14)
TARSOURCES = Makefile *.c *.h COPYRIGHT* \
pg_hint_plan--*.sql \
installcheck: $(REGRESSION_EXPECTED)
-rpms: rpm13
+rpms: rpm14
# pg_hint_plan.c includes core.c and make_join_rel.c
pg_hint_plan.o: core.c make_join_rel.c # pg_stat_statements.c
tar -chzf $@ $(addprefix $(subst .tar.gz,,$@)/, $^)
rm $(subst .tar.gz,,$@)
-rpm13: $(STARBALL13)
- MAKE_ROOT=`pwd` rpmbuild -bb SPECS/pg_hint_plan13.spec
+rpm14: $(STARBALL14)
+ MAKE_ROOT=`pwd` rpmbuild -bb SPECS/pg_hint_plan14.spec
# SPEC file for pg_store_plans
# Copyright(C) 2020 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
-%define _pgdir /usr/pgsql-13
+%define _pgdir /usr/pgsql-14
%define _bindir %{_pgdir}/bin
%define _libdir %{_pgdir}/lib
%define _datadir %{_pgdir}/share
## Set general information for pg_store_plans.
Summary: Optimizer hint on PostgreSQL 12
-Name: pg_hint_plan13
-Version: 1.3.7
+Name: pg_hint_plan14
+Version: 1.4
Release: 1%{?dist}
License: BSD
Group: Applications/Databases
Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION
## We use postgresql-devel package
-BuildRequires: postgresql13-devel
-Requires: postgresql13-server
+BuildRequires: postgresql14-devel
+Requires: postgresql14-server
## Description for "pg_hint_plan"
%description
pg_hint_plan provides capability to tweak execution plans to be
executed on PostgreSQL.
-Note that this package is available for only PostgreSQL 13.
+Note that this package is available for only PostgreSQL 14.
%package llvmjit
-Requires: postgresql13-server, postgresql13-llvmjit
-Requires: pg_hint_plan13 = 1.3.7
-Summary: Just-in-time compilation support for pg_hint_plan13
+Requires: postgresql14-server, postgresql14-llvmjit
+Requires: pg_hint_plan14 = 1.4
+Summary: Just-in-time compilation support for pg_hint_plan14
%description llvmjit
-Just-in-time compilation support for pg_hint_plan13
+Just-in-time compilation support for pg_hint_plan14
## pre work for build pg_hint_plan
%prep
-PATH=/usr/pgsql-13/bin:$PATH
+PATH=/usr/pgsql-14/bin:$PATH
if [ "${MAKE_ROOT}" != "" ]; then
pushd ${MAKE_ROOT}
make clean %{name}-%{version}.tar.gz
## Set variables for build environment
%build
-PATH=/usr/pgsql-13/bin:$PATH
+PATH=/usr/pgsql-14/bin:$PATH
make USE_PGXS=1 %{?_smp_mflags}
## Set variables for install
%defattr(0755,root,root)
%{_libdir}/pg_hint_plan.so
%defattr(0644,root,root)
-%{_datadir}/extension/pg_hint_plan--1.3.7.sql
+%{_datadir}/extension/pg_hint_plan--1.4.sql
%{_datadir}/extension/pg_hint_plan.control
%files llvmjit
# History of pg_hint_plan.
%changelog
-* Thu Oct 29 2020 Kyotaro Horiguchi
-- First release of pg_hint_plan13.
+* Tue Oct 05 2021 Kyotaro Horiguchi
+- Support PostgreSQL 14.
* function.
*
* static functions:
+ * set_rel_pathlist()
* set_plain_rel_pathlist()
+ * set_tablesample_rel_pathlist
+ * set_foreign_pathlist()
* set_append_rel_pathlist()
+ * set_function_pathlist()
+ * set_values_pathlist()
+ * set_tablefunc_pathlist()
* create_plain_partial_paths()
*
* src/backend/optimizer/path/joinrels.c
*-------------------------------------------------------------------------
*/
-static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
- RelOptInfo *rel2, RelOptInfo *joinrel,
- SpecialJoinInfo *sjinfo, List *restrictlist);
+#include "access/tsmapi.h"
+#include "catalog/pg_operator.h"
+#include "foreign/fdwapi.h"
/*
* set_plain_rel_pathlist
/*
+ * set_tablesample_rel_pathlist
+ * Build access paths for a sampled relation
+ */
+static void
+set_tablesample_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ Relids required_outer;
+ Path *path;
+
+ /*
+ * We don't support pushing join clauses into the quals of a samplescan,
+ * but it could still have required parameterization due to LATERAL refs
+ * in its tlist or TABLESAMPLE arguments.
+ */
+ required_outer = rel->lateral_relids;
+
+ /* Consider sampled scan */
+ path = create_samplescan_path(root, rel, required_outer);
+
+ /*
+ * If the sampling method does not support repeatable scans, we must avoid
+ * plans that would scan the rel multiple times. Ideally, we'd simply
+ * avoid putting the rel on the inside of a nestloop join; but adding such
+ * a consideration to the planner seems like a great deal of complication
+ * to support an uncommon usage of second-rate sampling methods. Instead,
+ * if there is a risk that the query might perform an unsafe join, just
+ * wrap the SampleScan in a Materialize node. We can check for joins by
+ * counting the membership of all_baserels (note that this correctly
+ * counts inheritance trees as single rels). If we're inside a subquery,
+ * we can't easily check whether a join might occur in the outer query, so
+ * just assume one is possible.
+ *
+ * GetTsmRoutine is relatively expensive compared to the other tests here,
+ * so check repeatable_across_scans last, even though that's a bit odd.
+ */
+ if ((root->query_level > 1 ||
+ bms_membership(root->all_baserels) != BMS_SINGLETON) &&
+ !(GetTsmRoutine(rte->tablesample->tsmhandler)->repeatable_across_scans))
+ {
+ path = (Path *) create_material_path(rel, path);
+ }
+
+ add_path(rel, path);
+
+ /* For the moment, at least, there are no other paths to consider */
+}
+
+
+/*
+ * set_foreign_pathlist
+ * Build access paths for a foreign table RTE
+ */
+static void
+set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ /* Call the FDW's GetForeignPaths function to generate path(s) */
+ rel->fdwroutine->GetForeignPaths(root, rel, rte->relid);
+}
+
+
+/*
+ * set_function_pathlist
+ * Build the (single) access path for a function RTE
+ */
+static void
+set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ Relids required_outer;
+ List *pathkeys = NIL;
+
+ /*
+ * We don't support pushing join clauses into the quals of a function
+ * scan, but it could still have required parameterization due to LATERAL
+ * refs in the function expression.
+ */
+ required_outer = rel->lateral_relids;
+
+ /*
+ * The result is considered unordered unless ORDINALITY was used, in which
+ * case it is ordered by the ordinal column (the last one). See if we
+ * care, by checking for uses of that Var in equivalence classes.
+ */
+ if (rte->funcordinality)
+ {
+ AttrNumber ordattno = rel->max_attr;
+ Var *var = NULL;
+ ListCell *lc;
+
+ /*
+ * Is there a Var for it in rel's targetlist? If not, the query did
+ * not reference the ordinality column, or at least not in any way
+ * that would be interesting for sorting.
+ */
+ foreach(lc, rel->reltarget->exprs)
+ {
+ Var *node = (Var *) lfirst(lc);
+
+ /* checking varno/varlevelsup is just paranoia */
+ if (IsA(node, Var) &&
+ node->varattno == ordattno &&
+ node->varno == rel->relid &&
+ node->varlevelsup == 0)
+ {
+ var = node;
+ break;
+ }
+ }
+
+ /*
+ * Try to build pathkeys for this Var with int8 sorting. We tell
+ * build_expression_pathkey not to build any new equivalence class; if
+ * the Var isn't already mentioned in some EC, it means that nothing
+ * cares about the ordering.
+ */
+ if (var)
+ pathkeys = build_expression_pathkey(root,
+ (Expr *) var,
+ NULL, /* below outer joins */
+ Int8LessOperator,
+ rel->relids,
+ false);
+ }
+
+ /* Generate appropriate path */
+ add_path(rel, create_functionscan_path(root, rel,
+ pathkeys, required_outer));
+}
+
+
+/*
+ * set_values_pathlist
+ * Build the (single) access path for a VALUES RTE
+ */
+static void
+set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ Relids required_outer;
+
+ /*
+ * We don't support pushing join clauses into the quals of a values scan,
+ * but it could still have required parameterization due to LATERAL refs
+ * in the values expressions.
+ */
+ required_outer = rel->lateral_relids;
+
+ /* Generate appropriate path */
+ add_path(rel, create_valuesscan_path(root, rel, required_outer));
+}
+
+/*
+ * set_tablefunc_pathlist
+ * Build the (single) access path for a table func RTE
+ */
+static void
+set_tablefunc_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ Relids required_outer;
+
+ /*
+ * We don't support pushing join clauses into the quals of a tablefunc
+ * scan, but it could still have required parameterization due to LATERAL
+ * refs in the function expression.
+ */
+ required_outer = rel->lateral_relids;
+
+ /* Generate appropriate path */
+ add_path(rel, create_tablefuncscan_path(root, rel,
+ required_outer));
+}
+
+
+/*
+ * set_rel_pathlist
+ * Build access paths for a base relation
+ */
+static void
+set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
+ Index rti, RangeTblEntry *rte)
+{
+ if (IS_DUMMY_REL(rel))
+ {
+ /* We already proved the relation empty, so nothing more to do */
+ }
+ else if (rte->inh)
+ {
+ /* It's an "append relation", process accordingly */
+ set_append_rel_pathlist(root, rel, rti, rte);
+ }
+ else
+ {
+ switch (rel->rtekind)
+ {
+ case RTE_RELATION:
+ if (rte->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ /* Foreign table */
+ set_foreign_pathlist(root, rel, rte);
+ }
+ else if (rte->tablesample != NULL)
+ {
+ /* Sampled relation */
+ set_tablesample_rel_pathlist(root, rel, rte);
+ }
+ else
+ {
+ /* Plain relation */
+ set_plain_rel_pathlist(root, rel, rte);
+ }
+ break;
+ case RTE_SUBQUERY:
+ /* Subquery --- fully handled during set_rel_size */
+ break;
+ case RTE_FUNCTION:
+ /* RangeFunction */
+ set_function_pathlist(root, rel, rte);
+ break;
+ case RTE_TABLEFUNC:
+ /* Table Function */
+ set_tablefunc_pathlist(root, rel, rte);
+ break;
+ case RTE_VALUES:
+ /* Values list */
+ set_values_pathlist(root, rel, rte);
+ break;
+ case RTE_CTE:
+ /* CTE reference --- fully handled during set_rel_size */
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ /* tuplestore reference --- fully handled during set_rel_size */
+ break;
+ case RTE_RESULT:
+ /* simple Result --- fully handled during set_rel_size */
+ break;
+ default:
+ elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
+ break;
+ }
+ }
+
+ /*
+ * Allow a plugin to editorialize on the set of Paths for this base
+ * relation. It could add new paths (such as CustomPaths) by calling
+ * add_path(), or add_partial_path() if parallel aware. It could also
+ * delete or modify paths added by the core code.
+ */
+ if (set_rel_pathlist_hook)
+ (*set_rel_pathlist_hook) (root, rel, rti, rte);
+
+ /*
+ * If this is a baserel, we should normally consider gathering any partial
+ * paths we may have created for it. We have to do this after calling the
+ * set_rel_pathlist_hook, else it cannot add partial paths to be included
+ * here.
+ *
+ * However, if this is an inheritance child, skip it. Otherwise, we could
+ * end up with a very large number of gather nodes, each trying to grab
+ * its own pool of workers. Instead, we'll consider gathering partial
+ * paths for the parent appendrel.
+ *
+ * Also, if this is the topmost scan/join rel (that is, the only baserel),
+ * we postpone gathering until the final scan/join targetlist is available
+ * (see grouping_planner).
+ */
+ if (rel->reloptkind == RELOPT_BASEREL &&
+ bms_membership(root->all_baserels) != BMS_SINGLETON)
+ generate_useful_gather_paths(root, rel, false);
+
+ /* Now find the cheapest of the paths for this rel */
+ set_cheapest(rel);
+
+#ifdef OPTIMIZER_DEBUG
+ debug_print_rel(root, rel);
+#endif
+}
+
+
+/*
* set_append_rel_pathlist
* Build access paths for an "append relation"
*/
if (IS_DUMMY_REL(childrel))
continue;
- /* Bubble up childrel's partitioned children. */
- if (rel->part_scheme)
- rel->partitioned_child_rels =
- list_concat(rel->partitioned_child_rels,
- childrel->partitioned_child_rels);
-
/*
* Child is live, so add it to the live_childrels list for use below.
*/
join_search_one_level(root, lev);
/*
- * Run generate_partitionwise_join_paths() and generate_gather_paths()
- * for each just-processed joinrel. We could not do this earlier
- * because both regular and partial paths can get added to a
- * particular joinrel at multiple times within join_search_one_level.
+ * Run generate_partitionwise_join_paths() and
+ * generate_useful_gather_paths() for each just-processed joinrel. We
+ * could not do this earlier because both regular and partial paths
+ * can get added to a particular joinrel at multiple times within
+ * join_search_one_level.
*
* After that, we're done creating paths for the joinrel, so run
* set_cheapest().
constraint_exclusion | partition | Query Tuning / Other Planner Options
cursor_tuple_fraction | 0.1 | Query Tuning / Other Planner Options
default_statistics_target | 100 | Query Tuning / Other Planner Options
- force_parallel_mode | off | Query Tuning / Other Planner Options
from_collapse_limit | 8 | Query Tuning / Other Planner Options
jit | on | Query Tuning / Other Planner Options
join_collapse_limit | 8 | Query Tuning / Other Planner Options
parallel_tuple_cost | 0.1 | Query Tuning / Planner Cost Constants
random_page_cost | 4 | Query Tuning / Planner Cost Constants
seq_page_cost | 1 | Query Tuning / Planner Cost Constants
+ enable_async_append | on | Query Tuning / Planner Method Configuration
enable_bitmapscan | on | Query Tuning / Planner Method Configuration
enable_gathermerge | on | Query Tuning / Planner Method Configuration
enable_hashagg | on | Query Tuning / Planner Method Configuration
enable_indexonlyscan | on | Query Tuning / Planner Method Configuration
enable_indexscan | on | Query Tuning / Planner Method Configuration
enable_material | on | Query Tuning / Planner Method Configuration
+ enable_memoize | on | Query Tuning / Planner Method Configuration
enable_mergejoin | on | Query Tuning / Planner Method Configuration
enable_nestloop | on | Query Tuning / Planner Method Configuration
enable_parallel_append | on | Query Tuning / Planner Method Configuration
enable_seqscan | on | Query Tuning / Planner Method Configuration
enable_sort | on | Query Tuning / Planner Method Configuration
enable_tidscan | on | Query Tuning / Planner Method Configuration
-(47 rows)
+(48 rows)
ANALYZE;
(4 rows)
EXPLAIN (COSTS false) SELECT * FROM t1, t2 WHERE t1.val = t2.val;
- QUERY PLAN
---------------------------------
- Hash Join
- Hash Cond: (t2.val = t1.val)
+ QUERY PLAN
+-------------------------------------------
+ Nested Loop
-> Seq Scan on t2
- -> Hash
- -> Seq Scan on t1
-(5 rows)
+ -> Memoize
+ Cache Key: t2.val
+ -> Index Scan using t1_val on t1
+ Index Cond: (val = t2.val)
+(6 rows)
LOAD 'pg_hint_plan';
SET pg_hint_plan.debug_print TO on;
(4 rows)
EXPLAIN (COSTS false) SELECT * FROM t1, t2 WHERE t1.val = t2.val;
- QUERY PLAN
---------------------------------
- Hash Join
- Hash Cond: (t2.val = t1.val)
+ QUERY PLAN
+-------------------------------------------
+ Nested Loop
-> Seq Scan on t2
- -> Hash
- -> Seq Scan on t1
-(5 rows)
+ -> Memoize
+ Cache Key: t2.val
+ -> Index Scan using t1_val on t1
+ Index Cond: (val = t2.val)
+(6 rows)
/*+ Test (t1 t2) */
EXPLAIN (COSTS false) SELECT * FROM t1, t2 WHERE t1.id = t2.id;
Index Cond: (id = $1)
(51 rows)
--- ambigous error
+-- ambiguous error
EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
- QUERY PLAN
--------------------------------------------------
- Merge Join
- Merge Cond: (t1.id = t2.id)
- -> Merge Join
- Merge Cond: (t1.id = t1_1.id)
+ QUERY PLAN
+--------------------------------------------
+ Nested Loop
+ -> Nested Loop
+ -> Seq Scan on t1 t1_1
-> Index Scan using t1_pkey on t1
- -> Index Scan using t1_pkey on t1 t1_1
+ Index Cond: (id = t1_1.id)
-> Index Scan using t2_pkey on t2
+ Index Cond: (id = t1.id)
(7 rows)
-/*+NestLoop(t1 t2)*/
+/*+MergeJoin(t1 t2)*/
EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
-INFO: pg_hint_plan: hint syntax error at or near "NestLoop(t1 t2)"
+INFO: pg_hint_plan: hint syntax error at or near "MergeJoin(t1 t2)"
DETAIL: Relation name "t1" is ambiguous.
LOG: pg_hint_plan:
used hint:
not used hint:
duplication hint:
error hint:
-NestLoop(t1 t2)
+MergeJoin(t1 t2)
- QUERY PLAN
--------------------------------------------------
- Merge Join
- Merge Cond: (t1.id = t2.id)
- -> Merge Join
- Merge Cond: (t1.id = t1_1.id)
+ QUERY PLAN
+--------------------------------------------
+ Nested Loop
+ -> Nested Loop
+ -> Seq Scan on t1 t1_1
-> Index Scan using t1_pkey on t1
- -> Index Scan using t1_pkey on t1 t1_1
+ Index Cond: (id = t1_1.id)
-> Index Scan using t2_pkey on t2
+ Index Cond: (id = t1.id)
(7 rows)
/*+Leading(t1 t2 t1)*/
error hint:
Leading(t1 t2 t1)
- QUERY PLAN
--------------------------------------------------
- Merge Join
- Merge Cond: (t1.id = t2.id)
- -> Merge Join
- Merge Cond: (t1.id = t1_1.id)
+ QUERY PLAN
+--------------------------------------------
+ Nested Loop
+ -> Nested Loop
+ -> Seq Scan on t1 t1_1
-> Index Scan using t1_pkey on t1
- -> Index Scan using t1_pkey on t1 t1_1
+ Index Cond: (id = t1_1.id)
-> Index Scan using t2_pkey on t2
+ Index Cond: (id = t1.id)
(7 rows)
-- identifier length test
duplication hint:
error hint:
-CONTEXT: SQL statement "SELECT 1, /*+ SeqScan(t1) */ * from t1"
+CONTEXT: SQL statement "SELECT 1, /*+ SeqScan(t1) */ * from t1"
PL/pgSQL function testfunc() line 3 at PERFORM
testfunc
----------
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | on
enable_material | on
+ enable_memoize | on
enable_mergejoin | on
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
SET pg_hint_plan.parse_messages TO error;
/*+Set(enable_seqscan off)Set(geqo_threshold 100)SeqScan(t1)MergeJoin(t1 t2)NestLoop(t1 t1)*/
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | on
enable_material | on
+ enable_memoize | on
enable_mergejoin | on
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
/*+Set(enable_seqscan off)Set(geqo_threshold 100)SeqScan(t1)MergeJoin(t1 t2)*/
EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | on
enable_material | on
+ enable_memoize | on
enable_mergejoin | on
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
SET pg_hint_plan.parse_messages TO error;
/*+Set(enable_seqscan off)Set(geqo_threshold 100)SeqScan(t1)MergeJoin(t1 t2)NestLoop(t1 t1)*/
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | on
enable_material | on
+ enable_memoize | on
enable_mergejoin | on
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
EXPLAIN (COSTS false) EXECUTE p1;
QUERY PLAN
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | on
enable_material | on
+ enable_memoize | on
enable_mergejoin | on
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
SET pg_hint_plan.parse_messages TO error;
EXPLAIN (COSTS false) EXECUTE p2;
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | on
enable_material | on
+ enable_memoize | on
enable_mergejoin | on
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
-- No. A-12-1-4
-- No. A-12-2-4
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | on
enable_material | on
+ enable_memoize | on
enable_mergejoin | on
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
SET pg_hint_plan.parse_messages TO error;
EXPLAIN (COSTS false) EXECUTE p2;
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | on
enable_material | on
+ enable_memoize | on
enable_mergejoin | on
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
DEALLOCATE p1;
SET pg_hint_plan.parse_messages TO LOG;
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | off
enable_material | on
+ enable_memoize | on
enable_mergejoin | off
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
/*+Set(enable_indexscan on)Set(geqo_threshold 100)IndexScan(t2)MergeJoin(t1 t2)Leading(t2 t1)*/
EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | off
enable_material | on
+ enable_memoize | on
enable_mergejoin | off
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
QUERY PLAN
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | off
enable_material | on
+ enable_memoize | on
enable_mergejoin | off
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
BEGIN;
/*+Set(enable_indexscan on)Set(geqo_threshold 100)IndexScan(t2)MergeJoin(t1 t2)Leading(t2 t1)*/
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | off
enable_material | on
+ enable_memoize | on
enable_mergejoin | off
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
QUERY PLAN
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | off
enable_material | on
+ enable_memoize | on
enable_mergejoin | off
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
/*+Set(enable_indexscan on)Set(geqo_threshold 100)IndexScan(t2)MergeJoin(t1 t2)Leading(t2 t1)*/
EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
constraint_exclusion | partition
cursor_tuple_fraction | 0.1
default_statistics_target | 100
- force_parallel_mode | off
from_collapse_limit | 8
jit | on
join_collapse_limit | 8
parallel_tuple_cost | 0.1
random_page_cost | 4
seq_page_cost | 1
+ enable_async_append | on
enable_bitmapscan | on
enable_gathermerge | on
enable_hashagg | on
enable_indexonlyscan | on
enable_indexscan | off
enable_material | on
+ enable_memoize | on
enable_mergejoin | off
enable_nestloop | on
enable_parallel_append | on
enable_seqscan | on
enable_sort | on
enable_tidscan | on
-(47 rows)
+(48 rows)
EXPLAIN (COSTS false) SELECT * FROM s1.t1, s1.t2 WHERE t1.c1 = t2.c1;
QUERY PLAN
-- No. S-3-8-4
EXPLAIN (COSTS false) UPDATE s1.p1 SET c4 = c4 WHERE c1 = 1;
- QUERY PLAN
------------------------------
+ QUERY PLAN
+-----------------------------------
Update on p1
- Update on p1
- Update on p1c1 p1_1
- -> Seq Scan on p1
- Filter: (c1 = 1)
- -> Seq Scan on p1c1 p1_1
- Filter: (c1 = 1)
-(7 rows)
+ Update on p1 p1_1
+ Update on p1c1 p1_2
+ -> Append
+ -> Seq Scan on p1 p1_1
+ Filter: (c1 = 1)
+ -> Seq Scan on p1c1 p1_2
+ Filter: (c1 = 1)
+(8 rows)
/*+IndexScan(p1)*/
EXPLAIN (COSTS false) UPDATE s1.p1 SET c4 = c4 WHERE c1 = 1;
duplication hint:
error hint:
- QUERY PLAN
---------------------------------------------
+ QUERY PLAN
+--------------------------------------------------
Update on p1
- Update on p1
- Update on p1c1 p1_1
- -> Index Scan using p1_i on p1
- Index Cond: (c1 = 1)
- -> Index Scan using p1c1_i on p1c1 p1_1
- Index Cond: (c1 = 1)
-(7 rows)
+ Update on p1 p1_1
+ Update on p1c1 p1_2
+ -> Append
+ -> Index Scan using p1_i on p1 p1_1
+ Index Cond: (c1 = 1)
+ -> Index Scan using p1c1_i on p1c1 p1_2
+ Index Cond: (c1 = 1)
+(8 rows)
/*+IndexScan(p1 p1_pkey)*/
EXPLAIN (COSTS false) UPDATE s1.p1 SET c4 = c4 WHERE c1 = 1;
LOG: available indexes for IndexScan(p1): p1_pkey
LOG: available indexes for IndexScan(p1c1): p1c1_pkey
-LOG: available indexes for IndexScan(p1): p1_pkey
-LOG: available indexes for IndexScan(p1c1): p1c1_pkey
LOG: pg_hint_plan:
used hint:
IndexScan(p1 p1_pkey)
duplication hint:
error hint:
- QUERY PLAN
------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------
Update on p1
- Update on p1
- Update on p1c1 p1_1
- -> Index Scan using p1_pkey on p1
- Index Cond: (c1 = 1)
- -> Index Scan using p1c1_pkey on p1c1 p1_1
- Index Cond: (c1 = 1)
-(7 rows)
+ Update on p1 p1_1
+ Update on p1c1 p1_2
+ -> Append
+ -> Index Scan using p1_pkey on p1 p1_1
+ Index Cond: (c1 = 1)
+ -> Index Scan using p1c1_pkey on p1c1 p1_2
+ Index Cond: (c1 = 1)
+(8 rows)
----
---- No. S-3-9 inheritance table number
joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,
&restrictlist);
- /* !!! START: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
+ /* !!! START: HERE IS THE PART WHICH IS ADDED FOR PG_HINT_PLAN !!! */
{
RowsHint *rows_hint = NULL;
int i;
}
}
- /* !!! END: HERE IS THE PART WHICH ADDED FOR PG_HINT_PLAN !!! */
+ /* !!! END: HERE IS THE PART WHICH IS ADDED FOR PG_HINT_PLAN !!! */
/*
* If we've already proven this join is empty, we needn't consider any
#ifndef NORMALIZE_QUERY_H
#define NORMALIZE_QUERY_H
-/*
- * Struct for tracking locations/lengths of constants during normalization
- */
-typedef struct pgssLocationLen
-{
- int location; /* start offset in query text */
- int length; /* length in bytes, or -1 to ignore */
-} pgssLocationLen;
-
-/*
- * Working state for computing a query jumble and producing a normalized
- * query string
- */
-typedef struct pgssJumbleState
-{
- /* Jumble of current query tree */
- unsigned char *jumble;
-
- /* Number of bytes used in jumble[] */
- Size jumble_len;
-
- /* Array of locations of constants that should be removed */
- pgssLocationLen *clocations;
-
- /* Allocated length of clocations array */
- int clocations_buf_size;
-
- /* Current number of valid entries in clocations array */
- int clocations_count;
-
- /* highest Param id we've seen, in order to start normalization correctly */
- int highest_extern_param_id;
-} pgssJumbleState;
-
static char *
-generate_normalized_query(pgssJumbleState *jstate, const char *query,
- int query_loc, int *query_len_p, int encoding);
-static void JumbleQuery(pgssJumbleState *jstate, Query *query);
-
-#define JUMBLE_SIZE 1024
-
+generate_normalized_query(JumbleState *jstate, const char *query,
+ int query_loc, int *query_len_p);
#endif /* NORMALIZE_QUERY_H */
-/* pg_hint_plan/pg_hint_plan--1.3.7.sql */
+/* pg_hint_plan/pg_hint_plan--1.4.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pg_hint_plan" to load this file. \quit
static char qnostr[32];
static const char *current_hint_str = NULL;
-/*
- * However we usually take a hint stirng in post_parse_analyze_hook, we still
- * need to do so in planner_hook when client starts query execution from the
- * bind message on a prepared query. This variable prevent duplicate and
- * sometimes harmful hint string retrieval.
- */
-static bool current_hint_retrieved = false;
-
/* common data for all hints. */
struct Hint
{
static void push_hint(HintState *hstate);
static void pop_hint(void);
-static void pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query);
-static void pg_hint_plan_ProcessUtility(PlannedStmt *pstmt,
- const char *queryString,
- ProcessUtilityContext context,
- ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, QueryCompletion *qc);
static PlannedStmt *pg_hint_plan_planner(Query *parse, const char *query_string,
int cursorOptions,
ParamListInfo boundParams);
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
-static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
- Index rti, RangeTblEntry *rte);
RelOptInfo *pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1,
RelOptInfo *rel2);
};
/* Saved hook values in case of unload */
-static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
static planner_hook_type prev_planner = NULL;
static join_search_hook_type prev_join_search = NULL;
static set_rel_pathlist_hook_type prev_set_rel_pathlist = NULL;
-static ProcessUtility_hook_type prev_ProcessUtility_hook = NULL;
/* Hold reference to currently active hint */
static HintState *current_hint_state = NULL;
NULL);
/* Install hooks. */
- prev_post_parse_analyze_hook = post_parse_analyze_hook;
- post_parse_analyze_hook = pg_hint_plan_post_parse_analyze;
prev_planner = planner_hook;
planner_hook = pg_hint_plan_planner;
prev_join_search = join_search_hook;
join_search_hook = pg_hint_plan_join_search;
prev_set_rel_pathlist = set_rel_pathlist_hook;
set_rel_pathlist_hook = pg_hint_plan_set_rel_pathlist;
- prev_ProcessUtility_hook = ProcessUtility_hook;
- ProcessUtility_hook = pg_hint_plan_ProcessUtility;
/* setup PL/pgSQL plugin hook */
var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
PLpgSQL_plugin **var_ptr;
/* Uninstall hooks. */
- post_parse_analyze_hook = prev_post_parse_analyze_hook;
planner_hook = prev_planner;
join_search_hook = prev_join_search;
set_rel_pathlist_hook = prev_set_rel_pathlist;
- ProcessUtility_hook = prev_ProcessUtility_hook;
/* uninstall PL/pgSQL plugin hook */
var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
}
/*
- * Get client-supplied query string. Addtion to that the jumbled query is
- * supplied if the caller requested. From the restriction of JumbleQuery, some
- * kind of query needs special amendments. Reutrns NULL if this query doesn't
- * change the current hint. This function returns NULL also when something
- * wrong has happend and let the caller continue using the current hints.
- */
-static const char *
-get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
-{
- const char *p = debug_query_string;
-
- /*
- * If debug_query_string is set, it is the top level statement. But in some
- * cases we reach here with debug_query_string set NULL for example in the
- * case of DESCRIBE message handling or EXECUTE command. We may still see a
- * candidate top-level query in pstate in the case.
- */
- if (pstate && pstate->p_sourcetext)
- p = pstate->p_sourcetext;
-
- /* We don't see a query string, return NULL */
- if (!p)
- return NULL;
-
- if (jumblequery != NULL)
- *jumblequery = query;
-
- if (query->commandType == CMD_UTILITY)
- {
- Query *target_query = (Query *)query->utilityStmt;
-
- /*
- * Some CMD_UTILITY statements have a subquery that we can hint on.
- * Since EXPLAIN can be placed before other kind of utility statements
- * and EXECUTE can be contained other kind of utility statements, these
- * conditions are not mutually exclusive and should be considered in
- * this order.
- */
- if (IsA(target_query, ExplainStmt))
- {
- ExplainStmt *stmt = (ExplainStmt *)target_query;
-
- Assert(IsA(stmt->query, Query));
- target_query = (Query *)stmt->query;
-
- /* strip out the top-level query for further processing */
- if (target_query->commandType == CMD_UTILITY &&
- target_query->utilityStmt != NULL)
- target_query = (Query *)target_query->utilityStmt;
- }
-
- if (IsA(target_query, DeclareCursorStmt))
- {
- DeclareCursorStmt *stmt = (DeclareCursorStmt *)target_query;
- Query *query = (Query *)stmt->query;
-
- /* the target must be CMD_SELECT in this case */
- Assert(IsA(query, Query) && query->commandType == CMD_SELECT);
- target_query = query;
- }
-
- if (IsA(target_query, CreateTableAsStmt))
- {
- CreateTableAsStmt *stmt = (CreateTableAsStmt *) target_query;
-
- Assert(IsA(stmt->query, Query));
- target_query = (Query *) stmt->query;
-
- /* strip out the top-level query for further processing */
- if (target_query->commandType == CMD_UTILITY &&
- target_query->utilityStmt != NULL)
- target_query = (Query *)target_query->utilityStmt;
- }
-
- if (IsA(target_query, ExecuteStmt))
- {
- /*
- * Use the prepared query for EXECUTE. The Query for jumble
- * also replaced with the corresponding one.
- */
- ExecuteStmt *stmt = (ExecuteStmt *)target_query;
- PreparedStatement *entry;
-
- entry = FetchPreparedStatement(stmt->name, true);
-
- if (entry->plansource->is_valid)
- {
- p = entry->plansource->query_string;
- target_query = (Query *) linitial (entry->plansource->query_list);
- }
- else
- {
- /* igonre the hint for EXECUTE if invalidated */
- p = NULL;
- target_query = NULL;
- }
- }
-
- /* JumbleQuery accespts only a non-utility Query */
- if (target_query &&
- (!IsA(target_query, Query) ||
- target_query->utilityStmt != NULL))
- target_query = NULL;
-
- if (jumblequery)
- *jumblequery = target_query;
- }
- /*
- * Return NULL if pstate is not of top-level query. We don't need this
- * when jumble info is not requested or cannot do this when pstate is NULL.
- */
- else if (!jumblequery && pstate && pstate->p_sourcetext != p &&
- strcmp(pstate->p_sourcetext, p) != 0)
- p = NULL;
-
- return p;
-}
-
-/*
* Get hints from the head block comment in client-supplied query string.
*/
static const char *
* Retrieve and store hint string from given query or from the hint table.
*/
static void
-get_current_hint_string(ParseState *pstate, Query *query)
+get_current_hint_string(Query *query, const char *query_str)
{
- const char *query_str;
MemoryContext oldcontext;
- /* do nothing under hint table search */
+ /* do nothing while scanning hint table */
if (hint_inhibit_level > 0)
return;
- /* We alredy have one, don't parse it again. */
- if (current_hint_retrieved)
- return;
-
- /* Don't parse the current query hereafter */
- current_hint_retrieved = true;
-
- if (!pg_hint_plan_enable_hint)
+ /* Make sure trashing old hint string */
+ if (current_hint_str)
{
- if (current_hint_str)
- {
- pfree((void *)current_hint_str);
- current_hint_str = NULL;
- }
- return;
+ pfree((void *)current_hint_str);
+ current_hint_str = NULL;
}
+ /* Return if nothing to do. */
+ if (!pg_hint_plan_enable_hint || !query_str)
+ return;
+
/* increment the query number */
qnostr[0] = 0;
if (debug_level > 1)
/* search the hint table for a hint if requested */
if (pg_hint_plan_enable_hint_table)
{
+ JumbleState *jstate;
int query_len;
- pgssJumbleState jstate;
- Query *jumblequery;
- char *normalized_query = NULL;
-
- query_str = get_query_string(pstate, query, &jumblequery);
-
- /* If this query is not for hint, just return */
- if (!query_str)
- return;
+ char *normalized_query;
- /* clear the previous hint string */
- if (current_hint_str)
- {
- pfree((void *)current_hint_str);
- current_hint_str = NULL;
- }
-
- if (jumblequery)
- {
- /*
- * XXX: normalization code is copied from pg_stat_statements.c.
- * Make sure to keep up-to-date with it.
- */
- jstate.jumble = (unsigned char *) palloc(JUMBLE_SIZE);
- jstate.jumble_len = 0;
- jstate.clocations_buf_size = 32;
- jstate.clocations = (pgssLocationLen *)
- palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
- jstate.clocations_count = 0;
+ jstate = JumbleQuery(query, query_str);
- JumbleQuery(&jstate, jumblequery);
-
- /*
- * Normalize the query string by replacing constants with '?'
- */
- /*
- * Search hint string which is stored keyed by query string
- * and application name. The query string is normalized to allow
- * fuzzy matching.
- *
- * Adding 1 byte to query_len ensures that the returned string has
- * a terminating NULL.
- */
- query_len = strlen(query_str) + 1;
- normalized_query =
- generate_normalized_query(&jstate, query_str, 0, &query_len,
- GetDatabaseEncoding());
+ /*
+ * Normalize the query string by replacing constants with '?'
+ */
+ /*
+ * Search hint string which is stored keyed by query string
+ * and application name. The query string is normalized to allow
+ * fuzzy matching.
+ *
+ * Adding 1 byte to query_len ensures that the returned string has
+ * a terminating NULL.
+ */
+ query_len = strlen(query_str) + 1;
+ normalized_query =
+ generate_normalized_query(jstate, query_str, 0, &query_len);
- /*
- * find a hint for the normalized query. the result should be in
- * TopMemoryContext
- */
- oldcontext = MemoryContextSwitchTo(TopMemoryContext);
- current_hint_str =
- get_hints_from_table(normalized_query, application_name);
- MemoryContextSwitchTo(oldcontext);
+ /*
+ * find a hint for the normalized query. the result should be in
+ * TopMemoryContext
+ */
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ current_hint_str =
+ get_hints_from_table(normalized_query, application_name);
+ MemoryContextSwitchTo(oldcontext);
- if (debug_level > 1)
- {
- if (current_hint_str)
- ereport(pg_hint_plan_debug_message_level,
- (errmsg("pg_hint_plan[qno=0x%x]: "
- "post_parse_analyze_hook: "
- "hints from table: \"%s\": "
- "normalized_query=\"%s\", "
- "application name =\"%s\"",
- qno, current_hint_str,
- normalized_query, application_name),
- errhidestmt(msgqno != qno),
- errhidecontext(msgqno != qno)));
- else
- ereport(pg_hint_plan_debug_message_level,
- (errmsg("pg_hint_plan[qno=0x%x]: "
- "no match found in table: "
- "application name = \"%s\", "
- "normalized_query=\"%s\"",
- qno, application_name,
- normalized_query),
- errhidestmt(msgqno != qno),
- errhidecontext(msgqno != qno)));
-
- msgqno = qno;
- }
+ if (debug_level > 1)
+ {
+ if (current_hint_str)
+ ereport(pg_hint_plan_debug_message_level,
+ (errmsg("pg_hint_plan[qno=0x%x]: "
+ "hints from table: \"%s\": "
+ "normalized_query=\"%s\", "
+ "application name =\"%s\"",
+ qno, current_hint_str,
+ normalized_query, application_name),
+ errhidestmt(msgqno != qno),
+ errhidecontext(msgqno != qno)));
+ else
+ ereport(pg_hint_plan_debug_message_level,
+ (errmsg("pg_hint_plan[qno=0x%x]: "
+ "no match found in table: "
+ "application name = \"%s\", "
+ "normalized_query=\"%s\"",
+ qno, application_name,
+ normalized_query),
+ errhidestmt(msgqno != qno),
+ errhidecontext(msgqno != qno)));
+
+ msgqno = qno;
}
- /* retrun if we have hint here */
+ /* retrun if we have hint string here */
if (current_hint_str)
return;
}
- else
- query_str = get_query_string(pstate, query, NULL);
-
- if (query_str)
- {
- /*
- * get hints from the comment. However we may have the same query
- * string with the previous call, but the extra comparison seems no
- * use..
- */
- if (current_hint_str)
- pfree((void *)current_hint_str);
- oldcontext = MemoryContextSwitchTo(TopMemoryContext);
- current_hint_str = get_hints_from_comment(query_str);
- MemoryContextSwitchTo(oldcontext);
- }
- else
- {
- /*
- * Failed to get query. We would be in fetching invalidated
- * plancache. Try the next chance.
- */
- current_hint_retrieved = false;
- }
+ /* get hints from the comment */
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ current_hint_str = get_hints_from_comment(query_str);
+ MemoryContextSwitchTo(oldcontext);
if (debug_level > 1)
{
}
/*
- * Retrieve hint string from the current query.
- */
-static void
-pg_hint_plan_post_parse_analyze(ParseState *pstate, Query *query)
-{
- if (prev_post_parse_analyze_hook)
- prev_post_parse_analyze_hook(pstate, query);
-
- /* always retrieve hint from the top-level query string */
- if (plpgsql_recurse_level == 0)
- current_hint_retrieved = false;
-
- get_current_hint_string(pstate, query);
-}
-
-/*
- * We need to reset current_hint_retrieved flag always when a command execution
- * is finished. This is true even for a pure utility command that doesn't
- * involve planning phase.
- */
-static void
-pg_hint_plan_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
- ProcessUtilityContext context,
- ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, QueryCompletion *qc)
-{
- if (prev_ProcessUtility_hook)
- prev_ProcessUtility_hook(pstmt, queryString, context, params, queryEnv,
- dest, qc);
- else
- standard_ProcessUtility(pstmt, queryString, context, params, queryEnv,
- dest, qc);
-
- if (plpgsql_recurse_level == 0)
- current_hint_retrieved = false;
-}
-
-/*
* Read and set up hint information
*/
static PlannedStmt *
goto standard_planner_proc;
}
- /*
- * Support for nested plpgsql functions. This is quite ugly but this is the
- * only point I could find where I can get the query string.
- */
- if (plpgsql_recurse_level > 0 &&
- error_context_stack && error_context_stack->arg)
+ /* always retrieve hint from the top-level query string */
+ if (plpgsql_recurse_level == 0 && current_hint_str)
{
- MemoryContext oldcontext;
-
- oldcontext = MemoryContextSwitchTo(TopMemoryContext);
- current_hint_str =
- get_hints_from_comment((char *)error_context_stack->arg);
- MemoryContextSwitchTo(oldcontext);
+ pfree((void *)current_hint_str);
+ current_hint_str = NULL;
}
- /*
- * Query execution in extended protocol can be started without the analyze
- * phase. In the case retrieve hint string here.
- */
- if (!current_hint_str)
- get_current_hint_string(NULL, parse);
+ get_current_hint_string(parse, query_string);
/* No hint, go the normal way */
if (!current_hint_str)
/* parse the hint into hint state struct */
hstate = create_hintstate(parse, pstrdup(current_hint_str));
- /* run standard planner if the statement has not valid hint */
+ /* run standard planner if we're given with no valid hints */
if (!hstate)
+ {
+ /* forget invalid hint string */
+ if (current_hint_str)
+ {
+ pfree((void *)current_hint_str);
+ current_hint_str = NULL;
+ }
+
goto standard_planner_proc;
+ }
/*
* Push new hint struct to the hint stack to disable previous hint context.
{
pfree((void *)current_hint_str);
current_hint_str = NULL;
- current_hint_retrieved = false;
}
/* Print hint in debug mode. */
return 0;
}
- /*
- * Forget about the parent of another subquery, but don't forget if the
- * inhTargetkind of the root is not INHKIND_NONE, which signals the root
- * contains only appendrel members. See inheritance_planner for details.
- *
- * (PG12.0) 428b260f87 added one more planning cycle for updates on
- * partitioned tables and hints set up in the cycle are overriden by the
- * second cycle. Since I didn't find no apparent distinction between the
- * PlannerRoot of the cycle and that of ordinary CMD_SELECT, pg_hint_plan
- * accepts both cycles and the later one wins. In the second cycle root
- * doesn't have inheritance information at all so use the parent_relid set
- * in the first cycle.
- */
- if (root->inhTargetKind == INHKIND_NONE)
+ if (bms_num_members(rel->top_parent_relids) == 1)
{
- if (root != current_hint_state->current_root)
- current_hint_state->parent_relid = 0;
-
- /* Find the parent for this relation other than the registered parent */
- foreach (l, root->append_rel_list)
- {
- AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
-
- if (appinfo->child_relid == rel->relid)
- {
- if (current_hint_state->parent_relid != appinfo->parent_relid)
- {
- new_parent_relid = appinfo->parent_relid;
- current_hint_state->current_root = root;
- }
- break;
- }
- }
-
- if (!l)
- {
- /*
- * This relation doesn't have a parent. Cancel
- * current_hint_state.
- */
- current_hint_state->parent_relid = 0;
- current_hint_state->parent_scan_hint = NULL;
- current_hint_state->parent_parallel_hint = NULL;
- }
+ new_parent_relid = bms_next_member(rel->top_parent_relids, -1);
+ current_hint_state->current_root = root;
+ Assert(new_parent_relid > 0);
+ }
+ else
+ {
+ /* This relation doesn't have a parent. Cancel current_hint_state. */
+ current_hint_state->parent_relid = 0;
+ current_hint_state->parent_scan_hint = NULL;
+ current_hint_state->parent_parallel_hint = NULL;
}
if (new_parent_relid > 0)
}
/*
- * set_rel_pathlist
- * Build access paths for a base relation
- *
- * This function was copied and edited from set_rel_pathlist() in
- * src/backend/optimizer/path/allpaths.c in order not to copy other static
- * functions not required here.
- */
-static void
-set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
- Index rti, RangeTblEntry *rte)
-{
- if (IS_DUMMY_REL(rel))
- {
- /* We already proved the relation empty, so nothing more to do */
- }
- else if (rte->inh)
- {
- /* It's an "append relation", process accordingly */
- set_append_rel_pathlist(root, rel, rti, rte);
- }
- else
- {
- if (rel->rtekind == RTE_RELATION)
- {
- if (rte->relkind == RELKIND_RELATION)
- {
- if(rte->tablesample != NULL)
- elog(ERROR, "sampled relation is not supported");
-
- /* Plain relation */
- set_plain_rel_pathlist(root, rel, rte);
- }
- else
- elog(ERROR, "unexpected relkind: %c", rte->relkind);
- }
- else
- elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
- }
-
- /*
- * Allow a plugin to editorialize on the set of Paths for this base
- * relation. It could add new paths (such as CustomPaths) by calling
- * add_path(), or delete or modify paths added by the core code.
- */
- if (set_rel_pathlist_hook)
- (*set_rel_pathlist_hook) (root, rel, rti, rte);
-
- /* Now find the cheapest of the paths for this rel */
- set_cheapest(rel);
-}
-
-/*
* stmt_beg callback is called when each query in PL/pgSQL function is about
* to be executed. At that timing, we save query string in the global variable
* plpgsql_query_string to use it in planner hook. It's safe to use one global
plpgsql_recurse_level = 0;
}
+
+/* include core static functions */
+static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
+ RelOptInfo *rel2, RelOptInfo *joinrel,
+ SpecialJoinInfo *sjinfo, List *restrictlist);
+static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
+ Index rti, RangeTblEntry *rte);
+
#define standard_join_search pg_hint_plan_standard_join_search
#define join_search_one_level pg_hint_plan_join_search_one_level
#define make_join_rel make_join_rel_wrapper
# pg_hint_plan extension
comment = ''
-default_version = '1.3.7'
+default_version = '1.4'
relocatable = false
schema = hint_plan
*/
#include "postgres.h"
-#include <sys/stat.h>
-
-#include "access/hash.h"
#include "parser/scanner.h"
-static void AppendJumble(pgssJumbleState *jstate,
- const unsigned char *item, Size size);
-static void JumbleQuery(pgssJumbleState *jstate, Query *query);
-static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable);
-static void JumbleExpr(pgssJumbleState *jstate, Node *node);
-static void RecordConstLocation(pgssJumbleState *jstate, int location);
-static char *generate_normalized_query(pgssJumbleState *jstate, const char *query,
- int query_loc, int *query_len_p, int encoding);
-static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query,
+static char *generate_normalized_query(JumbleState *jstate, const char *query,
+ int query_loc, int *query_len_p);
+static void fill_in_constant_lengths(JumbleState *jstate, const char *query,
int query_loc);
static int comp_location(const void *a, const void *b);
-/*
- * AppendJumble: Append a value that is substantive in a given query to
- * the current jumble.
- */
-static void
-AppendJumble(pgssJumbleState *jstate, const unsigned char *item, Size size)
-{
- unsigned char *jumble = jstate->jumble;
- Size jumble_len = jstate->jumble_len;
-
- /*
- * Whenever the jumble buffer is full, we hash the current contents and
- * reset the buffer to contain just that hash value, thus relying on the
- * hash to summarize everything so far.
- */
- while (size > 0)
- {
- Size part_size;
-
- if (jumble_len >= JUMBLE_SIZE)
- {
- uint64 start_hash;
-
- start_hash = DatumGetUInt64(hash_any_extended(jumble,
- JUMBLE_SIZE, 0));
- memcpy(jumble, &start_hash, sizeof(start_hash));
- jumble_len = sizeof(start_hash);
- }
- part_size = Min(size, JUMBLE_SIZE - jumble_len);
- memcpy(jumble + jumble_len, item, part_size);
- jumble_len += part_size;
- item += part_size;
- size -= part_size;
- }
- jstate->jumble_len = jumble_len;
-}
-
-/*
- * Wrappers around AppendJumble to encapsulate details of serialization
- * of individual local variable elements.
- */
-#define APP_JUMB(item) \
- AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
-#define APP_JUMB_STRING(str) \
- AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)
-
-/*
- * JumbleQuery: Selectively serialize the query tree, appending significant
- * data to the "query jumble" while ignoring nonsignificant data.
- *
- * Rule of thumb for what to include is that we should ignore anything not
- * semantically significant (such as alias names) as well as anything that can
- * be deduced from child nodes (else we'd just be double-hashing that piece
- * of information).
- */
-static void
-JumbleQuery(pgssJumbleState *jstate, Query *query)
-{
- Assert(IsA(query, Query));
- Assert(query->utilityStmt == NULL);
-
- APP_JUMB(query->commandType);
- /* resultRelation is usually predictable from commandType */
- JumbleExpr(jstate, (Node *) query->cteList);
- JumbleRangeTable(jstate, query->rtable);
- JumbleExpr(jstate, (Node *) query->jointree);
- JumbleExpr(jstate, (Node *) query->targetList);
- JumbleExpr(jstate, (Node *) query->onConflict);
- JumbleExpr(jstate, (Node *) query->returningList);
- JumbleExpr(jstate, (Node *) query->groupClause);
- JumbleExpr(jstate, (Node *) query->groupingSets);
- JumbleExpr(jstate, query->havingQual);
- JumbleExpr(jstate, (Node *) query->windowClause);
- JumbleExpr(jstate, (Node *) query->distinctClause);
- JumbleExpr(jstate, (Node *) query->sortClause);
- JumbleExpr(jstate, query->limitOffset);
- JumbleExpr(jstate, query->limitCount);
- /* we ignore rowMarks */
- JumbleExpr(jstate, query->setOperations);
-}
-
-/*
- * Jumble a range table
- */
-static void
-JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
-{
- ListCell *lc;
-
- foreach(lc, rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- APP_JUMB(rte->rtekind);
- switch (rte->rtekind)
- {
- case RTE_RELATION:
- APP_JUMB(rte->relid);
- JumbleExpr(jstate, (Node *) rte->tablesample);
- break;
- case RTE_SUBQUERY:
- JumbleQuery(jstate, rte->subquery);
- break;
- case RTE_JOIN:
- APP_JUMB(rte->jointype);
- break;
- case RTE_FUNCTION:
- JumbleExpr(jstate, (Node *) rte->functions);
- break;
- case RTE_TABLEFUNC:
- JumbleExpr(jstate, (Node *) rte->tablefunc);
- break;
- case RTE_VALUES:
- JumbleExpr(jstate, (Node *) rte->values_lists);
- break;
- case RTE_CTE:
-
- /*
- * Depending on the CTE name here isn't ideal, but it's the
- * only info we have to identify the referenced WITH item.
- */
- APP_JUMB_STRING(rte->ctename);
- APP_JUMB(rte->ctelevelsup);
- break;
- case RTE_NAMEDTUPLESTORE:
- APP_JUMB_STRING(rte->enrname);
- break;
- case RTE_RESULT:
- break;
- default:
- elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
- break;
- }
- }
-}
-
-/*
- * Jumble an expression tree
- *
- * In general this function should handle all the same node types that
- * expression_tree_walker() does, and therefore it's coded to be as parallel
- * to that function as possible. However, since we are only invoked on
- * queries immediately post-parse-analysis, we need not handle node types
- * that only appear in planning.
- *
- * Note: the reason we don't simply use expression_tree_walker() is that the
- * point of that function is to support tree walkers that don't care about
- * most tree node types, but here we care about all types. We should complain
- * about any unrecognized node type.
- */
-static void
-JumbleExpr(pgssJumbleState *jstate, Node *node)
-{
- ListCell *temp;
-
- if (node == NULL)
- return;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * We always emit the node's NodeTag, then any additional fields that are
- * considered significant, and then we recurse to any child nodes.
- */
- APP_JUMB(node->type);
-
- switch (nodeTag(node))
- {
- case T_Var:
- {
- Var *var = (Var *) node;
-
- APP_JUMB(var->varno);
- APP_JUMB(var->varattno);
- APP_JUMB(var->varlevelsup);
- }
- break;
- case T_Const:
- {
- Const *c = (Const *) node;
-
- /* We jumble only the constant's type, not its value */
- APP_JUMB(c->consttype);
- /* Also, record its parse location for query normalization */
- RecordConstLocation(jstate, c->location);
- }
- break;
- case T_Param:
- {
- Param *p = (Param *) node;
-
- APP_JUMB(p->paramkind);
- APP_JUMB(p->paramid);
- APP_JUMB(p->paramtype);
- /* Also, track the highest external Param id */
- if (p->paramkind == PARAM_EXTERN &&
- p->paramid > jstate->highest_extern_param_id)
- jstate->highest_extern_param_id = p->paramid;
- }
- break;
- case T_Aggref:
- {
- Aggref *expr = (Aggref *) node;
-
- APP_JUMB(expr->aggfnoid);
- JumbleExpr(jstate, (Node *) expr->aggdirectargs);
- JumbleExpr(jstate, (Node *) expr->args);
- JumbleExpr(jstate, (Node *) expr->aggorder);
- JumbleExpr(jstate, (Node *) expr->aggdistinct);
- JumbleExpr(jstate, (Node *) expr->aggfilter);
- }
- break;
- case T_GroupingFunc:
- {
- GroupingFunc *grpnode = (GroupingFunc *) node;
-
- JumbleExpr(jstate, (Node *) grpnode->refs);
- }
- break;
- case T_WindowFunc:
- {
- WindowFunc *expr = (WindowFunc *) node;
-
- APP_JUMB(expr->winfnoid);
- APP_JUMB(expr->winref);
- JumbleExpr(jstate, (Node *) expr->args);
- JumbleExpr(jstate, (Node *) expr->aggfilter);
- }
- break;
- case T_SubscriptingRef:
- {
- SubscriptingRef *sbsref = (SubscriptingRef *) node;
-
- JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
- JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
- JumbleExpr(jstate, (Node *) sbsref->refexpr);
- JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
- }
- break;
- case T_FuncExpr:
- {
- FuncExpr *expr = (FuncExpr *) node;
-
- APP_JUMB(expr->funcid);
- JumbleExpr(jstate, (Node *) expr->args);
- }
- break;
- case T_NamedArgExpr:
- {
- NamedArgExpr *nae = (NamedArgExpr *) node;
-
- APP_JUMB(nae->argnumber);
- JumbleExpr(jstate, (Node *) nae->arg);
- }
- break;
- case T_OpExpr:
- case T_DistinctExpr: /* struct-equivalent to OpExpr */
- case T_NullIfExpr: /* struct-equivalent to OpExpr */
- {
- OpExpr *expr = (OpExpr *) node;
-
- APP_JUMB(expr->opno);
- JumbleExpr(jstate, (Node *) expr->args);
- }
- break;
- case T_ScalarArrayOpExpr:
- {
- ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
-
- APP_JUMB(expr->opno);
- APP_JUMB(expr->useOr);
- JumbleExpr(jstate, (Node *) expr->args);
- }
- break;
- case T_BoolExpr:
- {
- BoolExpr *expr = (BoolExpr *) node;
-
- APP_JUMB(expr->boolop);
- JumbleExpr(jstate, (Node *) expr->args);
- }
- break;
- case T_SubLink:
- {
- SubLink *sublink = (SubLink *) node;
-
- APP_JUMB(sublink->subLinkType);
- APP_JUMB(sublink->subLinkId);
- JumbleExpr(jstate, (Node *) sublink->testexpr);
- JumbleQuery(jstate, castNode(Query, sublink->subselect));
- }
- break;
- case T_FieldSelect:
- {
- FieldSelect *fs = (FieldSelect *) node;
-
- APP_JUMB(fs->fieldnum);
- JumbleExpr(jstate, (Node *) fs->arg);
- }
- break;
- case T_FieldStore:
- {
- FieldStore *fstore = (FieldStore *) node;
-
- JumbleExpr(jstate, (Node *) fstore->arg);
- JumbleExpr(jstate, (Node *) fstore->newvals);
- }
- break;
- case T_RelabelType:
- {
- RelabelType *rt = (RelabelType *) node;
-
- APP_JUMB(rt->resulttype);
- JumbleExpr(jstate, (Node *) rt->arg);
- }
- break;
- case T_CoerceViaIO:
- {
- CoerceViaIO *cio = (CoerceViaIO *) node;
-
- APP_JUMB(cio->resulttype);
- JumbleExpr(jstate, (Node *) cio->arg);
- }
- break;
- case T_ArrayCoerceExpr:
- {
- ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node;
-
- APP_JUMB(acexpr->resulttype);
- JumbleExpr(jstate, (Node *) acexpr->arg);
- JumbleExpr(jstate, (Node *) acexpr->elemexpr);
- }
- break;
- case T_ConvertRowtypeExpr:
- {
- ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node;
-
- APP_JUMB(crexpr->resulttype);
- JumbleExpr(jstate, (Node *) crexpr->arg);
- }
- break;
- case T_CollateExpr:
- {
- CollateExpr *ce = (CollateExpr *) node;
-
- APP_JUMB(ce->collOid);
- JumbleExpr(jstate, (Node *) ce->arg);
- }
- break;
- case T_CaseExpr:
- {
- CaseExpr *caseexpr = (CaseExpr *) node;
-
- JumbleExpr(jstate, (Node *) caseexpr->arg);
- foreach(temp, caseexpr->args)
- {
- CaseWhen *when = lfirst_node(CaseWhen, temp);
-
- JumbleExpr(jstate, (Node *) when->expr);
- JumbleExpr(jstate, (Node *) when->result);
- }
- JumbleExpr(jstate, (Node *) caseexpr->defresult);
- }
- break;
- case T_CaseTestExpr:
- {
- CaseTestExpr *ct = (CaseTestExpr *) node;
-
- APP_JUMB(ct->typeId);
- }
- break;
- case T_ArrayExpr:
- JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements);
- break;
- case T_RowExpr:
- JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args);
- break;
- case T_RowCompareExpr:
- {
- RowCompareExpr *rcexpr = (RowCompareExpr *) node;
-
- APP_JUMB(rcexpr->rctype);
- JumbleExpr(jstate, (Node *) rcexpr->largs);
- JumbleExpr(jstate, (Node *) rcexpr->rargs);
- }
- break;
- case T_CoalesceExpr:
- JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args);
- break;
- case T_MinMaxExpr:
- {
- MinMaxExpr *mmexpr = (MinMaxExpr *) node;
-
- APP_JUMB(mmexpr->op);
- JumbleExpr(jstate, (Node *) mmexpr->args);
- }
- break;
- case T_SQLValueFunction:
- {
- SQLValueFunction *svf = (SQLValueFunction *) node;
-
- APP_JUMB(svf->op);
- /* type is fully determined by op */
- APP_JUMB(svf->typmod);
- }
- break;
- case T_XmlExpr:
- {
- XmlExpr *xexpr = (XmlExpr *) node;
-
- APP_JUMB(xexpr->op);
- JumbleExpr(jstate, (Node *) xexpr->named_args);
- JumbleExpr(jstate, (Node *) xexpr->args);
- }
- break;
- case T_NullTest:
- {
- NullTest *nt = (NullTest *) node;
-
- APP_JUMB(nt->nulltesttype);
- JumbleExpr(jstate, (Node *) nt->arg);
- }
- break;
- case T_BooleanTest:
- {
- BooleanTest *bt = (BooleanTest *) node;
- APP_JUMB(bt->booltesttype);
- JumbleExpr(jstate, (Node *) bt->arg);
- }
- break;
- case T_CoerceToDomain:
- {
- CoerceToDomain *cd = (CoerceToDomain *) node;
-
- APP_JUMB(cd->resulttype);
- JumbleExpr(jstate, (Node *) cd->arg);
- }
- break;
- case T_CoerceToDomainValue:
- {
- CoerceToDomainValue *cdv = (CoerceToDomainValue *) node;
-
- APP_JUMB(cdv->typeId);
- }
- break;
- case T_SetToDefault:
- {
- SetToDefault *sd = (SetToDefault *) node;
-
- APP_JUMB(sd->typeId);
- }
- break;
- case T_CurrentOfExpr:
- {
- CurrentOfExpr *ce = (CurrentOfExpr *) node;
-
- APP_JUMB(ce->cvarno);
- if (ce->cursor_name)
- APP_JUMB_STRING(ce->cursor_name);
- APP_JUMB(ce->cursor_param);
- }
- break;
- case T_NextValueExpr:
- {
- NextValueExpr *nve = (NextValueExpr *) node;
-
- APP_JUMB(nve->seqid);
- APP_JUMB(nve->typeId);
- }
- break;
- case T_InferenceElem:
- {
- InferenceElem *ie = (InferenceElem *) node;
-
- APP_JUMB(ie->infercollid);
- APP_JUMB(ie->inferopclass);
- JumbleExpr(jstate, ie->expr);
- }
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- APP_JUMB(tle->resno);
- APP_JUMB(tle->ressortgroupref);
- JumbleExpr(jstate, (Node *) tle->expr);
- }
- break;
- case T_RangeTblRef:
- {
- RangeTblRef *rtr = (RangeTblRef *) node;
-
- APP_JUMB(rtr->rtindex);
- }
- break;
- case T_JoinExpr:
- {
- JoinExpr *join = (JoinExpr *) node;
-
- APP_JUMB(join->jointype);
- APP_JUMB(join->isNatural);
- APP_JUMB(join->rtindex);
- JumbleExpr(jstate, join->larg);
- JumbleExpr(jstate, join->rarg);
- JumbleExpr(jstate, join->quals);
- }
- break;
- case T_FromExpr:
- {
- FromExpr *from = (FromExpr *) node;
-
- JumbleExpr(jstate, (Node *) from->fromlist);
- JumbleExpr(jstate, from->quals);
- }
- break;
- case T_OnConflictExpr:
- {
- OnConflictExpr *conf = (OnConflictExpr *) node;
-
- APP_JUMB(conf->action);
- JumbleExpr(jstate, (Node *) conf->arbiterElems);
- JumbleExpr(jstate, conf->arbiterWhere);
- JumbleExpr(jstate, (Node *) conf->onConflictSet);
- JumbleExpr(jstate, conf->onConflictWhere);
- APP_JUMB(conf->constraint);
- APP_JUMB(conf->exclRelIndex);
- JumbleExpr(jstate, (Node *) conf->exclRelTlist);
- }
- break;
- case T_List:
- foreach(temp, (List *) node)
- {
- JumbleExpr(jstate, (Node *) lfirst(temp));
- }
- break;
- case T_IntList:
- foreach(temp, (List *) node)
- {
- APP_JUMB(lfirst_int(temp));
- }
- break;
- case T_SortGroupClause:
- {
- SortGroupClause *sgc = (SortGroupClause *) node;
-
- APP_JUMB(sgc->tleSortGroupRef);
- APP_JUMB(sgc->eqop);
- APP_JUMB(sgc->sortop);
- APP_JUMB(sgc->nulls_first);
- }
- break;
- case T_GroupingSet:
- {
- GroupingSet *gsnode = (GroupingSet *) node;
-
- JumbleExpr(jstate, (Node *) gsnode->content);
- }
- break;
- case T_WindowClause:
- {
- WindowClause *wc = (WindowClause *) node;
-
- APP_JUMB(wc->winref);
- APP_JUMB(wc->frameOptions);
- JumbleExpr(jstate, (Node *) wc->partitionClause);
- JumbleExpr(jstate, (Node *) wc->orderClause);
- JumbleExpr(jstate, wc->startOffset);
- JumbleExpr(jstate, wc->endOffset);
- }
- break;
- case T_CommonTableExpr:
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
-
- /* we store the string name because RTE_CTE RTEs need it */
- APP_JUMB_STRING(cte->ctename);
- APP_JUMB(cte->ctematerialized);
- JumbleQuery(jstate, castNode(Query, cte->ctequery));
- }
- break;
- case T_SetOperationStmt:
- {
- SetOperationStmt *setop = (SetOperationStmt *) node;
-
- APP_JUMB(setop->op);
- APP_JUMB(setop->all);
- JumbleExpr(jstate, setop->larg);
- JumbleExpr(jstate, setop->rarg);
- }
- break;
- case T_RangeTblFunction:
- {
- RangeTblFunction *rtfunc = (RangeTblFunction *) node;
-
- JumbleExpr(jstate, rtfunc->funcexpr);
- }
- break;
- case T_TableFunc:
- {
- TableFunc *tablefunc = (TableFunc *) node;
-
- JumbleExpr(jstate, tablefunc->docexpr);
- JumbleExpr(jstate, tablefunc->rowexpr);
- JumbleExpr(jstate, (Node *) tablefunc->colexprs);
- }
- break;
- case T_TableSampleClause:
- {
- TableSampleClause *tsc = (TableSampleClause *) node;
-
- APP_JUMB(tsc->tsmhandler);
- JumbleExpr(jstate, (Node *) tsc->args);
- JumbleExpr(jstate, (Node *) tsc->repeatable);
- }
- break;
- default:
- /* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(node));
- break;
- }
-}
-
-/*
- * Record location of constant within query string of query tree
- * that is currently being walked.
- */
-static void
-RecordConstLocation(pgssJumbleState *jstate, int location)
-{
- /* -1 indicates unknown or undefined location */
- if (location >= 0)
- {
- /* enlarge array if needed */
- if (jstate->clocations_count >= jstate->clocations_buf_size)
- {
- jstate->clocations_buf_size *= 2;
- jstate->clocations = (pgssLocationLen *)
- repalloc(jstate->clocations,
- jstate->clocations_buf_size *
- sizeof(pgssLocationLen));
- }
- jstate->clocations[jstate->clocations_count].location = location;
- /* initialize lengths to -1 to simplify fill_in_constant_lengths */
- jstate->clocations[jstate->clocations_count].length = -1;
- jstate->clocations_count++;
- }
-}
/*
* Generate a normalized version of the query string that will be used to
* Returns a palloc'd string.
*/
static char *
-generate_normalized_query(pgssJumbleState *jstate, const char *query,
- int query_loc, int *query_len_p, int encoding)
+generate_normalized_query(JumbleState *jstate, const char *query,
+ int query_loc, int *query_len_p)
{
char *norm_query;
int query_len = *query_len_p;
memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
n_quer_loc += len_to_wrt;
- /*
- * PG_HINT_PLAN: DON'T TAKE IN a6f22e8356 so that the designed behavior
- * is kept stable.
- */
- /* And insert a '?' in place of the constant token */
- norm_query[n_quer_loc++] = '?';
+ /* And insert a param symbol in place of the constant token */
+
+ /* !!! START: HERE IS THE PART WHICH IS MODIFIED FOR PG_HINT_PLAN !!! */
+ n_quer_loc += sprintf(norm_query + n_quer_loc, "?");
+ /* !!! END: HERE IS THE PART WHICH IS MODIFIED FOR PG_HINT_PLAN !!! */
quer_loc = off + tok_len;
last_off = off;
* reason for a constant to start with a '-'.
*/
static void
-fill_in_constant_lengths(pgssJumbleState *jstate, const char *query,
+fill_in_constant_lengths(JumbleState *jstate, const char *query,
int query_loc)
{
- pgssLocationLen *locs;
+ LocationLen *locs;
core_yyscan_t yyscanner;
core_yy_extra_type yyextra;
core_YYSTYPE yylval;
*/
if (jstate->clocations_count > 1)
qsort(jstate->clocations, jstate->clocations_count,
- sizeof(pgssLocationLen), comp_location);
+ sizeof(LocationLen), comp_location);
locs = jstate->clocations;
/* initialize the flex scanner --- should match raw_parser() */
}
/*
- * comp_location: comparator for qsorting pgssLocationLen structs by location
+ * comp_location: comparator for qsorting LocationLen structs by location
*/
static int
comp_location(const void *a, const void *b)
{
- int l = ((const pgssLocationLen *) a)->location;
- int r = ((const pgssLocationLen *) b)->location;
+ int l = ((const LocationLen *) a)->location;
+ int r = ((const LocationLen *) b)->location;
if (l < r)
return -1;
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
);
--- ambigous error
+-- ambiguous error
EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
-/*+NestLoop(t1 t2)*/
+/*+MergeJoin(t1 t2)*/
EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;
/*+Leading(t1 t2 t1)*/
EXPLAIN (COSTS false) SELECT * FROM t1, s0.t1, t2 WHERE public.t1.id = s0.t1.id AND public.t1.id = t2.id;