All Products
Search
Document Center

PolarDB:hypopg

Last Updated:Mar 28, 2026

hypopg is a PostgreSQL extension for creating hypothetical indexes — virtual indexes that have no resource cost (CPU, disk, or memory) and are never written to disk. Use it to find out whether a specific index would improve a slow query without waiting for the index to build or consuming server resources.

Prerequisites

Before you begin, ensure that you have:

  • A PolarDB for PostgreSQL (Compatible with Oracle) cluster running one of the following engine versions:

    • PolarDB for PostgreSQL (Compatible with Oracle) 2.0, revision version 2.0.14.1.0 or later

    • PolarDB for PostgreSQL (Compatible with Oracle) 1.0, revision version 1.1.28 or later

  • Identified the queries you want to optimize and the index types to test

To check the revision version of your cluster, run:

SHOW polar_version;

How it works

A hypothetical index is stored in the private memory of your current connection — not in any system table or physical file. Because it does not exist on disk, it works only with EXPLAIN (without the ANALYZE option). Running EXPLAIN ANALYZE executes the query against real data, so the database ignores hypothetical indexes and uses a sequential scan instead.

Supported index types:

Index typeAccess methodPrerequisite
B-tree indexbtreeNone
Block range index (BRIN)brinNone
Hash indexhashNone
Bloom indexbloomInstall the bloom extension first

Install the extension

  1. Install hypopg:

    CREATE EXTENSION hypopg;
  2. Verify the installation:

    \dx hypopg

    Expected output:

    List of installed extensions
      Name  | Version | Schema |             Description
    --------+---------+--------+-------------------------------------
     hypopg | 1.3.1   | public | Hypothetical indexes for PostgreSQL
    (1 row)

    Alternatively, query the pg_extension catalog:

    SELECT * FROM pg_extension WHERE extname = 'hypopg';

    Expected output:

     extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition
    ---------+----------+--------------+----------------+------------+-----------+--------------
     hypopg  |       10 |         2200 | t              | 1.3.1      |           |
    (1 row)

Configure parameters

ParameterDefaultDescription
hypopg.enabledonEnables or disables hypopg. When set to off, the database ignores hypothetical indexes but does not delete them.
hypopg.use_real_oidsoffControls how object identifiers (OIDs) are assigned to hypothetical indexes. See OID assignment for details.

OID assignment

By default (hypopg.use_real_oids = off), hypopg borrows OIDs from a reserved range that PostgreSQL sets aside for future use. This range is computed dynamically the first time a connection uses hypopg, and it works on a secondary server. The drawback is a limit of approximately 2,500 hypothetical indexes per connection. Once that limit is reached, creating a new hypothetical index becomes very slow. Call hypopg_reset() to clear all hypothetical indexes and reset the counter.

Set hypopg.use_real_oids = on if you need more than 2,500 hypothetical indexes at the same time. With real OIDs, there is no practical index limit, but hypopg requires more lock resources and cannot be used on a secondary server. Changing this parameter does not reset existing hypothetical indexes — real OIDs and borrowed OIDs can coexist.

Test a hypothetical index

Because hypothetical indexes exist only in the current connection's memory, run the hypopg_create_index() call and the EXPLAIN statement in the same session.

