@query-doctor/core 0.2.4 → 0.3.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.
- package/dist/index.cjs +58 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +57 -32
- package/dist/index.js.map +1 -1
- package/dist/optimizer/genalgo.d.ts +1 -6
- package/dist/optimizer/genalgo.d.ts.map +1 -1
- package/dist/sql/builder.d.ts +6 -0
- package/dist/sql/builder.d.ts.map +1 -1
- package/dist/sql/permutations.d.ts +10 -0
- package/dist/sql/permutations.d.ts.map +1 -0
- package/dist/sql/pg-identifier.d.ts.map +1 -1
- package/package.json +7 -4
- package/dist/optimizer/pss-rewriter.test.d.ts +0 -2
- package/dist/optimizer/pss-rewriter.test.d.ts.map +0 -1
- package/dist/sql/analyzer.test.d.ts +0 -2
- package/dist/sql/analyzer.test.d.ts.map +0 -1
- package/dist/sql/permutations.test.d.ts +0 -2
- package/dist/sql/permutations.test.d.ts.map +0 -1
- package/dist/sql/pg-identifier.test.d.ts +0 -2
- package/dist/sql/pg-identifier.test.d.ts.map +0 -1
- package/dist/sql/walker.test.d.ts +0 -2
- package/dist/sql/walker.test.d.ts.map +0 -1
- package/tsconfig.json +0 -12
package/dist/index.cjs
CHANGED
|
@@ -53,8 +53,7 @@ __export(index_exports, {
|
|
|
53
53
|
ignoredIdentifier: () => ignoredIdentifier,
|
|
54
54
|
isIndexProbablyDroppable: () => isIndexProbablyDroppable,
|
|
55
55
|
isIndexSupported: () => isIndexSupported,
|
|
56
|
-
parseNudges: () => parseNudges
|
|
57
|
-
permuteWithFeedback: () => permuteWithFeedback
|
|
56
|
+
parseNudges: () => parseNudges
|
|
58
57
|
});
|
|
59
58
|
module.exports = __toCommonJS(index_exports);
|
|
60
59
|
|
|
@@ -830,6 +829,9 @@ var PostgresQueryBuilder = class _PostgresQueryBuilder {
|
|
|
830
829
|
__publicField(this, "isIntrospection", false);
|
|
831
830
|
__publicField(this, "explainFlags", []);
|
|
832
831
|
__publicField(this, "_preamble", 0);
|
|
832
|
+
__publicField(this, "parameters", {});
|
|
833
|
+
// substitution for `limit $1` -> `limit 5`
|
|
834
|
+
__publicField(this, "limitSubstitution");
|
|
833
835
|
}
|
|
834
836
|
get preamble() {
|
|
835
837
|
return this._preamble;
|
|
@@ -863,6 +865,14 @@ var PostgresQueryBuilder = class _PostgresQueryBuilder {
|
|
|
863
865
|
this.explainFlags = flags;
|
|
864
866
|
return this;
|
|
865
867
|
}
|
|
868
|
+
parameterize(parameters) {
|
|
869
|
+
Object.assign(this.parameters, parameters);
|
|
870
|
+
return this;
|
|
871
|
+
}
|
|
872
|
+
replaceLimit(limit) {
|
|
873
|
+
this.limitSubstitution = limit;
|
|
874
|
+
return this;
|
|
875
|
+
}
|
|
866
876
|
build() {
|
|
867
877
|
let commands = this.generateSetCommands();
|
|
868
878
|
commands += this.generateExplain().query;
|
|
@@ -891,14 +901,28 @@ var PostgresQueryBuilder = class _PostgresQueryBuilder {
|
|
|
891
901
|
return commands;
|
|
892
902
|
}
|
|
893
903
|
generateExplain() {
|
|
894
|
-
let
|
|
904
|
+
let finalQuery = "";
|
|
895
905
|
if (this.explainFlags.length > 0) {
|
|
896
|
-
|
|
906
|
+
finalQuery += `explain (${this.explainFlags.join(", ")}) `;
|
|
907
|
+
}
|
|
908
|
+
const query = this.substituteQuery();
|
|
909
|
+
const semicolon = query.endsWith(";") ? "" : ";";
|
|
910
|
+
const preamble = finalQuery.length;
|
|
911
|
+
finalQuery += `${query}${semicolon}`;
|
|
912
|
+
return { query: finalQuery, preamble };
|
|
913
|
+
}
|
|
914
|
+
substituteQuery() {
|
|
915
|
+
let query = this.query;
|
|
916
|
+
if (this.limitSubstitution !== void 0) {
|
|
917
|
+
query = query.replace(
|
|
918
|
+
/limit\s+\$\d+/g,
|
|
919
|
+
`limit ${this.limitSubstitution}`
|
|
920
|
+
);
|
|
897
921
|
}
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
return
|
|
922
|
+
for (const [key, value] of Object.entries(this.parameters)) {
|
|
923
|
+
query = query.replaceAll(`\\$${key}`, value.toString());
|
|
924
|
+
}
|
|
925
|
+
return query;
|
|
902
926
|
}
|
|
903
927
|
};
|
|
904
928
|
|
|
@@ -916,7 +940,9 @@ var _PgIdentifier = class _PgIdentifier {
|
|
|
916
940
|
const identifierRegex = /^[a-z_][a-zA-Z0-9_]*$/;
|
|
917
941
|
const match = identifier.match(/^"(.+)"$/);
|
|
918
942
|
if (match) {
|
|
919
|
-
|
|
943
|
+
const value = match[1];
|
|
944
|
+
const quoted2 = !identifierRegex.test(value) || this.reservedKeywords.has(value.toLowerCase());
|
|
945
|
+
return new _PgIdentifier(value, quoted2);
|
|
920
946
|
}
|
|
921
947
|
const quoted = !identifierRegex.test(identifier) || this.reservedKeywords.has(identifier.toLowerCase());
|
|
922
948
|
return new _PgIdentifier(identifier, quoted);
|
|
@@ -1121,6 +1147,24 @@ var PgIdentifier = _PgIdentifier;
|
|
|
1121
1147
|
|
|
1122
1148
|
// src/optimizer/genalgo.ts
|
|
1123
1149
|
var import_colorette2 = require("colorette");
|
|
1150
|
+
|
|
1151
|
+
// src/sql/permutations.ts
|
|
1152
|
+
function permutationsWithDescendingLength(arr) {
|
|
1153
|
+
const collected = [];
|
|
1154
|
+
function collect(path, rest) {
|
|
1155
|
+
for (let i = 0; i < rest.length; i++) {
|
|
1156
|
+
const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];
|
|
1157
|
+
const nextPath = [...path, rest[i]];
|
|
1158
|
+
collected.push(nextPath);
|
|
1159
|
+
collect(nextPath, nextRest);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
collect([], arr);
|
|
1163
|
+
collected.sort((a, b) => b.length - a.length);
|
|
1164
|
+
return collected;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/optimizer/genalgo.ts
|
|
1124
1168
|
var _IndexOptimizer = class _IndexOptimizer {
|
|
1125
1169
|
constructor(db, statistics, existingIndexes, config = {}) {
|
|
1126
1170
|
this.db = db;
|
|
@@ -1253,14 +1297,12 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
1253
1297
|
* Derive the list of indexes [tableA(X, Y, Z), tableB(H, I, J)]
|
|
1254
1298
|
**/
|
|
1255
1299
|
indexesToCreate(rootCandidates) {
|
|
1256
|
-
const permutedIndexes = this.
|
|
1300
|
+
const permutedIndexes = this.groupPotentialIndexColumnsByTable(rootCandidates);
|
|
1257
1301
|
const nextStage = [];
|
|
1258
1302
|
for (const permutation of permutedIndexes.values()) {
|
|
1259
1303
|
const { table: rawTable, schema: rawSchema, columns } = permutation;
|
|
1260
|
-
const permutations =
|
|
1261
|
-
|
|
1262
|
-
while (!iter.done) {
|
|
1263
|
-
const columns2 = iter.value;
|
|
1304
|
+
const permutations = permutationsWithDescendingLength(columns);
|
|
1305
|
+
for (const columns2 of permutations) {
|
|
1264
1306
|
const schema = PgIdentifier.fromString(rawSchema);
|
|
1265
1307
|
const table = PgIdentifier.fromString(rawTable);
|
|
1266
1308
|
const existingIndex = this.indexAlreadyExists(
|
|
@@ -1268,12 +1310,10 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
1268
1310
|
columns2
|
|
1269
1311
|
);
|
|
1270
1312
|
if (existingIndex) {
|
|
1271
|
-
iter = permutations.next(PROCEED);
|
|
1272
1313
|
continue;
|
|
1273
1314
|
}
|
|
1274
1315
|
const indexName = this.indexName();
|
|
1275
1316
|
const definition = this.toDefinition({ table, schema, columns: columns2 }).raw;
|
|
1276
|
-
iter = permutations.next(PROCEED);
|
|
1277
1317
|
nextStage.push({
|
|
1278
1318
|
name: indexName,
|
|
1279
1319
|
schema: schema.toString(),
|
|
@@ -1405,7 +1445,7 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
1405
1445
|
}
|
|
1406
1446
|
throw new Error("Unreachable");
|
|
1407
1447
|
}
|
|
1408
|
-
|
|
1448
|
+
groupPotentialIndexColumnsByTable(indexes) {
|
|
1409
1449
|
const tableColumns = /* @__PURE__ */ new Map();
|
|
1410
1450
|
for (const index of indexes) {
|
|
1411
1451
|
const existing = tableColumns.get(`${index.schema}.${index.table}`);
|
|
@@ -1475,21 +1515,6 @@ var RollbackError = class {
|
|
|
1475
1515
|
};
|
|
1476
1516
|
var PROCEED = Symbol("PROCEED");
|
|
1477
1517
|
var SKIP = Symbol("SKIP");
|
|
1478
|
-
function* permuteWithFeedback(arr) {
|
|
1479
|
-
function* helper(path, rest) {
|
|
1480
|
-
let i = 0;
|
|
1481
|
-
while (i < rest.length) {
|
|
1482
|
-
const nextPath = [...path, rest[i]];
|
|
1483
|
-
const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];
|
|
1484
|
-
const input = yield nextPath;
|
|
1485
|
-
if (input === PROCEED) {
|
|
1486
|
-
yield* helper(nextPath, nextRest);
|
|
1487
|
-
}
|
|
1488
|
-
i++;
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
yield* helper([], arr);
|
|
1492
|
-
}
|
|
1493
1518
|
|
|
1494
1519
|
// src/optimizer/statistics.ts
|
|
1495
1520
|
var import_colorette3 = require("colorette");
|
|
@@ -2206,7 +2231,6 @@ var PssRewriter = class {
|
|
|
2206
2231
|
ignoredIdentifier,
|
|
2207
2232
|
isIndexProbablyDroppable,
|
|
2208
2233
|
isIndexSupported,
|
|
2209
|
-
parseNudges
|
|
2210
|
-
permuteWithFeedback
|
|
2234
|
+
parseNudges
|
|
2211
2235
|
});
|
|
2212
2236
|
//# sourceMappingURL=index.cjs.map
|