@query-doctor/core 0.0.3 → 0.0.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.cjs +226 -98
- 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 +222 -98
- package/dist/index.js.map +1 -1
- package/dist/optimizer/genalgo.d.ts +10 -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 +4 -4
- 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
package/dist/index.cjs
CHANGED
|
@@ -41,13 +41,17 @@ __export(index_exports, {
|
|
|
41
41
|
ExportedStatsV1: () => ExportedStatsV1,
|
|
42
42
|
IndexOptimizer: () => IndexOptimizer,
|
|
43
43
|
PROCEED: () => PROCEED,
|
|
44
|
+
PgIdentifier: () => PgIdentifier,
|
|
44
45
|
PostgresQueryBuilder: () => PostgresQueryBuilder,
|
|
45
46
|
PostgresVersion: () => PostgresVersion,
|
|
46
47
|
SKIP: () => SKIP,
|
|
47
48
|
Statistics: () => Statistics,
|
|
48
49
|
StatisticsMode: () => StatisticsMode,
|
|
49
50
|
StatisticsSource: () => StatisticsSource,
|
|
51
|
+
dropIndex: () => dropIndex,
|
|
50
52
|
ignoredIdentifier: () => ignoredIdentifier,
|
|
53
|
+
isIndexProbablyDroppable: () => isIndexProbablyDroppable,
|
|
54
|
+
isIndexSupported: () => isIndexSupported,
|
|
51
55
|
parseNudges: () => parseNudges,
|
|
52
56
|
permuteWithFeedback: () => permuteWithFeedback
|
|
53
57
|
});
|
|
@@ -749,9 +753,26 @@ var Analyzer = class {
|
|
|
749
753
|
// src/sql/database.ts
|
|
750
754
|
var import_zod = require("zod");
|
|
751
755
|
var PostgresVersion = import_zod.z.string().brand("PostgresVersion");
|
|
756
|
+
async function dropIndex(tx, index) {
|
|
757
|
+
try {
|
|
758
|
+
await tx.exec(`
|
|
759
|
+
savepoint idx_drop;
|
|
760
|
+
drop index if exists ${index} cascade;
|
|
761
|
+
`);
|
|
762
|
+
return true;
|
|
763
|
+
} catch (error) {
|
|
764
|
+
await tx.exec(`rollback to idx_drop`);
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
752
768
|
|
|
753
|
-
// src/
|
|
754
|
-
|
|
769
|
+
// src/sql/indexes.ts
|
|
770
|
+
function isIndexSupported(index) {
|
|
771
|
+
return index.index_type === "btree";
|
|
772
|
+
}
|
|
773
|
+
function isIndexProbablyDroppable(index) {
|
|
774
|
+
return !index.is_primary && !index.is_unique;
|
|
775
|
+
}
|
|
755
776
|
|
|
756
777
|
// src/sql/builder.ts
|
|
757
778
|
var PostgresQueryBuilder = class _PostgresQueryBuilder {
|
|
@@ -833,7 +854,141 @@ var PostgresQueryBuilder = class _PostgresQueryBuilder {
|
|
|
833
854
|
}
|
|
834
855
|
};
|
|
835
856
|
|
|
857
|
+
// src/sql/pg-identifier.ts
|
|
858
|
+
var _PgIdentifier = class _PgIdentifier {
|
|
859
|
+
constructor(value, quoted) {
|
|
860
|
+
this.value = value;
|
|
861
|
+
this.quoted = quoted;
|
|
862
|
+
}
|
|
863
|
+
static fromString(identifier) {
|
|
864
|
+
const identifierRegex = /^[a-z_][a-zA-Z0-9_]*$/;
|
|
865
|
+
const match = identifier.match(/^"(.+)"$/);
|
|
866
|
+
if (match) {
|
|
867
|
+
return new _PgIdentifier(match[1], true);
|
|
868
|
+
}
|
|
869
|
+
const quoted = !identifierRegex.test(identifier) || this.reservedKeywords.has(identifier.toLowerCase());
|
|
870
|
+
return new _PgIdentifier(identifier, quoted);
|
|
871
|
+
}
|
|
872
|
+
toString() {
|
|
873
|
+
if (this.quoted) {
|
|
874
|
+
return `"${this.value.replace(/"/g, '""')}"`;
|
|
875
|
+
}
|
|
876
|
+
return this.value;
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
__publicField(_PgIdentifier, "reservedKeywords", /* @__PURE__ */ new Set([
|
|
880
|
+
"abort",
|
|
881
|
+
"analyze",
|
|
882
|
+
"binary",
|
|
883
|
+
"cluster",
|
|
884
|
+
"constraint",
|
|
885
|
+
"copy",
|
|
886
|
+
"do",
|
|
887
|
+
"explain",
|
|
888
|
+
"extend",
|
|
889
|
+
"listen",
|
|
890
|
+
"load",
|
|
891
|
+
"lock",
|
|
892
|
+
"move",
|
|
893
|
+
"new",
|
|
894
|
+
"none",
|
|
895
|
+
"notify",
|
|
896
|
+
"offset",
|
|
897
|
+
"reset",
|
|
898
|
+
"setof",
|
|
899
|
+
"show",
|
|
900
|
+
"unlisten",
|
|
901
|
+
"until",
|
|
902
|
+
"vacuum",
|
|
903
|
+
"verbose",
|
|
904
|
+
"all",
|
|
905
|
+
"any",
|
|
906
|
+
"asc",
|
|
907
|
+
"between",
|
|
908
|
+
"bit",
|
|
909
|
+
"both",
|
|
910
|
+
"case",
|
|
911
|
+
"cast",
|
|
912
|
+
"char",
|
|
913
|
+
"character",
|
|
914
|
+
"check",
|
|
915
|
+
"coalesce",
|
|
916
|
+
"collate",
|
|
917
|
+
"column",
|
|
918
|
+
"constraint",
|
|
919
|
+
"cross",
|
|
920
|
+
"current",
|
|
921
|
+
"current_date",
|
|
922
|
+
"current_time",
|
|
923
|
+
"current_timestamp",
|
|
924
|
+
"current_user",
|
|
925
|
+
"dec",
|
|
926
|
+
"decimal",
|
|
927
|
+
"default",
|
|
928
|
+
"desc",
|
|
929
|
+
"distinct",
|
|
930
|
+
"else",
|
|
931
|
+
"end",
|
|
932
|
+
"except",
|
|
933
|
+
"exists",
|
|
934
|
+
"extract",
|
|
935
|
+
"false",
|
|
936
|
+
"float",
|
|
937
|
+
"for",
|
|
938
|
+
"foreign",
|
|
939
|
+
"from",
|
|
940
|
+
"full",
|
|
941
|
+
"global",
|
|
942
|
+
"group",
|
|
943
|
+
"having",
|
|
944
|
+
"in",
|
|
945
|
+
"inner",
|
|
946
|
+
"intersect",
|
|
947
|
+
"into",
|
|
948
|
+
"is",
|
|
949
|
+
"join",
|
|
950
|
+
"leading",
|
|
951
|
+
"left",
|
|
952
|
+
"like",
|
|
953
|
+
"local",
|
|
954
|
+
"natural",
|
|
955
|
+
"nchar",
|
|
956
|
+
"not",
|
|
957
|
+
"null",
|
|
958
|
+
"nullif",
|
|
959
|
+
"numeric",
|
|
960
|
+
"on",
|
|
961
|
+
"or",
|
|
962
|
+
"order",
|
|
963
|
+
"outer",
|
|
964
|
+
"overlaps",
|
|
965
|
+
"position",
|
|
966
|
+
"precision",
|
|
967
|
+
"primary",
|
|
968
|
+
"public",
|
|
969
|
+
"references",
|
|
970
|
+
"right",
|
|
971
|
+
"select",
|
|
972
|
+
"session_user",
|
|
973
|
+
"some",
|
|
974
|
+
"substring",
|
|
975
|
+
"table",
|
|
976
|
+
"then",
|
|
977
|
+
"to",
|
|
978
|
+
"transaction",
|
|
979
|
+
"trim",
|
|
980
|
+
"true",
|
|
981
|
+
"union",
|
|
982
|
+
"unique",
|
|
983
|
+
"user",
|
|
984
|
+
"varchar",
|
|
985
|
+
"when",
|
|
986
|
+
"where"
|
|
987
|
+
]));
|
|
988
|
+
var PgIdentifier = _PgIdentifier;
|
|
989
|
+
|
|
836
990
|
// src/optimizer/genalgo.ts
|
|
991
|
+
var import_colorette2 = require("colorette");
|
|
837
992
|
var _IndexOptimizer = class _IndexOptimizer {
|
|
838
993
|
constructor(db, statistics, existingIndexes, config = {}) {
|
|
839
994
|
this.db = db;
|
|
@@ -841,8 +996,12 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
841
996
|
this.existingIndexes = existingIndexes;
|
|
842
997
|
this.config = config;
|
|
843
998
|
}
|
|
844
|
-
async run(builder, indexes) {
|
|
845
|
-
const baseExplain = await this.
|
|
999
|
+
async run(builder, indexes, beforeQuery) {
|
|
1000
|
+
const baseExplain = await this.testQueryWithStats(builder, async (tx) => {
|
|
1001
|
+
if (beforeQuery) {
|
|
1002
|
+
await beforeQuery(tx);
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
846
1005
|
const baseCost = Number(baseExplain.Plan["Total Cost"]);
|
|
847
1006
|
if (baseCost === 0) {
|
|
848
1007
|
return {
|
|
@@ -850,76 +1009,17 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
850
1009
|
explainPlan: baseExplain
|
|
851
1010
|
};
|
|
852
1011
|
}
|
|
853
|
-
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
for (const { table, schema, columns } of permutedIndexes.values()) {
|
|
858
|
-
const permutations = permuteWithFeedback(columns);
|
|
859
|
-
let iter = permutations.next(PROCEED);
|
|
860
|
-
const previousCost = baseCost;
|
|
861
|
-
while (!iter.done) {
|
|
862
|
-
const columns2 = iter.value;
|
|
863
|
-
const existingIndex = this.indexAlreadyExists(table, columns2);
|
|
864
|
-
if (existingIndex) {
|
|
865
|
-
console.log(` <${(0, import_colorette2.gray)("skip")}> ${(0, import_colorette2.gray)(existingIndex.index_name)}`);
|
|
866
|
-
iter = permutations.next(PROCEED);
|
|
867
|
-
continue;
|
|
868
|
-
}
|
|
869
|
-
let indexDefinition = "?";
|
|
870
|
-
const indexName = this.indexName();
|
|
871
|
-
const { raw, colored } = this.toDefinition({
|
|
872
|
-
columns: columns2,
|
|
873
|
-
schema,
|
|
874
|
-
table
|
|
875
|
-
});
|
|
876
|
-
const shortenedSchema = schema === "public" ? "" : `"${schema}".`;
|
|
877
|
-
const indexDefinitionClean = `${shortenedSchema}"${table}"(${columns2.map((c) => `"${c.column}"`).join(", ")})`;
|
|
878
|
-
indexDefinition = colored;
|
|
879
|
-
const query = PostgresQueryBuilder.createIndex(
|
|
880
|
-
raw,
|
|
881
|
-
indexName
|
|
882
|
-
).introspect();
|
|
883
|
-
triedIndexes.set(indexName, {
|
|
884
|
-
schema,
|
|
885
|
-
table,
|
|
886
|
-
columns: columns2,
|
|
887
|
-
definition: indexDefinitionClean
|
|
888
|
-
});
|
|
889
|
-
const explain = await this.testQueryWithStats(builder, async (sql) => {
|
|
890
|
-
await sql.exec(query.build());
|
|
891
|
-
});
|
|
892
|
-
const explainCost = Number(explain.Plan["Total Cost"]);
|
|
893
|
-
const costDeltaPercentage = (previousCost - explainCost) / previousCost * 100;
|
|
894
|
-
if (previousCost > explainCost) {
|
|
895
|
-
console.log(
|
|
896
|
-
`${(0, import_colorette2.green)(
|
|
897
|
-
`+${costDeltaPercentage.toFixed(2).padStart(5, "0")}%`
|
|
898
|
-
)} ${indexDefinition} `
|
|
899
|
-
);
|
|
900
|
-
iter = permutations.next(PROCEED);
|
|
901
|
-
} else {
|
|
902
|
-
console.log(
|
|
903
|
-
`${previousCost === explainCost ? ` ${(0, import_colorette2.gray)("00.00%")}` : `${(0, import_colorette2.red)(
|
|
904
|
-
`-${Math.abs(costDeltaPercentage).toFixed(2).padStart(5, "0")}%`
|
|
905
|
-
)}`} ${indexDefinition}`
|
|
906
|
-
);
|
|
907
|
-
iter = permutations.next(PROCEED);
|
|
908
|
-
}
|
|
909
|
-
nextStage.push({
|
|
910
|
-
name: indexName,
|
|
911
|
-
schema,
|
|
912
|
-
table,
|
|
913
|
-
columns: columns2
|
|
914
|
-
});
|
|
1012
|
+
const toCreate = this.indexesToCreate(indexes);
|
|
1013
|
+
const finalExplain = await this.testQueryWithStats(builder, async (tx) => {
|
|
1014
|
+
if (beforeQuery) {
|
|
1015
|
+
await beforeQuery(tx);
|
|
915
1016
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
);
|
|
1017
|
+
for (const permutation of toCreate) {
|
|
1018
|
+
const createIndex = PostgresQueryBuilder.createIndex(
|
|
1019
|
+
this.toDefinition(permutation).raw,
|
|
1020
|
+
permutation.name
|
|
1021
|
+
).introspect().build();
|
|
1022
|
+
await tx.exec(createIndex);
|
|
923
1023
|
}
|
|
924
1024
|
});
|
|
925
1025
|
const finalCost = Number(finalExplain.Plan["Total Cost"]);
|
|
@@ -938,14 +1038,15 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
938
1038
|
)} ${(0, import_colorette2.gray)("If there's a better index, we haven't tried it")}`
|
|
939
1039
|
);
|
|
940
1040
|
}
|
|
941
|
-
const
|
|
1041
|
+
const baseIndexes = this.findUsedIndexes(baseExplain.Plan);
|
|
1042
|
+
const finalIndexes = this.findUsedIndexes(finalExplain.Plan);
|
|
942
1043
|
return {
|
|
943
1044
|
kind: "ok",
|
|
944
1045
|
baseCost,
|
|
945
1046
|
finalCost,
|
|
946
|
-
newIndexes,
|
|
947
|
-
existingIndexes:
|
|
948
|
-
triedIndexes,
|
|
1047
|
+
newIndexes: finalIndexes.newIndexes,
|
|
1048
|
+
existingIndexes: baseIndexes.existingIndexes,
|
|
1049
|
+
triedIndexes: new Map(toCreate.map((index) => [index.name, index])),
|
|
949
1050
|
baseExplainPlan: baseExplain,
|
|
950
1051
|
explainPlan: finalExplain
|
|
951
1052
|
};
|
|
@@ -969,6 +1070,37 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
969
1070
|
(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)
|
|
970
1071
|
);
|
|
971
1072
|
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Derive the list of indexes [tableA(X, Y, Z), tableB(H, I, J)]
|
|
1075
|
+
**/
|
|
1076
|
+
indexesToCreate(rootCandidates) {
|
|
1077
|
+
const permutedIndexes = this.tableColumnIndexCandidates(rootCandidates);
|
|
1078
|
+
const nextStage = [];
|
|
1079
|
+
for (const { table, schema, columns } of permutedIndexes.values()) {
|
|
1080
|
+
const permutations = permuteWithFeedback(columns);
|
|
1081
|
+
let iter = permutations.next(PROCEED);
|
|
1082
|
+
while (!iter.done) {
|
|
1083
|
+
const columns2 = iter.value;
|
|
1084
|
+
const existingIndex = this.indexAlreadyExists(table, columns2);
|
|
1085
|
+
if (existingIndex) {
|
|
1086
|
+
iter = permutations.next(PROCEED);
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
const indexName = this.indexName();
|
|
1090
|
+
const shortenedSchema = schema === "public" ? "" : `"${schema}".`;
|
|
1091
|
+
const indexDefinitionClean = `${shortenedSchema}"${table}"(${columns2.map((c) => `"${c.column}"`).join(", ")})`;
|
|
1092
|
+
iter = permutations.next(PROCEED);
|
|
1093
|
+
nextStage.push({
|
|
1094
|
+
name: indexName,
|
|
1095
|
+
schema,
|
|
1096
|
+
table,
|
|
1097
|
+
columns: columns2,
|
|
1098
|
+
definition: indexDefinitionClean
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return nextStage;
|
|
1103
|
+
}
|
|
972
1104
|
toDefinition(permuted) {
|
|
973
1105
|
const make = (col, order, where, keyword) => {
|
|
974
1106
|
const baseColumn = `"${permuted.schema}"."${permuted.table}"(${permuted.columns.map((c) => {
|
|
@@ -991,16 +1123,15 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
991
1123
|
return { raw, colored };
|
|
992
1124
|
}
|
|
993
1125
|
/**
|
|
994
|
-
* Drop indexes that can be dropped
|
|
1126
|
+
* Drop indexes that can be dropped. Ignore the ones that can't
|
|
995
1127
|
*/
|
|
996
1128
|
async dropExistingIndexes(tx) {
|
|
997
1129
|
for (const index of this.existingIndexes) {
|
|
998
|
-
if (index
|
|
1130
|
+
if (!isIndexProbablyDroppable(index)) {
|
|
999
1131
|
continue;
|
|
1000
1132
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
);
|
|
1133
|
+
const indexName = `${index.schema_name}.${index.index_name}`;
|
|
1134
|
+
await dropIndex(tx, indexName);
|
|
1004
1135
|
}
|
|
1005
1136
|
}
|
|
1006
1137
|
whereClause(c, col, keyword) {
|
|
@@ -1050,9 +1181,12 @@ var _IndexOptimizer = class _IndexOptimizer {
|
|
|
1050
1181
|
await this.db.transaction(async (tx) => {
|
|
1051
1182
|
await f?.(tx);
|
|
1052
1183
|
await this.statistics.restoreStats(tx);
|
|
1053
|
-
const flags = ["format json"
|
|
1184
|
+
const flags = ["format json"];
|
|
1054
1185
|
if (options && !options.genericPlan) {
|
|
1055
1186
|
flags.push("analyze");
|
|
1187
|
+
if (this.config.trace) {
|
|
1188
|
+
flags.push("trace");
|
|
1189
|
+
}
|
|
1056
1190
|
} else {
|
|
1057
1191
|
flags.push("generic_plan");
|
|
1058
1192
|
}
|
|
@@ -1189,13 +1323,7 @@ var ExportedStatsStatistics = import_zod2.z.object({
|
|
|
1189
1323
|
});
|
|
1190
1324
|
var ExportedStatsColumns = import_zod2.z.object({
|
|
1191
1325
|
columnName: import_zod2.z.string(),
|
|
1192
|
-
stats: ExportedStatsStatistics.nullable()
|
|
1193
|
-
dataType: import_zod2.z.string(),
|
|
1194
|
-
isNullable: import_zod2.z.boolean(),
|
|
1195
|
-
numericScale: import_zod2.z.number().nullable(),
|
|
1196
|
-
columnDefault: import_zod2.z.string().nullable(),
|
|
1197
|
-
numericPrecision: import_zod2.z.number().nullable(),
|
|
1198
|
-
characterMaximumLength: import_zod2.z.number().nullable()
|
|
1326
|
+
stats: ExportedStatsStatistics.nullable()
|
|
1199
1327
|
});
|
|
1200
1328
|
var ExportedStatsIndex = import_zod2.z.object({
|
|
1201
1329
|
indexName: import_zod2.z.string(),
|
|
@@ -1663,12 +1791,6 @@ var _Statistics = class _Statistics {
|
|
|
1663
1791
|
json_agg(
|
|
1664
1792
|
json_build_object(
|
|
1665
1793
|
'columnName', c.column_name,
|
|
1666
|
-
'dataType', c.data_type,
|
|
1667
|
-
'isNullable', (c.is_nullable = 'YES')::boolean,
|
|
1668
|
-
'characterMaximumLength', c.character_maximum_length,
|
|
1669
|
-
'numericPrecision', c.numeric_precision,
|
|
1670
|
-
'numericScale', c.numeric_scale,
|
|
1671
|
-
'columnDefault', c.column_default,
|
|
1672
1794
|
'stats', (
|
|
1673
1795
|
SELECT json_build_object(
|
|
1674
1796
|
'starelid', s.starelid,
|
|
@@ -1764,6 +1886,7 @@ var _Statistics = class _Statistics {
|
|
|
1764
1886
|
COALESCE(pt.parent_table::text, t.relname) AS table_name,
|
|
1765
1887
|
i.relname AS index_name,
|
|
1766
1888
|
ix.indisprimary as is_primary,
|
|
1889
|
+
ix.indisunique as is_unique,
|
|
1767
1890
|
am.amname AS index_type,
|
|
1768
1891
|
array_agg(
|
|
1769
1892
|
CASE
|
|
@@ -1794,9 +1917,10 @@ var _Statistics = class _Statistics {
|
|
|
1794
1917
|
LEFT JOIN pg_attribute a ON a.attnum = k.attnum AND a.attrelid = t.oid
|
|
1795
1918
|
JOIN pg_namespace n ON t.relnamespace = n.oid
|
|
1796
1919
|
WHERE
|
|
1797
|
-
n.nspname
|
|
1920
|
+
n.nspname not like 'pg_%' and
|
|
1921
|
+
n.nspname <> 'information_schema'
|
|
1798
1922
|
GROUP BY
|
|
1799
|
-
n.nspname, COALESCE(pt.parent_table::text, t.relname), i.relname, am.amname, ix.indisprimary
|
|
1923
|
+
n.nspname, COALESCE(pt.parent_table::text, t.relname), i.relname, am.amname, ix.indisprimary, ix.indisunique
|
|
1800
1924
|
ORDER BY
|
|
1801
1925
|
COALESCE(pt.parent_table::text, t.relname), i.relname; -- @qd_introspection
|
|
1802
1926
|
`);
|
|
@@ -1820,13 +1944,17 @@ var Statistics = _Statistics;
|
|
|
1820
1944
|
ExportedStatsV1,
|
|
1821
1945
|
IndexOptimizer,
|
|
1822
1946
|
PROCEED,
|
|
1947
|
+
PgIdentifier,
|
|
1823
1948
|
PostgresQueryBuilder,
|
|
1824
1949
|
PostgresVersion,
|
|
1825
1950
|
SKIP,
|
|
1826
1951
|
Statistics,
|
|
1827
1952
|
StatisticsMode,
|
|
1828
1953
|
StatisticsSource,
|
|
1954
|
+
dropIndex,
|
|
1829
1955
|
ignoredIdentifier,
|
|
1956
|
+
isIndexProbablyDroppable,
|
|
1957
|
+
isIndexSupported,
|
|
1830
1958
|
parseNudges,
|
|
1831
1959
|
permuteWithFeedback
|
|
1832
1960
|
});
|