The following example walks through the full workflow on a sample table.

  1. Create a table and populate it:

    CREATE TABLE hypo (id integer, val text);
    INSERT INTO hypo SELECT i, 'line ' || i FROM generate_series(1, 100000) i;
    VACUUM ANALYZE hypo;
  2. Check the baseline query plan. Without any index, the planner uses a sequential scan:

    EXPLAIN SELECT val FROM hypo WHERE id = 1;

    Output:

    QUERY PLAN
    --------------------------------------------------------
     Seq Scan on hypo  (cost=0.00..1791.00 rows=1 width=10)
       Filter: (id = 1)
    (2 rows)
  3. Create a hypothetical index on the id column:

    SELECT * FROM hypopg_create_index('CREATE INDEX ON hypo (id)');

    Output:

     indexrelid |      indexname
    ------------+----------------------
          13925 | <13925>btree_hypo_id
    (1 row)

    hypopg_create_index() accepts any standard CREATE INDEX statement and creates a hypothetical index for each one. The OID shown (13925 in this example) is dynamically generated.

  4. Run EXPLAIN again to check whether the planner would use the hypothetical index:

    EXPLAIN SELECT val FROM hypo WHERE id = 1;

    Output:

    QUERY PLAN
    ------------------------------------------------------------------------------------
     Index Scan using "<13925>btree_hypo_id" on hypo  (cost=0.04..8.06 rows=1 width=10)
       Index Cond: (id = 1)
    (2 rows)

    The planner switched from a sequential scan to an index scan, confirming the hypothetical index would help.

  5. Confirm that EXPLAIN ANALYZE ignores the hypothetical index (as expected):

    EXPLAIN ANALYZE SELECT val FROM hypo WHERE id = 1;

    Output:

    QUERY PLAN
    ---------------------------------------------------------------------------------------------------
     Seq Scan on hypo  (cost=0.00..1791.00 rows=1 width=10) (actual time=0.030..15.439 rows=1 loops=1)
       Filter: (id = 1)
       Rows Removed by Filter: 99999
     Planning Time: 0.066 ms
     Execution Time: 15.492 ms
    (5 rows)

    EXPLAIN ANALYZE executes the query for real, so the hypothetical index is not used.

Manage hypothetical indexes

A quick reference of all hypopg functions and views:

Function / viewDescription
hypopg_create_index(text)Creates a hypothetical index from a CREATE INDEX statement
hypopg_list_indexesView that lists all hypothetical indexes in the current session
hypopg()Lists all hypothetical indexes in the same format as pg_index
hypopg_get_indexdef(oid)Returns the CREATE INDEX statement for a hypothetical index
hypopg_relation_size(oid)Estimates the size of a hypothetical index
hypopg_drop_index(oid)Deletes the hypothetical index with the specified OID
hypopg_reset()Deletes all hypothetical indexes in the current session

List hypothetical indexes

Use the hypopg_list_indexes view:

SELECT * FROM hypopg_list_indexes;

Output:

 indexrelid |      index_name      | schema_name | table_name | am_name
------------+----------------------+-------------+------------+---------
      13925 | <13925>btree_hypo_id | public      | hypo       | btree
(1 row)

Or use the hypopg() function to get the same fields as pg_index:

SELECT * FROM hypopg();

Output:

      indexname       | indexrelid | indrelid | innatts | indisunique | indkey | indcollation | indclass | indoption | indexprs | indpred | amid
----------------------+------------+----------+---------+-------------+--------+--------------+----------+-----------+----------+---------+------
 <13925>btree_hypo_id |      13925 |    16450 |       1 | f           | 1      | 0            | 1978     |           |          |         |  403
(1 row)

Get the index definition

SELECT index_name, hypopg_get_indexdef(indexrelid) FROM hypopg_list_indexes;

Output:

      index_name      |             hypopg_get_indexdef
----------------------+----------------------------------------------
 <13925>btree_hypo_id | CREATE INDEX ON public.hypo USING btree (id)
(1 row)

Estimate the index size

SELECT index_name, pg_size_pretty(hypopg_relation_size(indexrelid))
FROM hypopg_list_indexes;

Output:

      index_name      | pg_size_pretty
----------------------+----------------
 <13925>btree_hypo_id | 2544 kB
(1 row)

Delete a hypothetical index

Delete a specific index by OID:

SELECT hypopg_drop_index(13925);

Output:

 hypopg_drop_index
-------------------
 t
(1 row)

Delete all hypothetical indexes in the current session:

SELECT hypopg_reset();

Remove the extension

DROP EXTENSION hypopg;