heroku 11.6.0 → 11.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/commands/auth/whoami.js +1 -1
  3. package/dist/commands/pg/cache-hit.d.ts +16 -0
  4. package/dist/commands/pg/cache-hit.js +40 -0
  5. package/dist/commands/pg/calls.d.ts +19 -0
  6. package/dist/commands/pg/calls.js +55 -0
  7. package/dist/commands/pg/extensions.d.ts +16 -0
  8. package/dist/commands/pg/extensions.js +39 -0
  9. package/dist/commands/pg/fdwsql.d.ts +16 -0
  10. package/dist/commands/pg/fdwsql.js +70 -0
  11. package/dist/commands/pg/index-size.d.ts +16 -0
  12. package/dist/commands/pg/index-size.js +40 -0
  13. package/dist/commands/pg/index-usage.d.ts +16 -0
  14. package/dist/commands/pg/index-usage.js +41 -0
  15. package/dist/commands/pg/long-running-queries.d.ts +16 -0
  16. package/dist/commands/pg/long-running-queries.js +43 -0
  17. package/dist/commands/pg/mandelbrot.d.ts +15 -0
  18. package/dist/commands/pg/mandelbrot.js +49 -0
  19. package/dist/commands/pg/records-rank.d.ts +16 -0
  20. package/dist/commands/pg/records-rank.js +38 -0
  21. package/dist/commands/pg/seq-scans.d.ts +16 -0
  22. package/dist/commands/pg/seq-scans.js +36 -0
  23. package/dist/commands/pg/stats-reset.d.ts +15 -0
  24. package/dist/commands/pg/stats-reset.js +34 -0
  25. package/dist/commands/pg/table-indexes-size.d.ts +16 -0
  26. package/dist/commands/pg/table-indexes-size.js +39 -0
  27. package/dist/commands/pg/table-size.d.ts +16 -0
  28. package/dist/commands/pg/table-size.js +39 -0
  29. package/dist/commands/pg/total-index-size.d.ts +16 -0
  30. package/dist/commands/pg/total-index-size.js +37 -0
  31. package/dist/commands/pg/total-table-size.d.ts +16 -0
  32. package/dist/commands/pg/total-table-size.js +39 -0
  33. package/dist/commands/pg/unused-indexes.d.ts +16 -0
  34. package/dist/commands/pg/unused-indexes.js +41 -0
  35. package/dist/commands/pg/user-connections.d.ts +16 -0
  36. package/dist/commands/pg/user-connections.js +38 -0
  37. package/dist/lib/pg/extras.d.ts +11 -0
  38. package/dist/lib/pg/extras.js +57 -0
  39. package/npm-shrinkwrap.json +2 -2
  40. package/oclif.manifest.json +1173 -165
  41. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
6
 
