All Products
Search
Document Center

PolarDB:pg_hint_plan

Last Updated:Apr 01, 2026

pg_hint_plan lets you embed execution plan hints directly in SQL comments, overriding PolarDB's cost-based optimizer on a per-query basis without affecting the rest of the session.

/*+ SeqScan(a) HashJoin(a b) */
SELECT * FROM orders a JOIN customers b ON a.customer_id = b.id;

How pg_hint_plan works

PolarDB's optimizer selects execution plans based on data statistics, not static rules. It evaluates all candidate plans and picks the one with the lowest estimated cost. While this works well in most cases, the optimizer can miss non-obvious data relationships and choose a suboptimal plan.

Grand Unified Scheme (GUC) parameters let you tune the optimizer, but changes apply to the entire session. Use pg_hint_plan when you need to tune a single query without session-wide side effects.

Supported versions

pg_hint_plan is available on:

  • PolarDB for PostgreSQL 16 (revision version 2.0.16.9.6.0 or later)

  • PolarDB for PostgreSQL 14 (no revision version requirement)

  • PolarDB for PostgreSQL 11 (no revision version requirement)

To check your cluster version, run SHOW polardb_version; in the database or view it in the PolarDB console. If the revision version does not meet the requirement, update it.

Prerequisites

Before you begin, ensure that you have:

  • A PolarDB for PostgreSQL cluster running a supported version

  • Database access that is not through Data Management (DMS) — DMS does not support hints

Install and load pg_hint_plan

Step 1: Create the extension

CREATE EXTENSION pg_hint_plan;

Step 2: Load the extension

Choose one of the following loading methods based on your scope:

For a single user:

ALTER USER <username> SET session_preload_libraries = 'pg_hint_plan';

For a single database:

ALTER DATABASE <database_name> SET session_preload_libraries = 'pg_hint_plan';
If a misconfiguration prevents login, connect through another account or database and run:
ALTER USER <username> RESET session_preload_libraries;
ALTER DATABASE <database_name> RESET session_preload_libraries;

For a database cluster:

Go to the Quota Center, find the quota item named PolarDB PG pg_hint_plan use, and click Apply in the Actions column.

Step 3: Verify the extension is loaded

  1. Enable debug output:

    SET pg_hint_plan.debug_print TO on;
    SET pg_hint_plan.message_level TO notice;
  2. Run a test query with a hint:

    /*+Set(enable_seqscan 1)*/SELECT 1;

    If the extension is loaded, the following output appears:

    NOTICE:  pg_hint_plan: used hint: Set(enable_seqscan 1)
  3. Reset debug settings:

    RESET pg_hint_plan.debug_print;
    RESET pg_hint_plan.message_level;

Hint syntax

A hint block starts with /*+ and ends with */. Each hint consists of a hint name followed by parameters in parentheses, separated by spaces. Place multiple hints on separate lines for readability.

/*+
   HashJoin(a b)
   SeqScan(a)
 */
EXPLAIN SELECT *
   FROM pgbench_branches b
   JOIN pgbench_accounts a ON b.bid = a.bid
   ORDER BY a.aid;

Result:

QUERY PLAN
---------------------------------------------------------------------------------------
 Sort  (cost=31465.84..31715.84 rows=100000 width=197)
   Sort Key: a.aid
   ->  Hash Join  (cost=1.02..4016.02 rows=100000 width=197)
         Hash Cond: (a.bid = b.bid)
         ->  Seq Scan on pgbench_accounts a  (cost=0.00..2640.00 rows=100000 width=97)
         ->  Hash  (cost=1.01..1.01 rows=1 width=100)
               ->  Seq Scan on pgbench_branches b  (cost=0.00..1.01 rows=1 width=100)
(7 rows)

Behavior rules

  • pg_hint_plan reads hints from the first comment block only. Hints in subsequent comment blocks are ignored.

  • Accepted characters in hints: letters, digits, spaces, and _, ,, (, ). Any other character stops parsing immediately.

  • Object name matching is case-sensitive, unlike standard PostgreSQL behavior.

Example — case sensitivity:

If your database has a table named tbl, the following hint does not match it because TBL and tbl are treated as different names:

/*+ SeqScan(TBL) */
SELECT * FROM tbl;

Use the exact name or alias as it appears in the query:

/*+ SeqScan(tbl) */
SELECT * FROM tbl;

Limitations in PL/pgSQL stored procedures

When using pg_hint_plan inside PL/pgSQL stored procedures, hints take effect only in these statement types:

  • SELECT, INSERT, UPDATE, and DELETE

  • RETURN QUERY

  • EXECUTE QUERY

  • OPEN

  • FOR

A hint must be placed immediately after the first word of the SQL statement. If placed before the first word, it is not recognized as part of the query.

Hint types

pg_hint_plan supports six hint types.

Hints for scan methods

Specify the scan method for a table. If the table has an alias, refer to it by the alias.

Effective on: ordinary tables, inherited tables, unlogged tables, temporary tables, system tables

Not effective on: external tables, table functions, constant-value expressions, universal expressions, views, subqueries

/*+
    SeqScan(t1)
    IndexScan(t2 t2_pkey)
 */
SELECT * FROM table1 t1 JOIN table2 t2 ON (t1.key = t2.key);

Hints for join methods

Specify the join method for two or more tables.

Effective on: ordinary tables, inherited tables, unlogged tables, temporary tables, external tables, system tables, table functions, constant-value expressions, universal expressions

