@query-doctor/core 0.0.3 → 0.0.4

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 (42) hide show
  1. package/dist/index.cjs +222 -97
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +218 -97
  6. package/dist/index.js.map +1 -1
  7. package/dist/optimizer/genalgo.d.ts +9 -5
  8. package/dist/optimizer/genalgo.d.ts.map +1 -1
  9. package/dist/optimizer/genalgo.js +304 -0
  10. package/dist/optimizer/statistics.d.ts +1 -24
  11. package/dist/optimizer/statistics.d.ts.map +1 -1
  12. package/dist/optimizer/statistics.js +700 -0
  13. package/dist/package.json +25 -0
  14. package/dist/sql/analyzer.d.ts +3 -3
  15. package/dist/sql/analyzer.js +270 -0
  16. package/dist/sql/analyzer.test.d.ts +2 -0
  17. package/dist/sql/analyzer.test.d.ts.map +1 -0
  18. package/dist/sql/analyzer_test.js +584 -0
  19. package/dist/sql/builder.js +77 -0
  20. package/dist/sql/database.d.ts +5 -0
  21. package/dist/sql/database.d.ts.map +1 -1
  22. package/dist/sql/database.js +20 -0
  23. package/dist/sql/indexes.d.ts +8 -0
  24. package/dist/sql/indexes.d.ts.map +1 -0
  25. package/dist/sql/indexes.js +12 -0
  26. package/dist/sql/nudges.js +241 -0
  27. package/dist/sql/permutations.test.d.ts +2 -0
  28. package/dist/sql/permutations.test.d.ts.map +1 -0
  29. package/dist/sql/permutations_test.js +53 -0
  30. package/dist/sql/pg-identifier.d.ts +9 -0
  31. package/dist/sql/pg-identifier.d.ts.map +1 -0
  32. package/dist/sql/pg-identifier.test.d.ts +2 -0
  33. package/dist/sql/pg-identifier.test.d.ts.map +1 -0
  34. package/dist/sql/walker.d.ts +2 -2
  35. package/dist/sql/walker.js +295 -0
  36. package/package.json +2 -2
  37. package/dist/index.mjs +0 -24297
  38. package/dist/index.mjs.map +0 -1
  39. package/dist/sql/schema_dump.d.ts +0 -132
  40. package/dist/sql/schema_dump.d.ts.map +0 -1
  41. package/dist/sql/trace.d.ts +0 -1
  42. package/dist/sql/trace.d.ts.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import type { NullTestType } from "@pgsql/types";
2
- import type { SortContext } from "../sql/analyzer.ts";
2
+ import type { SortContext } from "../sql/analyzer.js";
3
3
  import { PostgresQueryBuilder } from "../sql/builder.js";
4
- import type { Postgres, PostgresTransaction } from "../sql/database.ts";
5
- import type { IndexedTable, Statistics } from "./statistics.ts";
4
+ import { type Postgres, type PostgresTransaction } from "../sql/database.js";
5
+ import type { IndexedTable, Statistics } from "./statistics.js";
6
6
  export type IndexIdentifier = string;
