@tabularis/explain 0.3.0 → 0.3.1
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 +41 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -726,7 +726,10 @@ function parsePostgresText(raw) {
|
|
|
726
726
|
let planningTime = null;
|
|
727
727
|
let executionTime = null;
|
|
728
728
|
let hasAnalyzeData = false;
|
|
729
|
+
let hasBuffers = false;
|
|
729
730
|
const ids = new NodeIdAllocator();
|
|
731
|
+
let rootIndent = null;
|
|
732
|
+
let inTrailer = false;
|
|
730
733
|
const stack = [];
|
|
731
734
|
let root = null;
|
|
732
735
|
const promoteOrAttach = (node) => {
|
|
@@ -768,12 +771,16 @@ function parsePostgresText(raw) {
|
|
|
768
771
|
if (node.actual_rows !== null || node.actual_time_ms !== null) {
|
|
769
772
|
hasAnalyzeData = true;
|
|
770
773
|
}
|
|
774
|
+
rootIndent ??= indent;
|
|
775
|
+
inTrailer = false;
|
|
771
776
|
popUntilShallower(indent);
|
|
772
777
|
stack.push({ indent, node });
|
|
773
778
|
} else {
|
|
779
|
+
if (rootIndent !== null && indent <= rootIndent) inTrailer = true;
|
|
780
|
+
if (inTrailer) continue;
|
|
774
781
|
const top = stack[stack.length - 1];
|
|
775
|
-
if (top !== void 0) {
|
|
776
|
-
|
|
782
|
+
if (top !== void 0 && applyTextAttribute(top.node, trimmed)) {
|
|
783
|
+
hasBuffers = true;
|
|
777
784
|
}
|
|
778
785
|
}
|
|
779
786
|
}
|
|
@@ -781,6 +788,7 @@ function parsePostgresText(raw) {
|
|
|
781
788
|
if (root === null) {
|
|
782
789
|
throw new Error("No plan nodes found in EXPLAIN text output");
|
|
783
790
|
}
|
|
791
|
+
if (hasBuffers) fillMissingBuffers(root);
|
|
784
792
|
return {
|
|
785
793
|
root,
|
|
786
794
|
planning_time_ms: planningTime,
|
|
@@ -884,7 +892,7 @@ function parseCostFields(inner) {
|
|
|
884
892
|
function parseActualFields(inner) {
|
|
885
893
|
const trimmed = inner.trim();
|
|
886
894
|
if (trimmed.toLowerCase() === "never executed") {
|
|
887
|
-
return [
|
|
895
|
+
return [0, 0, 0];
|
|
888
896
|
}
|
|
889
897
|
const rest = trimmed.startsWith("actual ") ? trimmed.slice(7) : trimmed;
|
|
890
898
|
let totalTime = null;
|
|
@@ -911,6 +919,10 @@ function parseFiniteNumber(text) {
|
|
|
911
919
|
return Number.isFinite(value) ? value : null;
|
|
912
920
|
}
|
|
913
921
|
function applyTextAttribute(node, content) {
|
|
922
|
+
if (content.startsWith("Buffers:")) {
|
|
923
|
+
applyBuffers(node, content.slice("Buffers:".length));
|
|
924
|
+
return true;
|
|
925
|
+
}
|
|
914
926
|
if (content.startsWith("Filter:")) {
|
|
915
927
|
node.filter = content.slice("Filter:".length).trim();
|
|
916
928
|
} else if (content.startsWith("Index Cond:")) {
|
|
@@ -925,6 +937,32 @@ function applyTextAttribute(node, content) {
|
|
|
925
937
|
node.extra[content.slice(0, sep).trim()] = content.slice(sep + 1).trim();
|
|
926
938
|
}
|
|
927
939
|
}
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
function applyBuffers(node, content) {
|
|
943
|
+
node.buffers_hit = 0;
|
|
944
|
+
node.buffers_read = 0;
|
|
945
|
+
for (const group of content.split(",")) {
|
|
946
|
+
const [scope, ...counters] = group.trim().split(/\s+/);
|
|
947
|
+
if (scope === void 0 || scope === "") continue;
|
|
948
|
+
const scopeLabel = scope.charAt(0).toUpperCase() + scope.slice(1).toLowerCase();
|
|
949
|
+
for (const counter of counters) {
|
|
950
|
+
const eq = counter.indexOf("=");
|
|
951
|
+
if (eq === -1) continue;
|
|
952
|
+
const name = counter.slice(0, eq).toLowerCase();
|
|
953
|
+
const value = parseFiniteNumber(counter.slice(eq + 1));
|
|
954
|
+
if (value === null) continue;
|
|
955
|
+
if (scopeLabel === "Shared" && name === "hit") node.buffers_hit = value;
|
|
956
|
+
else if (scopeLabel === "Shared" && name === "read") node.buffers_read = value;
|
|
957
|
+
else
|
|
958
|
+
node.extra[`${scopeLabel} ${name.charAt(0).toUpperCase()}${name.slice(1)} Blocks`] = value;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
function fillMissingBuffers(node) {
|
|
963
|
+
node.buffers_hit ??= 0;
|
|
964
|
+
node.buffers_read ??= 0;
|
|
965
|
+
for (const child of node.children) fillMissingBuffers(child);
|
|
928
966
|
}
|
|
929
967
|
|
|
930
968
|
// src/parsers/sqlite.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tabularis/explain",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Parse, analyse and visualise a database EXPLAIN plan. Takes raw EXPLAIN output; never runs a query.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/TabularisDB/tabularis/tree/main/packages/explain",
|