Not effective on: views and subqueries

Hints for join order

Specify the order in which tables are joined. Two approaches are available:

  • Specify join order without constraining direction at each level.

  • Specify join order and direction at each level.

/*+
    NestLoop(t1 t2)
    MergeJoin(t1 t2 t3)
    Leading(t1 t2 t3)
  */
SELECT * FROM table1 t1
    JOIN table2 t2 ON (t1.key = t2.key)
    JOIN table3 t3 ON (t2.key = t3.key);

In this example:

  • NestLoop(t1 t2) — join t1 and t2 using nested loop join

  • MergeJoin(t1 t2 t3) — join t1, t2, and t3 using merge join

  • Leading(t1 t2 t3) — set the join order to t1 → t2 → t3

Hints for row number correction

Correct row count estimates that the optimizer calculates incorrectly. Four operators are supported:

/*+ Rows(a b #10) */ SELECT ...;   -- Set join result row count to 10
/*+ Rows(a b +10) */ SELECT ...;   -- Increase row count by 10
/*+ Rows(a b -10) */ SELECT ...;   -- Decrease row count by 10
/*+ Rows(a b *10) */ SELECT ...;   -- Multiply row count by 10

Hints for parallel execution

Specify the parallel execution plan for a query.

Effective on: ordinary tables, inherited tables, unlogged tables, system tables

Not effective on: external tables, constant-value clauses, universal expressions, views, subqueries

Use the table's real name or alias to specify internal tables within a view.

Example 1: Set the degree of parallelism (DOP) to 3 for c1 and 5 for c2:

EXPLAIN /*+ Parallel(c1 3 hard) Parallel(c2 5 hard) */
       SELECT c2.a FROM c1 JOIN c2 ON (c1.a = c2.a);

Result:

QUERY PLAN
-------------------------------------------------------------------------------
 Hash Join  (cost=2.86..11406.38 rows=101 width=4)
   Hash Cond: (c1.a = c2.a)
   ->  Gather  (cost=0.00..7652.13 rows=1000101 width=4)
         Workers Planned: 3
         ->  Parallel Seq Scan on c1  (cost=0.00..7652.13 rows=322613 width=4)
   ->  Hash  (cost=1.59..1.59 rows=101 width=4)
         ->  Gather  (cost=0.00..1.59 rows=101 width=4)
               Workers Planned: 5
               ->  Parallel Seq Scan on c2  (cost=0.00..1.59 rows=59 width=4)

Example 2: Set the DOP for tl to 5:

EXPLAIN /*+ Parallel(tl 5 hard) */ SELECT sum(a) FROM tl;

Result:

QUERY PLAN
-----------------------------------------------------------------------------------
 Finalize Aggregate  (cost=693.02..693.03 rows=1 width=8)
   ->  Gather  (cost=693.00..693.01 rows=5 width=8)
         Workers Planned: 5
         ->  Partial Aggregate  (cost=693.00..693.01 rows=1 width=8)
               ->  Parallel Seq Scan on tl  (cost=0.00..643.00 rows=20000 width=4)

Hints for GUC parameter setting

Temporarily change a GUC parameter value for the duration of query planning. Unlike session-level GUC changes, these hints affect only the execution plan generation for the targeted query. If multiple hints set the same GUC parameter, the last one takes effect.

/*+ Set(random_page_cost 2.0) */
SELECT * FROM table1 t1 WHERE key = 'value';

Hint syntax reference

All supported hints are listed below. Optional parameters are enclosed in brackets ([ ]).

Type Syntax Description
Scan methods SeqScan(table) Forces a sequential scan
TidScan(table) Forces a TID scan
IndexScan(table [index...]) Forces an index scan; optionally specify an index
IndexOnlyScan(table [index...]) Forces an index-only scan; optionally specify an index
BitmapScan(table [index...]) Forces a bitmap scan; optionally specify an index
NoSeqScan(table) Prohibits a sequential scan
NoTidScan(table) Prohibits a TID scan
NoIndexScan(table) Prohibits an index scan
NoIndexOnlyScan(table) Prohibits an index-only scan
NoBitmapScan(table) Prohibits a bitmap scan
Join methods NestLoop(table table [table...]) Forces a nested loop join
HashJoin(table table [table...]) Forces a hash join
MergeJoin(table table [table...]) Forces a merge join
NoNestLoop(table table [table...]) Prohibits a nested loop join
NoHashJoin(table table [table...]) Prohibits a hash join
NoMergeJoin(table table [table...]) Prohibits a merge join
Join order Leading(table table [table...]) Specifies the join order
Leading(<join pair>) Specifies the join order and direction at each level
Row number correction Rows(table table [table...] correction) Corrects the estimated row count of a join result; operators: #<n>, +<n>, -<n>, *<n>
Parallel execution Parallel(table <# of workers> [soft|hard]) Sets or prohibits parallel execution for the specified table; 0 workers prohibits parallelism; soft (default) adjusts only max_parallel_workers_per_gather; hard adjusts all related parameters
PX(<# of workers>) Specifies cross-node parallel execution; <# of workers> sets the DOP
NoPX() Prohibits cross-node parallel execution
GUC parameter setting Set(GUC-param value) Sets a GUC parameter value during query planning
During cross-node parallel execution, the Rows(...) hint is not supported. Join method hints can target only two tables at a time, and join order hints must specify all tables.