Skip to content

Commit

Permalink
Don't fail to parse plans without costs nor analyze
Browse files Browse the repository at this point in the history
Unfortunately plans with line breaks can't be parsed anymore
See #36 and #40

Fixes #119
  • Loading branch information
pgiraud committed Nov 29, 2019
1 parent baf3e40 commit fb37be1
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 23 deletions.
16 changes: 16 additions & 0 deletions src/services/__tests__/08-plan-without-cost.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,19 @@ Seq Scan on pg_class (actual time=0.013..0.181 rows=645 loops=1)
});
});


describe('PlanService', () => {
test('Can parse plan without analyze nor costs', () => {
const planService = new PlanService();
const source = `
Bitmap Heap Scan on a
Recheck Cond: ((id = 42) OR (id = 4711))
-> BitmapOr
-> Bitmap Index Scan on a_pkey
Index Cond: (id = 42)
-> Bitmap Index Scan on a_pkey
Index Cond: (id = 4711)
`;
const r: any = planService.fromSource(source);
});
});
15 changes: 14 additions & 1 deletion src/services/__tests__/1-parse-error-line-break.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import { PlanService } from '@/services/plan-service';

describe('PlanService', () => {
test('Can`t parse plan with line break from text', () => {
test('Can`t parse plan with line break', () => {
const planService = new PlanService();
const planText = `
Seq Scan on ref (cost=0.00..14425.02 rows=1000002 width=8) (actual
`;
expect(() => { planService.fromText(planText); }).toThrow();
});

test('Can`t parse plan with line break', () => {
const planService = new PlanService();
// tslint:disable:max-line-length
const source = `
Nested Loop Left Join (cost=11.95..28.52 rows=5 width=157) (actual time=0.010..0.010 rows=0 loops=1)
Output: rel_users_exams.user_username, rel_users_exams.exam_id, rel_users_exams.started_at, rel_users_exams.finished_at, exam_1.id, exam_1.title, ex
here is a line break
Planning Time: 1.110 ms
Execution Time: 0.170 ms`;
expect(() => { planService.fromText(source); }).toThrow();
});

});
14 changes: 0 additions & 14 deletions src/services/__tests__/plan-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,18 +229,4 @@ Result (cost=0.31..0.32 rows=1 width=4)
expect(r.Plan.Plans[0]['Shared Hit Blocks']).toEqual(16230);
expect(r.Plan.Plans[0]['Shared Read Blocks']).toEqual(67104);
});

test('correctly parses line breaks', () => {
const planService = new PlanService();
// tslint:disable:max-line-length
const source = `
Nested Loop Left Join (cost=11.95..28.52 rows=5 width=157) (actual time=0.010..0.010 rows=0 loops=1)
Output: rel_users_exams.user_username, rel_users_exams.exam_id, rel_users_exams.started_at, rel_users_exams.finished_at, exam_1.id, exam_1.title, ex
here is a line break
Planning Time: 1.110 ms
Execution Time: 0.170 ms`;
// tslint:enable:max-line-length
const r: any = planService.fromSource(source);
expect(r['Execution Time']).toEqual(0.17);
});
});
20 changes: 12 additions & 8 deletions src/services/plan-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,11 @@ export class PlanService {
// Remove any begining "
line = line.replace(/^\s*"/, '');


const emptyLineRegex = '^\s*$';
const headerRegex = '^\\s*(QUERY|---|#).*$';
const prefixRegex = '^(\\s*->\\s*|\\s*)';
const typeRegex = '(\\S.*?)';
const typeRegex = '([^\\r\\n\\t\\f\\v\\:\\(]*?)';
// tslint:disable-next-line:max-line-length
const estimationRegex = '\\(cost=(\\d+\\.\\d+)\\.\\.(\\d+\\.\\d+)\\s+rows=(\\d+)\\s+width=(\\d+)\\)';
const nonCapturingGroupOpen = '(?:';
Expand All @@ -297,10 +300,9 @@ export class PlanService {
const actualRegex = '(?:actual\\stime=(\\d+\\.\\d+)\\.\\.(\\d+\\.\\d+)\\srows=(\\d+)\\sloops=(\\d+)|actual\\srows=(\\d+)\\sloops=(\\d+)|(never\\s+executed))';
const optionalGroup = '?';

const a = new RegExp(
prefixRegex,
'gm',
);
const emptyLineMatches = new RegExp(emptyLineRegex).exec(line);
const headerMatches = new RegExp(headerRegex).exec(line);

/*
* Groups
* 1: prefix
Expand Down Expand Up @@ -328,7 +330,7 @@ export class PlanService {
const nodeRegex = new RegExp(
prefixRegex +
typeRegex +
'\\s+' +
'\\s*' +
nonCapturingGroupOpen +
(nonCapturingGroupOpen + estimationRegex + '\\s+' +
openParenthesisRegex + actualRegex + closeParenthesisRegex +
Expand All @@ -337,7 +339,7 @@ export class PlanService {
nonCapturingGroupOpen + estimationRegex + nonCapturingGroupClose +
'|' +
nonCapturingGroupOpen + openParenthesisRegex + actualRegex + closeParenthesisRegex + nonCapturingGroupClose +
nonCapturingGroupClose +
nonCapturingGroupClose + '*' +
'\\s*$',
'gm',
);
Expand Down Expand Up @@ -386,7 +388,9 @@ export class PlanService {
const extraRegex = /^(\s*)(\S.*\S)\s*$/g;
const extraMatches = extraRegex.exec(line);

if (nodeMatches) {
if (emptyLineMatches || headerMatches) {
return;
} else if (nodeMatches && !cteMatches && !subMatches) {
const prefix = nodeMatches[1];
const neverExecuted = nodeMatches[13];
const newNode: Node = new Node(nodeMatches[2]);
Expand Down

0 comments on commit fb37be1

Please sign in to comment.