@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.
- package/dist/index.cjs +222 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +218 -97
- package/dist/index.js.map +1 -1
- package/dist/optimizer/genalgo.d.ts +9 -5
- package/dist/optimizer/genalgo.d.ts.map +1 -1
- package/dist/optimizer/genalgo.js +304 -0
- package/dist/optimizer/statistics.d.ts +1 -24
- package/dist/optimizer/statistics.d.ts.map +1 -1
- package/dist/optimizer/statistics.js +700 -0
- package/dist/package.json +25 -0
- package/dist/sql/analyzer.d.ts +3 -3
- package/dist/sql/analyzer.js +270 -0
- package/dist/sql/analyzer.test.d.ts +2 -0
- package/dist/sql/analyzer.test.d.ts.map +1 -0
- package/dist/sql/analyzer_test.js +584 -0
- package/dist/sql/builder.js +77 -0
- package/dist/sql/database.d.ts +5 -0
- package/dist/sql/database.d.ts.map +1 -1
- package/dist/sql/database.js +20 -0
- package/dist/sql/indexes.d.ts +8 -0
- package/dist/sql/indexes.d.ts.map +1 -0
- package/dist/sql/indexes.js +12 -0
- package/dist/sql/nudges.js +241 -0
- package/dist/sql/permutations.test.d.ts +2 -0
- package/dist/sql/permutations.test.d.ts.map +1 -0
- package/dist/sql/permutations_test.js +53 -0
- package/dist/sql/pg-identifier.d.ts +9 -0
- package/dist/sql/pg-identifier.d.ts.map +1 -0
- package/dist/sql/pg-identifier.test.d.ts +2 -0
- package/dist/sql/pg-identifier.test.d.ts.map +1 -0
- package/dist/sql/walker.d.ts +2 -2
- package/dist/sql/walker.js +295 -0
- package/package.json +2 -2
- package/dist/index.mjs +0 -24297
- package/dist/index.mjs.map +0 -1
- package/dist/sql/schema_dump.d.ts +0 -132
- package/dist/sql/schema_dump.d.ts.map +0 -1
- package/dist/sql/trace.d.ts +0 -1
- 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.
|
|
2
|
+
import type { SortContext } from "../sql/analyzer.js";
|
|
3
3
|
import { PostgresQueryBuilder } from "../sql/builder.js";
|
|
4
|
-
import type
|
|
5
|
-
import type { IndexedTable, Statistics } from "./statistics.
|
|
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
|
|
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":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"genalgo.d.ts","sourceRoot":"","sources":["../../src/optimizer/genalgo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAEL,KAAK,QAAQ,EAEb,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AAErC,MAAM,MAAM,mBAAmB,GAAG,sBAAsB,GAAG;IACzD,UAAU,EAAE,eAAe,CAAC;CAC7B,CAAC;AASF,qBAAa,cAAc;IAIvB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,MAAM,CAAC,MAAM,SAAW;gBAGL,EAAE,EAAE,QAAQ,EACZ,UAAU,EAAE,UAAU,EAC/B,eAAe,EAAE,YAAY,EAAE,EACtB,MAAM,GAAE;QACvB,KAAK,CAAC,EAAE,OAAO,CAAC;KACZ;IAGF,GAAG,CACP,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,kBAAkB,EAAE,EAC7B,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,GACvD,OAAO,CAAC,cAAc,CAAC;IA6DpB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB;cA2KlC,GAAG;;IArKtB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,kBAAkB;IAa1B;;QAEI;IACJ,OAAO,CAAC,eAAe;IAmCvB,OAAO,CAAC,YAAY;IA0CpB;;OAEG;YACW,mBAAmB;IAUjC,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,aAAa;IAiBf,kBAAkB,CACtB,OAAO,EAAE,oBAAoB,EAC7B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,EAC9C,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GACtD,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC;IA8BzB,OAAO,CAAC,0BAA0B;IAoBlC,OAAO,CAAC,eAAe;CAwBxB;AAED,MAAM,MAAM,cAAc,GACtB;IACE,IAAI,EAAE,IAAI,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC/C,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAMN,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAE9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,OAAO,eAAoB,CAAC;AACzC,eAAO,MAAM,IAAI,eAAiB,CAAC;AAEnC;;;GAGG;AACH,wBAAiB,mBAAmB,CAAC,CAAC,EACpC,GAAG,EAAE,CAAC,EAAE,GACP,SAAS,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,OAAO,GAAG,OAAO,IAAI,CAAC,CAoBpD"}
|
|
@@ -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":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,QAAQ,EACR,mBAAmB,EACnB,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,eAAO,MAAM,gBAAgB;;;;;mBAQ3B,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkClC,CAAC;AAEH,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"statistics.d.ts","sourceRoot":"","sources":["../../src/optimizer/statistics.ts"],"names":[],"mappings":"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"}
|