postgresai 0.14.0-dev.48 → 0.14.0-dev.50

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.
package/bun.lock CHANGED
@@ -131,7 +131,7 @@
131
131
 
132
132
  "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
133
133
 
134
- "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": "bin/js-yaml.js" }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
134
+ "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
135
135
 
136
136
  "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
137
137
 
@@ -13064,7 +13064,7 @@ var {
13064
13064
  // package.json
13065
13065
  var package_default = {
13066
13066
  name: "postgresai",
13067
- version: "0.14.0-dev.48",
13067
+ version: "0.14.0-dev.50",
13068
13068
  description: "postgres_ai CLI",
13069
13069
  license: "Apache-2.0",
13070
13070
  private: false,
@@ -15881,7 +15881,7 @@ var Result = import_lib.default.Result;
15881
15881
  var TypeOverrides = import_lib.default.TypeOverrides;
15882
15882
  var defaults = import_lib.default.defaults;
15883
15883
  // package.json
15884
- var version = "0.14.0-dev.48";
15884
+ var version = "0.14.0-dev.50";
15885
15885
  var package_default2 = {
15886
15886
  name: "postgresai",
15887
15887
  version,
@@ -24599,7 +24599,13 @@ redundant_indexes_tmp_num as (
24599
24599
  formated_schema_name as tag_schema_name,
24600
24600
  formated_table_name as tag_table_name,
24601
24601
  formated_relation_name as tag_relation_name,
24602
- supports_fk::int as supports_fk
24602
+ supports_fk::int as supports_fk,
24603
+ json_agg(
24604
+ distinct jsonb_build_object(
24605
+ 'index_name', reason,
24606
+ 'index_definition', main_index_def
24607
+ )
24608
+ )::text as covering_indexes_json
24603
24609
  from redundant_indexes_cut_grouped
24604
24610
  group by
24605
24611
  index_id,
@@ -24911,6 +24917,17 @@ async function getRedundantIndexes(client) {
24911
24917
  const transformed = transformMetricRow(row);
24912
24918
  const indexSizeBytes = parseInt(String(transformed.index_size_bytes || 0), 10);
24913
24919
  const tableSizeBytes = parseInt(String(transformed.table_size_bytes || 0), 10);
24920
+ let coveringIndexes = [];
24921
+ try {
24922
+ const jsonStr = String(transformed.covering_indexes_json || "[]");
24923
+ const parsed = JSON.parse(jsonStr);
24924
+ if (Array.isArray(parsed)) {
24925
+ coveringIndexes = parsed.map((item) => ({
24926
+ index_name: String(item.index_name || ""),
24927
+ index_definition: String(item.index_definition || "")
24928
+ }));
24929
+ }
24930
+ } catch {}
24914
24931
  return {
24915
24932
  schema_name: String(transformed.schema_name || ""),
24916
24933
  table_name: String(transformed.table_name || ""),
@@ -24924,7 +24941,8 @@ async function getRedundantIndexes(client) {
24924
24941
  supports_fk: transformed.supports_fk === true || transformed.supports_fk === 1,
24925
24942
  index_definition: String(transformed.index_definition || ""),
24926
24943
  index_size_pretty: formatBytes(indexSizeBytes),
24927
- table_size_pretty: formatBytes(tableSizeBytes)
24944
+ table_size_pretty: formatBytes(tableSizeBytes),
24945
+ covering_indexes: coveringIndexes
24928
24946
  };
24929
24947
  });
24930
24948
  }
package/lib/checkup.ts CHANGED
@@ -127,6 +127,14 @@ export interface StatsReset {
127
127
  /**
128
128
  * Redundant index entry (H004) - matches H004.schema.json redundantIndex
129
129
  */
130
+ /**
131
+ * Covering index definition (the index that makes another index redundant)
132
+ */
133
+ export interface CoveringIndex {
134
+ index_name: string;
135
+ index_definition: string;
136
+ }
137
+
130
138
  export interface RedundantIndex {
131
139
  schema_name: string;
132
140
  table_name: string;
@@ -141,6 +149,7 @@ export interface RedundantIndex {
141
149
  index_definition: string;
142
150
  index_size_pretty: string;
143
151
  table_size_pretty: string;
152
+ covering_indexes: CoveringIndex[];
144
153
  }
145
154
 
146
155
  /**
@@ -516,6 +525,22 @@ export async function getRedundantIndexes(client: Client): Promise<RedundantInde
516
525
  const transformed = transformMetricRow(row);
517
526
  const indexSizeBytes = parseInt(String(transformed.index_size_bytes || 0), 10);
518
527
  const tableSizeBytes = parseInt(String(transformed.table_size_bytes || 0), 10);
528
+
529
+ // Parse covering_indexes JSON array
530
+ let coveringIndexes: CoveringIndex[] = [];
531
+ try {
532
+ const jsonStr = String(transformed.covering_indexes_json || "[]");
533
+ const parsed = JSON.parse(jsonStr);
534
+ if (Array.isArray(parsed)) {
535
+ coveringIndexes = parsed.map((item: any) => ({
536
+ index_name: String(item.index_name || ""),
537
+ index_definition: String(item.index_definition || ""),
538
+ }));
539
+ }
540
+ } catch {
541
+ // If JSON parsing fails, leave as empty array
542
+ }
543
+
519
544
  return {
520
545
  schema_name: String(transformed.schema_name || ""),
521
546
  table_name: String(transformed.table_name || ""),
@@ -530,6 +555,7 @@ export async function getRedundantIndexes(client: Client): Promise<RedundantInde
530
555
  index_definition: String(transformed.index_definition || ""),
531
556
  index_size_pretty: formatBytes(indexSizeBytes),
532
557
  table_size_pretty: formatBytes(tableSizeBytes),
558
+ covering_indexes: coveringIndexes,
533
559
  };
534
560
  });
535
561
  }
@@ -418,7 +418,13 @@ redundant_indexes_tmp_num as (
418
418
  formated_schema_name as tag_schema_name,
419
419
  formated_table_name as tag_table_name,
420
420
  formated_relation_name as tag_relation_name,
421
- supports_fk::int as supports_fk
421
+ supports_fk::int as supports_fk,
422
+ json_agg(
423
+ distinct jsonb_build_object(
424
+ 'index_name', reason,
425
+ 'index_definition', main_index_def
426
+ )
427
+ )::text as covering_indexes_json
422
428
  from redundant_indexes_cut_grouped
423
429
  group by
424
430
  index_id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresai",
3
- "version": "0.14.0-dev.48",
3
+ "version": "0.14.0-dev.50",
4
4
  "description": "postgres_ai CLI",
5
5
  "license": "Apache-2.0",
6
6
  "private": false,
@@ -840,6 +840,9 @@ describe("H004 - Redundant indexes", () => {
840
840
  index_usage: "0",
841
841
  supports_fk: false,
842
842
  index_definition: "CREATE INDEX orders_user_id_idx ON public.orders USING btree (user_id)",
843
+ covering_indexes_json: JSON.stringify([
844
+ { index_name: "public.orders_user_id_created_idx", index_definition: "CREATE INDEX orders_user_id_created_idx ON public.orders USING btree (user_id, created_at)" }
845
+ ]),
843
846
  },
844
847
  ],
845
848
  });
@@ -853,6 +856,11 @@ describe("H004 - Redundant indexes", () => {
853
856
  expect(indexes[0].supports_fk).toBe(false);
854
857
  expect(indexes[0].index_definition).toBeTruthy();
855
858
  expect(indexes[0].relation_name).toBe("orders");
859
+ // Verify covering_indexes is populated with definitions
860
+ expect(indexes[0].covering_indexes).toBeInstanceOf(Array);
861
+ expect(indexes[0].covering_indexes.length).toBe(1);
862
+ expect(indexes[0].covering_indexes[0].index_name).toBe("public.orders_user_id_created_idx");
863
+ expect(indexes[0].covering_indexes[0].index_definition).toContain("CREATE INDEX");
856
864
  });
857
865
 
858
866
  test("generateH004 creates report with redundant indexes", async () => {
@@ -876,6 +884,9 @@ describe("H004 - Redundant indexes", () => {
876
884
  index_usage: "5",
877
885
  supports_fk: false,
878
886
  index_definition: "CREATE INDEX products_category_idx ON public.products USING btree (category)",
887
+ covering_indexes_json: JSON.stringify([
888
+ { index_name: "public.products_category_name_idx", index_definition: "CREATE INDEX products_category_name_idx ON public.products USING btree (category, name)" }
889
+ ]),
879
890
  },
880
891
  ],
881
892
  }
@@ -919,6 +930,9 @@ describe("H004 - Redundant indexes", () => {
919
930
  index_usage: "5",
920
931
  supports_fk: false,
921
932
  index_definition: "CREATE INDEX products_category_idx ON public.products USING btree (category)",
933
+ covering_indexes_json: JSON.stringify([
934
+ { index_name: "public.products_category_name_idx", index_definition: "CREATE INDEX products_category_name_idx ON public.products USING btree (category, name)" }
935
+ ]),
922
936
  },
923
937
  ],
924
938
  }
@@ -202,7 +202,10 @@ describe("H004 schema validation", () => {
202
202
  table_size_bytes: "16777216",
203
203
  index_usage: "0",
204
204
  supports_fk: false,
205
- index_definition: "CREATE INDEX orders_user_id_idx ON public.orders USING btree (user_id)"
205
+ index_definition: "CREATE INDEX orders_user_id_idx ON public.orders USING btree (user_id)",
206
+ covering_indexes_json: JSON.stringify([
207
+ { index_name: "public.orders_user_id_created_idx", index_definition: "CREATE INDEX orders_user_id_created_idx ON public.orders USING btree (user_id, created_at)" }
208
+ ])
206
209
  },
207
210
  ],
208
211
  });