7
7
  export type IndexRecommendation = PermutedIndexCandidate & {
8
8
  definition: IndexIdentifier;
@@ -16,7 +16,7 @@ export declare class IndexOptimizer {
16
16
  constructor(db: Postgres, statistics: Statistics, existingIndexes: IndexedTable[], config?: {
17
17
  debug?: boolean;
18
18
  });
19
- run(builder: PostgresQueryBuilder, indexes: RootIndexCandidate[]): Promise<OptimizeResult>;
19
+ run(builder: PostgresQueryBuilder, indexes: RootIndexCandidate[], beforeQuery?: (tx: PostgresTransaction) => Promise<void>): Promise<OptimizeResult>;
20
20
  runWithoutIndexes(builder: PostgresQueryBuilder): Promise<{
21
21
  Plan: any;
22
22
  }>;
@@ -27,9 +27,13 @@ export declare class IndexOptimizer {
27
27
  */
28
28
  private indexName;
29
29
  private indexAlreadyExists;
30
+ /**
31
+ * Derive the list of indexes [tableA(X, Y, Z), tableB(H, I, J)]
32
+ **/
33
+ private indexesToCreate;
30
34
  private toDefinition;
31
35
  /**
32
- * Drop indexes that can be dropped (non-primary keys)
36
+ * Drop indexes that can be dropped. Ignore the ones that can't
33
37
  */
34
38
  private dropExistingIndexes;
35
39
  private whereClause;
@@ -1 +1 @@
1
- {"version":3,"file":"genalgo.d.ts","sourceRoot":"","sources":["../../src/optimizer/genalgo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"genalgo.d.ts","sourceRoot":"","sources":["../../src/optimizer/genalgo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,304 @@
1
+ import { blue, gray, green, magenta, red, yellow } from "colorette";
2
+ import { PostgresQueryBuilder } from "../sql/builder.js";
3
+ import { dropIndex, } from "../sql/database.js";
4
+ import { isIndexProbablyDroppable } from "../sql/indexes.js";
5
+ export class IndexOptimizer {
6
+ db;
7
+ statistics;
8
+ existingIndexes;
9
+ config;
10
+ static prefix = "__qd_";
11
+ constructor(db, statistics, existingIndexes, config = {}) {
12
+ this.db = db;
13
+ this.statistics = statistics;
14
+ this.existingIndexes = existingIndexes;
15
+ this.config = config;
16
+ }
17
+ async run(builder, indexes) {
18
+ const baseExplain = await this.testQueryWithStats(builder);
19
+ const baseCost = Number(baseExplain.Plan["Total Cost"]);
20
+ if (baseCost === 0) {
21
+ return {
22
+ kind: "zero_cost_plan",
23
+ explainPlan: baseExplain,
24
+ };
25
+ }
26
+ const toCreate = this.indexesToCreate(indexes);
27
+ const finalExplain = await this.testQueryWithStats(builder, async (sql) => {
28
+ for (const permutation of toCreate) {
29
+ const createIndex = PostgresQueryBuilder.createIndex(this.toDefinition(permutation).raw, permutation.name)
30
+ .introspect()
31
+ .build();
32
+ await sql.exec(createIndex);
33
+ }
34
+ });
35
+ const finalCost = Number(finalExplain.Plan["Total Cost"]);
36
+ if (this.config.debug) {
37
+ console.dir(finalExplain, { depth: null });
38
+ }
39
+ const deltaPercentage = ((baseCost - finalCost) / baseCost) * 100;
40
+ if (finalCost < baseCost) {
41
+ console.log(` 🎉🎉🎉 ${green(`+${deltaPercentage.toFixed(2).padStart(5, "0")}%`)}`);
42
+ }
43
+ else if (finalCost > baseCost) {
44
+ console.log(`${red(`-${Math.abs(deltaPercentage).toFixed(2).padStart(5, "0")}%`)} ${gray("If there's a better index, we haven't tried it")}`);
45
+ }
46
+ const { newIndexes, existingIndexes: existingIndexesUsedByQuery } = this.findUsedIndexes(finalExplain.Plan);
47
+ return {
48
+ kind: "ok",
49
+ baseCost,
50
+ finalCost,
51
+ newIndexes,
52
+ existingIndexes: existingIndexesUsedByQuery,
53
+ triedIndexes: new Map(toCreate.map((index) => [index.name, index])),
54
+ baseExplainPlan: baseExplain,
55
+ explainPlan: finalExplain,
56
+ };
57
+ }
58
+ async runWithoutIndexes(builder) {
59
+ return await this.testQueryWithStats(builder, async (tx) => {
60
+ await this.dropExistingIndexes(tx);
61
+ });
62
+ }
63
+ /**
64
+ * Postgres has a limit of 63 characters for index names.
65
+ * So we use this to make sure we don't derive it from a list of columns that can
66
+ * overflow that limit.
67
+ */
68
+ indexName() {
69
+ return IndexOptimizer.prefix + Math.random().toString(36).substring(2, 16);
70
+ }
71
+ // TODO: this doesn't belong in the optimizer
72
+ indexAlreadyExists(table, columns) {
73
+ return this.existingIndexes.find((index) => index.index_type === "btree" &&
74
+ index.table_name === table &&
75
+ index.index_columns.length === columns.length &&
76
+ index.index_columns.every((c, i) => columns[i].column === c.name));
77
+ }
78
+ /**
79
+ * Derive the list of indexes [tableA(X, Y, Z), tableB(H, I, J)]
80
+ **/
81
+ indexesToCreate(rootCandidates) {
82
+ const permutedIndexes = this.tableColumnIndexCandidates(rootCandidates);
83
+ const nextStage = [];
84
+ for (const { table, schema, columns } of permutedIndexes.values()) {
85
+ const permutations = permuteWithFeedback(columns);
86
+ let iter = permutations.next(PROCEED);
87
+ while (!iter.done) {
88
+ const columns = iter.value;
89
+ const existingIndex = this.indexAlreadyExists(table, columns);
90
+ if (existingIndex) {
91
+ iter = permutations.next(PROCEED);
92
+ continue;
93
+ }
94
+ const indexName = this.indexName();
95
+ const shortenedSchema = schema === "public" ? "" : `"${schema}".`;
96
+ // TODO: this is silly, turn this into a data structure here ONLY
97
+ const indexDefinitionClean = `${shortenedSchema}"${table}"(${columns
98
+ .map((c) => `"${c.column}"`)
99
+ .join(", ")})`;
100
+ iter = permutations.next(PROCEED);
101
+ nextStage.push({
102
+ name: indexName,
103
+ schema,
104
+ table,
105
+ columns,
106
+ definition: indexDefinitionClean,
107
+ });
108
+ }
109
+ }
110
+ return nextStage;
111
+ }
112
+ toDefinition(permuted) {
113
+ const make = (col, order, where, keyword) => {
114
+ // let clauses: string[] = [];
115
+ // const columns = [...permuted.columns];
116
+ // // TODO
117
+ // for (let i = columns.length - 1; i >= 0; i--) {
118
+ // const c = columns[i];
119
+ // const clause = this.whereClause(c, col, where);
120
+ // if (clause) {
121
+ // clauses.push(clause);
122
+ // // TODO: make this
123
+ // if (columns.length > 1) {
124
+ // columns.splice(i, 1);
125
+ // }
126
+ // }
127
+ // }
128
+ const baseColumn = `"${permuted.schema}"."${permuted.table}"(${permuted.columns
129
+ .map((c) => {
130
+ const direction = c.sort && this.sortDirection(c.sort);
131
+ const nulls = c.sort && this.nullsOrder(c.sort);
132
+ let sort = col(`"${c.column}"`);
133
+ if (direction) {
134
+ sort += ` ${order(direction)}`;
135
+ }
136
+ if (nulls) {
137
+ sort += ` ${order(nulls)}`;
138
+ }
139
+ return sort;
140
+ })
141
+ .join(", ")})`;
142
+ // TODO: add support for generating partial indexes
143
+ // if (clauses.length > 0) {
144
+ // return `${baseColumn} ${where("where")} ${clauses.join(" and ")}`;
145
+ // }
146
+ return baseColumn;
147
+ };
148
+ const id = (a) => a;
149
+ const raw = make(id, id, id, id);
150
+ const colored = make(green, yellow, magenta, blue);
151
+ return { raw, colored };
152
+ }
153
+ /**
154
+ * Drop indexes that can be dropped. Ignore the ones that can't
155
+ */
156
+ async dropExistingIndexes(tx) {
157
+ for (const index of this.existingIndexes) {
158
+ if (!isIndexProbablyDroppable(index)) {
159
+ continue;
160
+ }
161
+ const indexName = `${index.schema_name}.${index.index_name}`;
162
+ await dropIndex(tx, indexName);
163
+ }
164
+ }
165
+ whereClause(c, col, keyword) {
166
+ if (!c.where) {
167
+ return "";
168
+ }
169
+ if (c.where.nulltest === "IS_NULL") {
170
+ return `${col(`"${c.column}"`)} is ${keyword("null")}`;
171
+ }
172
+ if (c.where.nulltest === "IS_NOT_NULL") {
173
+ return `${col(`"${c.column}"`)} is not ${keyword("null")}`;
174
+ }
175
+ return "";
176
+ }
177
+ nullsOrder(s) {
178
+ if (!s.nulls) {
179
+ return "";
180
+ }
181
+ switch (s.nulls) {
182
+ case "SORTBY_NULLS_FIRST":
183
+ return "nulls first";
184
+ case "SORTBY_NULLS_LAST":
185
+ return "nulls last";
186
+ case "SORTBY_NULLS_DEFAULT":
187
+ default:
188
+ return "";
189
+ }
190
+ }
191
+ sortDirection(s) {
192
+ if (!s.dir) {
193
+ return "";
194
+ }
195
+ switch (s.dir) {
196
+ case "SORTBY_DESC":
197
+ return "desc";
198
+ case "SORTBY_ASC":
199
+ return "asc";
200
+ case "SORTBY_DEFAULT":
201
+ // god help us if we ever run into this
202
+ case "SORTBY_USING":
203
+ default:
204
+ return "";
205
+ }
206
+ }
207
+ async testQueryWithStats(builder, f, options) {
208
+ try {
209
+ await this.db.transaction(async (tx) => {
210
+ await f?.(tx);
211
+ await this.statistics.restoreStats(tx);
212
+ const flags = ["format json", "trace"];
213
+ if (options && !options.genericPlan) {
214
+ flags.push("analyze");
215
+ }
216
+ else {
217
+ flags.push("generic_plan");
218
+ }
219
+ const { commands, query } = builder.explain(flags).buildParts();
220
+ // this is done in a separate step to prevent sending multiple commands when using parameters
221
+ await tx.exec(commands);
222
+ const result = await tx.exec(query, options?.params);
223
+ const explain = result[0]["QUERY PLAN"][0];
224
+ throw new RollbackError(explain);
225
+ });
226
+ }
227
+ catch (error) {
228
+ if (error instanceof RollbackError) {
229
+ return error.value;
230
+ }
231
+ throw error;
232
+ }
233
+ throw new Error("Unreachable");
234
+ }
235
+ tableColumnIndexCandidates(indexes) {
236
+ const tableColumns = new Map();
237
+ for (const index of indexes) {
238
+ const existing = tableColumns.get(`${index.schema}.${index.table}`);
239
+ if (existing) {
240
+ existing.columns.push(index);
241
+ }
242
+ else {
243
+ tableColumns.set(`${index.schema}.${index.table}`, {
244
+ table: index.table,
245
+ schema: index.schema,
246
+ columns: [index],
247
+ });
248
+ }
249
+ }
250
+ return tableColumns;
251
+ }
252
+ findUsedIndexes(explain) {
253
+ const newIndexes = new Set();
254
+ const existingIndexes = new Set();
255
+ function go(plan) {
256
+ const indexName = plan["Index Name"];
257
+ if (indexName) {
258
+ if (indexName.startsWith(IndexOptimizer.prefix)) {
259
+ newIndexes.add(indexName);
260
+ }
261
+ else {
262
+ existingIndexes.add(indexName);
263
+ }
264
+ }
265
+ if (plan.Plans) {
266
+ for (const p of plan.Plans) {
267
+ go(p);
268
+ }
269
+ }
270
+ }
271
+ go(explain);
272
+ return {
273
+ newIndexes,
274
+ existingIndexes,
275
+ };
276
+ }
277
+ }
278
+ class RollbackError {
279
+ value;
280
+ constructor(value) {
281
+ this.value = value;
282
+ }
283
+ }
284
+ export const PROCEED = Symbol("PROCEED");
285
+ export const SKIP = Symbol("SKIP");
286
+ /**
287
+ * Allows permuting over an array of items.
288
+ * The generator allows the caller to prematurely stop the permutation chain.
289
+ */
290
+ export function* permuteWithFeedback(arr) {
291
+ function* helper(path, rest) {
292
+ let i = 0;
293
+ while (i < rest.length) {
294
+ const nextPath = [...path, rest[i]];
295
+ const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];
296
+ const input = yield nextPath;
297
+ if (input === PROCEED) {
298
+ yield* helper(nextPath, nextRest);
299
+ }
300
+ i++;
301
+ }
302
+ }
303
+ yield* helper([], arr);
304
+ }
@@ -71,12 +71,6 @@ export declare const ExportedStatsColumns: z.ZodObject<{
71
71
  stavalues4: z.ZodNullable<z.ZodArray<z.ZodAny>>;
72
72
  stavalues5: z.ZodNullable<z.ZodArray<z.ZodAny>>;
73
73
  }, z.core.$strip>>;
74
- dataType: z.ZodString;
75
- isNullable: z.ZodBoolean;
76
- numericScale: z.ZodNullable<z.ZodNumber>;
77
- columnDefault: z.ZodNullable<z.ZodString>;
78
- numericPrecision: z.ZodNullable<z.ZodNumber>;
79
- characterMaximumLength: z.ZodNullable<z.ZodNumber>;
80
74
  }, z.core.$strip>;
81
75
  export declare const ExportedStatsIndex: z.ZodObject<{
82
76
  indexName: z.ZodString;
@@ -125,12 +119,6 @@ export declare const ExportedStatsV1: z.ZodObject<{
125
119
  stavalues4: z.ZodNullable<z.ZodArray<z.ZodAny>>;
126
120
  stavalues5: z.ZodNullable<z.ZodArray<z.ZodAny>>;
127
121
  }, z.core.$strip>>;
128
- dataType: z.ZodString;
129
- isNullable: z.ZodBoolean;
130
- numericScale: z.ZodNullable<z.ZodNumber>;
131
- columnDefault: z.ZodNullable<z.ZodString>;
132
- numericPrecision: z.ZodNullable<z.ZodNumber>;
133
- characterMaximumLength: z.ZodNullable<z.ZodNumber>;
134
122
  }, z.core.$strip>>>;
135
123
  indexes: z.ZodArray<z.ZodObject<{
136
124
  indexName: z.ZodString;
@@ -180,12 +168,6 @@ export declare const ExportedStats: z.ZodUnion<readonly [z.ZodObject<{
180
168
  stavalues4: z.ZodNullable<z.ZodArray<z.ZodAny>>;
181
169
  stavalues5: z.ZodNullable<z.ZodArray<z.ZodAny>>;
182
170
  }, z.core.$strip>>;
183
- dataType: z.ZodString;
184
- isNullable: z.ZodBoolean;
185
- numericScale: z.ZodNullable<z.ZodNumber>;
186
- columnDefault: z.ZodNullable<z.ZodString>;
187
- numericPrecision: z.ZodNullable<z.ZodNumber>;
188
- characterMaximumLength: z.ZodNullable<z.ZodNumber>;
189
171
  }, z.core.$strip>>>;
190
172
  indexes: z.ZodArray<z.ZodObject<{
191
173
  indexName: z.ZodString;
@@ -242,12 +224,6 @@ export declare const StatisticsMode: z.ZodDiscriminatedUnion<[z.ZodObject<{
242
224
  stavalues4: z.ZodNullable<z.ZodArray<z.ZodAny>>;
243
225
  stavalues5: z.ZodNullable<z.ZodArray<z.ZodAny>>;
244
226
  }, z.core.$strip>>;
245
- dataType: z.ZodString;
246
- isNullable: z.ZodBoolean;
247
- numericScale: z.ZodNullable<z.ZodNumber>;
248
- columnDefault: z.ZodNullable<z.ZodString>;
249
- numericPrecision: z.ZodNullable<z.ZodNumber>;
250
- characterMaximumLength: z.ZodNullable<z.ZodNumber>;
251
227
  }, z.core.$strip>>>;
252
228
  indexes: z.ZodArray<z.ZodObject<{
253
229
  indexName: z.ZodString;
@@ -364,6 +340,7 @@ export type IndexedTable = {
364
340
  order: IndexOrder;
365
341
  }>;
366
342
  is_primary: boolean;
343
+ is_unique: boolean;
367
344
  index_name: string;
368
345
  index_type: "btree" | "gin" | (string & {});
369
346
  schema_name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"statistics.d.ts","sourceRoot":"","sources":["../../src/optimizer/statistics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"statistics.d.ts","sourceRoot":"","sources":["../../src/optimizer/statistics.ts"],"names":[],"mappings":"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"}