Skip to content

refactor: first pass at adding pg commands from pg-extras plugin to cli PR 1 of 3#3770

Merged
jdodson merged 8 commits into
feature/migrate_pg_extras_to_clifrom
jbd_migrate_commands1
Jun 17, 2026
Merged

refactor: first pass at adding pg commands from pg-extras plugin to cli PR 1 of 3#3770
jdodson merged 8 commits into
feature/migrate_pg_extras_to_clifrom
jbd_migrate_commands1

Conversation

@jdodson

@jdodson jdodson commented Jun 15, 2026

Copy link
Copy Markdown

Summary

We need to move the commands from the pg-extras plugin to the core CLI, which will require us to update the commands to use oclif/core v4 and heroku-cli-command v12.

This is PR 1 of 3 to accomplish this task and we will be merging these PRs in feature/migrate_pg_extras_to_cli

Things done:

  • Add postgres words to cspell dictionary

This work item covers migrating the following 6 pg-extras commands to the core CLI:

  • pg/cache-hit.ts
  • pg/calls.ts
  • pg/extensions.ts
  • pg/fdwsql.ts
  • pg/index-size.ts
  • pg/index-usage.ts

ALSO did:

  • AGENTS.md requires usage examples to the migrated pg-extras commands so I added those
  • Fixed a bug in pg:call so that it now works 😄

What I'm not doing and why

  • There is some code overlap with the commands we are moving into CLI. Whereas we could refactor it all, it seems like a fair amount of work and I'd rather keep this series of PR's small and more time constrained.

Type of Change

Breaking Changes (major semver update)

  • Add a ! after your change type to denote a change that breaks current behavior

Feature Additions (minor semver update)

  • feat: Introduces a new feature to the codebase

Patch Updates (patch semver update)

  • fix: Bug fix
  • deps: Dependency upgrade
  • revert: Revert a previous commit
  • chore: Change that does not affect production code
  • refactor: Refactoring existing code without changing behavior
  • test: Add/update/remove tests

Testing

Notes:

The --help changes (should be limited to --prompt and GLOBAL FLAGS additions that came with cli upgrades) ALSO examples as the cli requires:

diff <(./bin/run pg:cache-hit --help) <(heroku pg:cache-hit --help)
diff <(./bin/run pg:calls --help) <(heroku pg:calls --help)
diff <(./bin/run pg:extensions --help) <(heroku pg:extensions --help)
diff <(./bin/run pg:fdwsql --help) <(heroku pg:fdwsql --help)
diff <(./bin/run pg:index-size --help) <(heroku pg:index-size --help)
diff <(./bin/run pg:index-usage --help) <(heroku pg:index-usage --help)

New Command Testing Steps:

  1. Show index and table hit rate (two rows: index hit rate and table hit rate, each with a
    ratio)

./bin/run pg:cache-hit -a $APP

  1. Confirm the hidden underscore alias produces identical output to step 1

./bin/run pg:cache_hit -a $APP

  1. Show index usage (relname | percent_of_times_index_used | rows_in_table, ordered by rows
    descending; tables with no scans show Insufficient data)

./bin/run pg:index-usage -a $APP

  1. Confirm the hidden underscore alias produces identical output to step 3

./bin/run pg:index_usage -a $APP

  1. Show index sizes (name | size for each index, descending by size, e.g. 5196 MB)

./bin/run pg:index-size -a $APP

  1. Confirm the hidden underscore alias produces identical output to step 5

./bin/run pg:index_size -a $APP

  1. List available and installed extensions (name | version | schema | description, e.g. a
    plpgsql row)

./bin/run pg:extensions -a $APP

  1. Show the 10 highest-frequency queries (total_exec_time | prop_exec_time | ncalls | sync_io_time | query, ordered by calls DESC; requires pg_stat_statements)

./bin/run pg:calls -a $APP

  1. Same as step 8 but with the query column truncated to 40 characters (look for … at the cut point)

./bin/run pg:calls --truncate -a $APP

  1. Generate foreign data wrapper SQL (output starts with CREATE EXTENSION IF NOT EXISTS postgres_fdw;, DROP SERVER IF EXISTS example_prefix_db;, CREATE SERVER example_prefix_db…, CREATE USER MAPPING…, then one CREATE FOREIGN TABLE… line per table/view; psql noise like (N rows) is filtered out)

./bin/run pg:fdwsql example_prefix -a $APP

  1. Confirm the optional database arg resolves a specific database instead of the default