7
+ ## [11.7.0](https://github.com/heroku/cli/compare/v11.6.0...v11.7.0) (2026-06-23)
8
+
9
+
10
+ ### Features
11
+
12
+ * migrate extra pg commands from pg-extras plugin to cli ([#3782](https://github.com/heroku/cli/issues/3782)) ([38a4604](https://github.com/heroku/cli/commit/38a4604e46a3385578a223cd88c6d7c784119e53))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * revert whoami status code fix ([#3785](https://github.com/heroku/cli/issues/3785)) ([1706012](https://github.com/heroku/cli/commit/1706012f6e37253677efffff35d866c8097648c2))
18
+
7
19
  ## [11.6.0](https://github.com/heroku/cli/compare/v11.5.0...v11.6.0) (2026-06-17)
8
20
 
9
21
 
@@ -17,7 +17,7 @@ export default class AuthWhoami extends Command {
17
17
  this.log(account.email);
18
18
  }
19
19
  catch (error) {
20
- if (error.http.statusCode === 401)
20
+ if (error.statusCode === 401)
21
21
  this.notloggedin();
22
22
  throw error;
23
23
  }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export declare const generateCacheHitQuery: () => string;
3
+ export default class PgCacheHit extends Command {
4
+ static args: {
5
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static hiddenAliases: string[];
14
+ static topic: string;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,40 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { nls } from '../../nls.js';
6
+ const heredoc = tsheredoc.default;
7
+ export const generateCacheHitQuery = () => `
8
+ SELECT
9
+ 'index hit rate' AS name,
10
+ (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) AS ratio
11
+ FROM pg_statio_user_indexes
12
+ UNION ALL
13
+ SELECT
14
+ 'table hit rate' AS name,
15
+ sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) AS ratio
16
+ FROM pg_statio_user_tables;
17
+ `.trim();
18
+ export default class PgCacheHit extends Command {
19
+ static args = {
20
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
21
+ };
22
+ static description = 'show index and table hit rate';
23
+ static examples = [heredoc `
24
+ ${color.command('heroku pg:cache-hit --app example-app')}
25
+ `];
26
+ static flags = {
27
+ app: flags.app({ required: true }),
28
+ remote: flags.remote(),
29
+ };
30
+ static hiddenAliases = ['pg:cache_hit'];
31
+ static topic = 'pg';
32
+ async run() {
33
+ const { args, flags } = await this.parse(PgCacheHit);
34
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
35
+ const db = await dbResolver.getDatabase(flags.app, args.database);
36
+ const psqlService = new utils.pg.PsqlService(db);
37
+ const output = await psqlService.execQuery(generateCacheHitQuery());
38
+ ux.stdout(output);
39
+ }
40
+ }
@@ -0,0 +1,19 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ import { pg } from '@heroku/heroku-cli-util';
3
+ export declare function generateCallsQuery(db: pg.ConnectionDetails, flags: {
4
+ truncate?: boolean;
5
+ }): Promise<string>;
6
+ export default class PgCalls extends Command {
7
+ static args: {
8
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
9
+ };
10
+ static description: string;
11
+ static examples: string[];
12
+ static flags: {
13
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ truncate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ static topic: string;
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,55 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { ensurePGStatStatement, newBlkTimeFields, newTotalExecTimeField } from '../../lib/pg/extras.js';
6
+ import { nls } from '../../nls.js';
7
+ const heredoc = tsheredoc.default;
8
+ export async function generateCallsQuery(db, flags) {
9
+ await ensurePGStatStatement(db);
10
+ const truncatedQueryString = flags.truncate
11
+ ? 'CASE WHEN length(query) <= 40 THEN query ELSE substr(query, 0, 39) || \'…\' END'
12
+ : 'query';
13
+ const newTotalExecTime = await newTotalExecTimeField(db);
14
+ const totalExecTimeField = newTotalExecTime ? 'total_exec_time' : 'total_time';
15
+ const newBlkTime = await newBlkTimeFields(db);
16
+ const blkReadField = newBlkTime ? 'shared_blk_read_time' : 'blk_read_time';
17
+ const blkWriteField = newBlkTime ? 'shared_blk_write_time' : 'blk_write_time';
18
+ return `
19
+ SELECT interval '1 millisecond' * ${totalExecTimeField} AS total_exec_time,
20
+ to_char((${totalExecTimeField}/sum(${totalExecTimeField}) OVER()) * 100, 'FM90D0') || '%' AS prop_exec_time,
21
+ to_char(calls, 'FM999G999G999G990') AS ncalls,
22
+ interval '1 millisecond' * (${blkReadField} + ${blkWriteField}) AS sync_io_time,
23
+ ${truncatedQueryString} AS query
24
+ FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1)
25
+ ORDER BY calls DESC
26
+ LIMIT 10
27
+ `.trim();
28
+ }
29
+ export default class PgCalls extends Command {
30
+ static args = {
31
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
32
+ };
33
+ static description = 'show 10 queries that have highest frequency of execution';
34
+ static examples = [heredoc `
35
+ ${color.command('heroku pg:calls --app example-app')}
36
+ `, heredoc `
37
+ # truncate queries to 40 characters
38
+ ${color.command('heroku pg:calls --truncate --app example-app')}
39
+ `];
40
+ static flags = {
41
+ app: flags.app({ required: true }),
42
+ remote: flags.remote(),
43
+ truncate: flags.boolean({ char: 't', description: 'truncate queries to 40 characters' }),
44
+ };
45
+ static topic = 'pg';
46
+ async run() {
47
+ const { args, flags } = await this.parse(PgCalls);
48
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
49
+ const db = await dbResolver.getDatabase(flags.app, args.database);
50
+ const psqlService = new utils.pg.PsqlService(db);
51
+ const query = await generateCallsQuery(db, flags);
52
+ const output = await psqlService.execQuery(query);
53
+ ux.stdout(output);
54
+ }
55
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ import { pg } from '@heroku/heroku-cli-util';
3
+ export declare function generateExtensionsQuery(db: pg.ConnectionDetails): string;
4
+ export default class PgExtensions extends Command {
5
+ static args: {
6
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ };
8
+ static description: string;
9
+ static examples: string[];
10
+ static flags: {
11
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ static topic: string;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,39 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { essentialNumPlan } from '../../lib/pg/extras.js';
6
+ import { nls } from '../../nls.js';
7
+ const heredoc = tsheredoc.default;
8
+ export function generateExtensionsQuery(db) {
9
+ return essentialNumPlan(db.attachment.addon)
10
+ ? `SELECT *
11
+ FROM pg_available_extensions
12
+ WHERE name IN (SELECT unnest(string_to_array(current_setting('rds.allowed_extensions'), ',')))`
13
+ : `SELECT *
14
+ FROM pg_available_extensions
15
+ WHERE name IN (SELECT unnest(string_to_array(current_setting('extwlist.extensions'), ',')))`;
16
+ }
17
+ export default class PgExtensions extends Command {
18
+ static args = {
19
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
20
+ };
21
+ static description = 'list available and installed extensions';
22
+ static examples = [heredoc `
23
+ ${color.command('heroku pg:extensions --app example-app')}
24
+ `];
25
+ static flags = {
26
+ app: flags.app({ required: true }),
27
+ remote: flags.remote(),
28
+ };
29
+ static topic = 'pg';
30
+ async run() {
31
+ const { args, flags } = await this.parse(PgExtensions);
32
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
33
+ const db = await dbResolver.getDatabase(flags.app, args.database);
34
+ const psqlService = new utils.pg.PsqlService(db);
35
+ const query = generateExtensionsQuery(db);
36
+ const output = await psqlService.execQuery(query);
37
+ ux.stdout(output);
38
+ }
39
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export declare const generateFdwsqlQuery: (prefix: string) => string;
3
+ export default class PgFdwsql extends Command {
4
+ static args: {
5
+ prefix: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
+ };
8
+ static description: string;
9
+ static examples: string[];
10
+ static flags: {
11
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ static topic: string;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,70 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { ensureEssentialTierPlan } from '../../lib/pg/extras.js';
6
+ import { nls } from '../../nls.js';
7
+ const heredoc = tsheredoc.default;
8
+ export const generateFdwsqlQuery = (prefix) => `
9
+ SELECT
10
+ 'CREATE FOREIGN TABLE '
11
+ || quote_ident('${prefix}_' || c.relname)
12
+ || '(' || array_to_string(array_agg(quote_ident(a.attname) || ' ' || t.typname), ', ') || ') '
13
+ || ' SERVER ${prefix}_db OPTIONS'
14
+ || ' (schema_name ''' || quote_ident(n.nspname) || ''', table_name ''' || quote_ident(c.relname) || ''');'
15
+ FROM
16
+ pg_class c,
17
+ pg_attribute a,
18
+ pg_type t,
19
+ pg_namespace n
20
+ WHERE
21
+ a.attnum > 0
22
+ AND a.attrelid = c.oid
23
+ AND a.atttypid = t.oid
24
+ AND n.oid = c.relnamespace
25
+ AND c.relkind in ('r', 'v')
26
+ AND n.nspname <> 'pg_catalog'
27
+ AND n.nspname <> 'information_schema'
28
+ AND n.nspname !~ '^pg_toast'
29
+ AND pg_catalog.pg_table_is_visible(c.oid)
30
+ GROUP BY c.relname, n.nspname
31
+ ORDER BY c.relname;
32
+ `.trim();
33
+ export default class PgFdwsql extends Command {
34
+ /* eslint-disable perfectionist/sort-objects */
35
+ static args = {
36
+ prefix: Args.string({ description: 'prefix for foreign data wrapper', required: true }),
37
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
38
+ };
39
+ /* eslint-enable perfectionist/sort-objects */
40
+ static description = 'generate fdw install sql for database';
41
+ static examples = [heredoc `
42
+ ${color.command('heroku pg:fdwsql example_prefix --app example-app')}
43
+ `];
44
+ static flags = {
45
+ app: flags.app({ required: true }),
46
+ remote: flags.remote(),
47
+ };
48
+ static topic = 'pg';
49
+ async run() {
50
+ const { args, flags } = await this.parse(PgFdwsql);
51
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(args.prefix)) {
52
+ ux.error('prefix must start with a letter or underscore and contain only letters, numbers, and underscores', { exit: 1 });
53
+ }
54
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
55
+ const db = await dbResolver.getDatabase(flags.app, args.database);
56
+ await ensureEssentialTierPlan(db);
57
+ const psqlService = new utils.pg.PsqlService(db);
58
+ ux.stdout('CREATE EXTENSION IF NOT EXISTS postgres_fdw;');
59
+ ux.stdout(`DROP SERVER IF EXISTS ${args.prefix}_db;`);
60
+ ux.stdout(`CREATE SERVER ${args.prefix}_db
61
+ FOREIGN DATA WRAPPER postgres_fdw
62
+ OPTIONS (dbname '${db.database}', host '${db.host}');`);
63
+ ux.stdout(`CREATE USER MAPPING FOR CURRENT_USER
64
+ SERVER ${args.prefix}_db
65
+ OPTIONS (user '${db.user}', password '${db.password}');`);
66
+ let output = await psqlService.execQuery(generateFdwsqlQuery(args.prefix));
67
+ output = output.split('\n').filter((l) => /CREATE/.test(l)).join('\n');
68
+ ux.stdout(output);
69
+ }
70
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export declare const generateIndexSizeQuery: () => string;
3
+ export default class PgIndexSize extends Command {
4
+ static args: {
5
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static hiddenAliases: string[];
14
+ static topic: string;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,40 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { nls } from '../../nls.js';
6
+ const heredoc = tsheredoc.default;
7
+ export const generateIndexSizeQuery = () => `
8
+ SELECT c.relname AS name,
9
+ pg_size_pretty(sum(c.relpages::bigint*8192)::bigint) AS size
10
+ FROM pg_class c
11
+ LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
12
+ WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
13
+ AND n.nspname !~ '^pg_toast'
14
+ AND c.relkind='i'
15
+ GROUP BY c.relname
16
+ ORDER BY sum(c.relpages) DESC;
17
+ `.trim();
18
+ export default class PgIndexSize extends Command {
19
+ static args = {
20
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
21
+ };
22
+ static description = 'show the size of indexes, descending by size';
23
+ static examples = [heredoc `
24
+ ${color.command('heroku pg:index-size --app example-app')}
25
+ `];
26
+ static flags = {
27
+ app: flags.app({ required: true }),
28
+ remote: flags.remote(),
29
+ };
30
+ static hiddenAliases = ['pg:index_size'];
31
+ static topic = 'pg';
32
+ async run() {
33
+ const { args, flags } = await this.parse(PgIndexSize);
34
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
35
+ const db = await dbResolver.getDatabase(flags.app, args.database);
36
+ const psqlService = new utils.pg.PsqlService(db);
37
+ const output = await psqlService.execQuery(generateIndexSizeQuery());
38
+ ux.stdout(output);
39
+ }
40
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export declare const generateIndexUsageQuery: () => string;
3
+ export default class PgIndexUsage extends Command {
4
+ static args: {
5
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static hiddenAliases: string[];
14
+ static topic: string;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,41 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { nls } from '../../nls.js';
6
+ const heredoc = tsheredoc.default;
7
+ export const generateIndexUsageQuery = () => `
8
+ SELECT relname,
9
+ CASE idx_scan
10
+ WHEN 0 THEN 'Insufficient data'
11
+ ELSE (100 * idx_scan / (seq_scan + idx_scan))::text
12
+ END percent_of_times_index_used,
13
+ n_live_tup rows_in_table
14
+ FROM
15
+ pg_stat_user_tables
16
+ ORDER BY
17
+ n_live_tup DESC;
18
+ `.trim();
19
+ export default class PgIndexUsage extends Command {
20
+ static args = {
21
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
22
+ };
23
+ static description = 'calculates your index hit rate (effective databases are at 99% and up)';
24
+ static examples = [heredoc `
25
+ ${color.command('heroku pg:index-usage --app example-app')}
26
+ `];
27
+ static flags = {
28
+ app: flags.app({ required: true }),
29
+ remote: flags.remote(),
30
+ };
31
+ static hiddenAliases = ['pg:index_usage'];
32
+ static topic = 'pg';
33
+ async run() {
34
+ const { args, flags } = await this.parse(PgIndexUsage);
35
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
36
+ const db = await dbResolver.getDatabase(flags.app, args.database);
37
+ const psqlService = new utils.pg.PsqlService(db);
38
+ const output = await psqlService.execQuery(generateIndexUsageQuery());
39
+ ux.stdout(output);
40
+ }
41
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export declare const generateLongRunningQueriesQuery: () => string;
3
+ export default class PgLongRunningQueries extends Command {
4
+ static args: {
5
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static hiddenAliases: string[];
14
+ static topic: string;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,43 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { nls } from '../../nls.js';
6
+ const heredoc = tsheredoc.default;
7
+ export const generateLongRunningQueriesQuery = () => `
8
+ SELECT
9
+ pid,
10
+ now() - pg_stat_activity.query_start AS duration,
11
+ query AS query
12
+ FROM
13
+ pg_stat_activity
14
+ WHERE
15
+ pg_stat_activity.query <> ''::text
16
+ AND state <> 'idle'
17
+ AND now() - pg_stat_activity.query_start > interval '5 minutes'
18
+ ORDER BY
19
+ now() - pg_stat_activity.query_start DESC;
20
+ `.trim();
21
+ export default class PgLongRunningQueries extends Command {
22
+ static args = {
23
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
24
+ };
25
+ static description = 'show all queries longer than five minutes by descending duration';
26
+ static examples = [heredoc `
27
+ ${color.command('heroku pg:long-running-queries --app example-app')}
28
+ `];
29
+ static flags = {
30
+ app: flags.app({ required: true }),
31
+ remote: flags.remote(),
32
+ };
33
+ static hiddenAliases = ['pg:long_running_queries'];
34
+ static topic = 'pg';
35
+ async run() {
36
+ const { args, flags } = await this.parse(PgLongRunningQueries);
37
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
38
+ const db = await dbResolver.getDatabase(flags.app, args.database);
39
+ const psqlService = new utils.pg.PsqlService(db);
40
+ const output = await psqlService.execQuery(generateLongRunningQueriesQuery());
41
+ ux.stdout(output);
42
+ }
43
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export declare const generateMandelbrotQuery: () => string;
3
+ export default class PgMandelbrot extends Command {
4
+ static args: {
5
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static topic: string;
14
+ run(): Promise<void>;
15
+ }
@@ -0,0 +1,49 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { nls } from '../../nls.js';
6
+ const heredoc = tsheredoc.default;
7
+ export const generateMandelbrotQuery = () => `
8
+ WITH RECURSIVE Z(IX, IY, CX, CY, X, Y, I) AS (
9
+ SELECT IX, IY, X::float, Y::float, X::float, Y::float, 0
10
+ FROM (select -2.2 + 0.031 * i, i from generate_series(0,101) as i) as xgen(x,ix),
11
+ (select -1.5 + 0.031 * i, i from generate_series(0,101) as i) as ygen(y,iy)
12
+ UNION ALL
13
+ SELECT IX, IY, CX, CY, X * X - Y * Y + CX AS X, Y * X * 2 + CY, I + 1
14
+ FROM Z
15
+ WHERE X * X + Y * Y < 16::float
16
+ AND I < 100
17
+ )
18
+ SELECT array_to_string(array_agg(SUBSTRING(' .,,,-----++++%%%%@@@@#### ', LEAST(GREATEST(I,1),27), 1)),'')
19
+ FROM (
20
+ SELECT IX, IY, MAX(I) AS I
21
+ FROM Z
22
+ GROUP BY IY, IX
23
+ ORDER BY IY, IX
24
+ ) AS ZT
25
+ GROUP BY IY
26
+ ORDER BY IY
27
+ `.trim();
28
+ export default class PgMandelbrot extends Command {
29
+ static args = {
30
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
31
+ };
32
+ static description = 'show the mandelbrot set';
33
+ static examples = [heredoc `
34
+ ${color.command('heroku pg:mandelbrot --app example-app')}
35
+ `];
36
+ static flags = {
37
+ app: flags.app({ required: true }),
38
+ remote: flags.remote(),
39
+ };
40
+ static topic = 'pg';
41
+ async run() {
42
+ const { args, flags } = await this.parse(PgMandelbrot);
43
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
44
+ const db = await dbResolver.getDatabase(flags.app, args.database);
45
+ const psqlService = new utils.pg.PsqlService(db);
46
+ const output = await psqlService.execQuery(generateMandelbrotQuery());
47
+ ux.stdout(output);
48
+ }
49
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export declare const generateRecordsRankQuery: () => string;
3
+ export default class PgRecordsRank extends Command {
4
+ static args: {
5
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static hiddenAliases: string[];
14
+ static topic: string;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,38 @@
1
+ import { Command, flags } from '@heroku-cli/command';
2
+ import { color, utils } from '@heroku/heroku-cli-util';
3
+ import { Args, ux } from '@oclif/core';
4
+ import tsheredoc from 'tsheredoc';
5
+ import { nls } from '../../nls.js';
6
+ const heredoc = tsheredoc.default;
7
+ export const generateRecordsRankQuery = () => `
8
+ SELECT
9
+ relname AS name,
10
+ n_live_tup AS estimated_count
11
+ FROM
12
+ pg_stat_user_tables
13
+ ORDER BY
14
+ n_live_tup DESC;
15
+ `.trim();
16
+ export default class PgRecordsRank extends Command {
17
+ static args = {
18
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: false }),
19
+ };
20
+ static description = 'show all tables and the number of rows in each ordered by number of rows descending';
21
+ static examples = [heredoc `
22
+ ${color.command('heroku pg:records-rank --app example-app')}
23
+ `];
24
+ static flags = {
25
+ app: flags.app({ required: true }),
26
+ remote: flags.remote(),
27
+ };
28
+ static hiddenAliases = ['pg:records_rank'];
29
+ static topic = 'pg';
30
+ async run() {
31
+ const { args, flags } = await this.parse(PgRecordsRank);
32
+ const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
33
+ const db = await dbResolver.getDatabase(flags.app, args.database);
34
+ const psqlService = new utils.pg.PsqlService(db);
35
+ const output = await psqlService.execQuery(generateRecordsRankQuery());
36
+ ux.stdout(output);
37
+ }
38
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@heroku-cli/command';
2
+ export declare const generateSeqScansQuery: () => string;
3
+ export default class PgSeqScans extends Command {
4
+ static args: {
5
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ static hiddenAliases: string[];
14
+ static topic: string;
15
+ run(): Promise<void>;
16
+ }