@query-doctor/core 0.2.3 → 0.2.5

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.js CHANGED
@@ -866,7 +866,9 @@ var _PgIdentifier = class _PgIdentifier {
866
866
  const identifierRegex = /^[a-z_][a-zA-Z0-9_]*$/;
867
867
  const match = identifier.match(/^"(.+)"$/);
868
868
  if (match) {
869
- return new _PgIdentifier(match[1], true);
869
+ const value = match[1];
870
+ const quoted2 = !identifierRegex.test(value) || this.reservedKeywords.has(value.toLowerCase());
871
+ return new _PgIdentifier(value, quoted2);
870
872
  }
871
873
  const quoted = !identifierRegex.test(identifier) || this.reservedKeywords.has(identifier.toLowerCase());
872
874
  return new _PgIdentifier(identifier, quoted);
@@ -1071,6 +1073,24 @@ var PgIdentifier = _PgIdentifier;
1071
1073
 
1072
1074
  // src/optimizer/genalgo.ts
1073
1075
  import { blue as blue2, gray, green, magenta, red, yellow } from "colorette";
1076
+
1077
+ // src/sql/permutations.ts
1078
+ function permutationsWithDescendingLength(arr) {
1079
+ const collected = [];
1080
+ function collect(path, rest) {
1081
+ for (let i = 0; i < rest.length; i++) {
1082
+ const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];
1083
+ const nextPath = [...path, rest[i]];
1084
+ collected.push(nextPath);
1085
+ collect(nextPath, nextRest);
1086
+ }
1087
+ }
1088
+ collect([], arr);
1089
+ collected.sort((a, b) => b.length - a.length);
1090
+ return collected;
1091
+ }
1092
+
1093
+ // src/optimizer/genalgo.ts
1074
1094
  var _IndexOptimizer = class _IndexOptimizer {
1075
1095
  constructor(db, statistics, existingIndexes, config = {}) {
1076
1096
  this.db = db;
@@ -1172,21 +1192,43 @@ var _IndexOptimizer = class _IndexOptimizer {
1172
1192
  // TODO: this doesn't belong in the optimizer
1173
1193
  indexAlreadyExists(table, columns) {
1174
1194
  return this.existingIndexes.find(
1175
- (index) => index.index_type === "btree" && index.table_name === table && index.index_columns.length === columns.length && index.index_columns.every((c, i) => columns[i].column === c.name)
1195
+ (index) => index.index_type === "btree" && index.table_name === table && index.index_columns.length === columns.length && index.index_columns.every((c, i) => {
1196
+ if (columns[i].column !== c.name) {
1197
+ return false;
1198
+ }
1199
+ if (columns[i].where) {
1200
+ return false;
1201
+ }
1202
+ if (columns[i].sort) {
1203
+ switch (columns[i].sort.dir) {
1204
+ // Sorting is ASC by default in postgres
1205
+ case "SORTBY_DEFAULT":
1206
+ case "SORTBY_ASC":
1207
+ if (c.order !== "ASC") {
1208
+ return false;
1209
+ }
1210
+ break;
1211
+ case "SORTBY_DESC":
1212
+ if (c.order !== "DESC") {
1213
+ return false;
1214
+ }
1215
+ break;
1216
+ }
1217
+ }
1218
+ return true;
1219
+ })
1176
1220
  );
1177
1221
  }
1178
1222
  /**
1179
1223
  * Derive the list of indexes [tableA(X, Y, Z), tableB(H, I, J)]
1180
1224
  **/
1181
1225
  indexesToCreate(rootCandidates) {
1182
- const permutedIndexes = this.tableColumnIndexCandidates(rootCandidates);
1226
+ const permutedIndexes = this.groupPotentialIndexColumnsByTable(rootCandidates);
1183
1227
  const nextStage = [];
1184
1228
  for (const permutation of permutedIndexes.values()) {
1185
1229
  const { table: rawTable, schema: rawSchema, columns } = permutation;
1186
- const permutations = permuteWithFeedback(columns);
1187
- let iter = permutations.next(PROCEED);
1188
- while (!iter.done) {
1189
- const columns2 = iter.value;
1230
+ const permutations = permutationsWithDescendingLength(columns);
1231
+ for (const columns2 of permutations) {
1190
1232
  const schema = PgIdentifier.fromString(rawSchema);
1191
1233
  const table = PgIdentifier.fromString(rawTable);
1192
1234
  const existingIndex = this.indexAlreadyExists(
@@ -1194,12 +1236,10 @@ var _IndexOptimizer = class _IndexOptimizer {
1194
1236
  columns2
1195
1237
  );
1196
1238
  if (existingIndex) {
1197
- iter = permutations.next(PROCEED);
1198
1239
  continue;
1199
1240
  }
1200
1241
  const indexName = this.indexName();
1201
1242
  const definition = this.toDefinition({ table, schema, columns: columns2 }).raw;
1202
- iter = permutations.next(PROCEED);
1203
1243
  nextStage.push({
1204
1244
  name: indexName,
1205
1245
  schema: schema.toString(),
@@ -1331,7 +1371,7 @@ var _IndexOptimizer = class _IndexOptimizer {
1331
1371
  }
1332
1372
  throw new Error("Unreachable");
1333
1373
  }
1334
- tableColumnIndexCandidates(indexes) {
1374
+ groupPotentialIndexColumnsByTable(indexes) {
1335
1375
  const tableColumns = /* @__PURE__ */ new Map();
1336
1376
  for (const index of indexes) {
1337
1377
  const existing = tableColumns.get(`${index.schema}.${index.table}`);
@@ -1401,21 +1441,6 @@ var RollbackError = class {
1401
1441
  };
1402
1442
  var PROCEED = Symbol("PROCEED");
1403
1443
  var SKIP = Symbol("SKIP");
1404
- function* permuteWithFeedback(arr) {
1405
- function* helper(path, rest) {
1406
- let i = 0;
1407
- while (i < rest.length) {
1408
- const nextPath = [...path, rest[i]];
1409
- const nextRest = [...rest.slice(0, i), ...rest.slice(i + 1)];
1410
- const input = yield nextPath;
1411
- if (input === PROCEED) {
1412
- yield* helper(nextPath, nextRest);
1413
- }
1414
- i++;
1415
- }
1416
- }
1417
- yield* helper([], arr);
1418
- }
1419
1444
 
1420
1445
  // src/optimizer/statistics.ts
1421
1446
  import { gray as gray2 } from "colorette";
@@ -2131,7 +2156,6 @@ export {
2131
2156
  ignoredIdentifier,
2132
2157
  isIndexProbablyDroppable,
2133
2158
  isIndexSupported,
2134
- parseNudges,
2135
- permuteWithFeedback
2159
+ parseNudges
2136
2160
  };
2137
2161
  //# sourceMappingURL=index.js.map