./bin/run pg:cache-hit DATABASE_URL -a $APP
./bin/run pg:fdwsql example_prefix DATABASE_URL -a $APP

Screenshots (if applicable)

Related Issues

GUS work item: https://gus.lightning.force.com/lightning/r/ADM_Work__c/a07EE00002Yzk1lYAB/view

Add postgres words to cspell dictionary
@jdodson jdodson requested a review from a team as a code owner June 15, 2026 17:55
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 17:55 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 17:55 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 17:55 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 17:55 — with GitHub Actions Inactive
@jdodson jdodson changed the title First pass at adding pg commands from pg-extras plugin to cli refactor: First pass at adding pg commands from pg-extras plugin to cli PR 1 of 3 Jun 15, 2026
@jdodson jdodson changed the title refactor: First pass at adding pg commands from pg-extras plugin to cli PR 1 of 3 refactor: first pass at adding pg commands from pg-extras plugin to cli PR 1 of 3 Jun 15, 2026
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 20:04 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 20:04 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 20:04 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 20:04 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 20:35 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 20:35 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 20:35 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 20:35 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 22:29 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 22:29 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 22:29 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 15, 2026 22:29 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 16, 2026 18:02 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 16, 2026 18:02 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 16, 2026 18:02 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 16, 2026 18:02 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 16, 2026 18:12 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 16, 2026 18:12 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 16, 2026 18:12 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 16, 2026 18:12 — with GitHub Actions Inactive

@eablack eablack left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migration looks faithful to heroku-pg-extras — SQL bodies for all six commands match verbatim, and the pg_stat_statements schema check correctly expanded from 'public' to ('public', 'heroku_ext') to match pg/outliers.ts. Approving, with three notes worth addressing here or in a quick follow-up.

1. ux.error is swallowed in src/lib/pg/extras.ts (regression hidden by tests)

The three helpers wrap ux.error(..., {exit: 1}) in try/catch. ux.error throws an oclif ExitError, which the catch then catches and replaces with a generic message. Net effect: a user whose DB is missing pg_stat_statements never sees the install instructions — they see "Failed to check pg_stat_statements extension availability". The pg:calls test at test/unit/commands/pg/calls.unit.test.ts even encodes this:

// ensurePGStatStatement raises the "need to be installed" error inside its own
// try/catch, which then re-raises the generic availability-check failure.
execQueryStub.onCall(0).resolves('f')
// ...
expect(error?.message).to.contain('Failed to check pg_stat_statements extension availability')

The simplest fix is to drop the try/catch entirely (this matches pg/outliers.ts, which lets the error propagate with the right message). Same applies to newTotalExecTimeField and newBlkTimeFields. Tests asserting the generic string would need to be updated to assert the real one — which is the user-visible improvement.

2. database arg description drifts from existing pg commands

The new commands hardcode description: 'database name'. Existing commands in this directory use the localized form:

// src/commands/pg/bloat.ts, src/commands/pg/outliers.ts
database: Args.string({
  description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`,
}),

Worth aligning so --help output stays consistent across pg:*.

3. SQL/identifier injection on args.prefix in pg:fdwsql

Pre-existing in heroku-pg-extras, but worth fixing while we're here. The user-supplied prefix is interpolated into SQL outside any quote_ident:

ux.stdout(`DROP SERVER IF EXISTS ${args.prefix}_db;`)
ux.stdout(`CREATE SERVER ${args.prefix}_db ...`)
// and inside generateFdwsqlQuery:
//   || ' SERVER ${prefix}_db OPTIONS'

A prefix containing ', ;, or whitespace produces malformed (or hostile) SQL. Risk is low because the user is operating on their own DB and pasting the output themselves, but a simple identifier-shape validation would close it: e.g. if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(args.prefix)) ux.error(...).

@jdodson jdodson temporarily deployed to AcceptanceTests June 17, 2026 16:59 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 17, 2026 16:59 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 17, 2026 16:59 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 17, 2026 16:59 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 17, 2026 18:00 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 17, 2026 18:00 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 17, 2026 18:00 — with GitHub Actions Inactive
@jdodson jdodson temporarily deployed to AcceptanceTests June 17, 2026 18:00 — with GitHub Actions Inactive
@jdodson

jdodson commented Jun 17, 2026

Copy link
Copy Markdown
Author

Thank you @eablack all notes addressed.

@jdodson jdodson merged commit 9b1dad0 into feature/migrate_pg_extras_to_cli Jun 17, 2026
18 checks passed
@jdodson jdodson deleted the jbd_migrate_commands1 branch June 17, 2026 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants