Skip to content
GitLab
About GitLab
GitLab: the DevOps platform
Explore GitLab
Install GitLab
How GitLab compares
Get started
GitLab docs
GitLab Learn
Pricing
Talk to an expert
/
Help
What's new
2
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Projects
Groups
Snippets
Sign up now
Login
Sign in / Register
Toggle navigation
Menu
Open sidebar
Hubert Lubaczewski
Pg--Explain
Commits
2353b0aa
Commit
2353b0aa
authored
Jun 03, 2015
by
Hubert Lubaczewski
Browse files
Allow parsing of plans without timing information, per request from Karl Bartel
parent
e2dcdfc9
Changes
13
Hide whitespace changes
Inline
Side-by-side
Changes
View file @
2353b0aa
Revision history for Pg-Explain
0.70 2014/06/08
- Allow parsing of plans without timing information, per request from Karl Bartel
0.69 2014/06/08
- Anonymize CTE names, per gripe from Brian Dunavant
...
...
MANIFEST
View file @
2353b0aa
...
...
@@ -113,6 +113,7 @@ t/22-anonymization-of-non-select-plans.t
t/23-anonymization-of-one-time-filters.t
t/24-anonymization-of-index-only-scans.t
t/25-anonymization-of-cte-names.t
t/26-explain-with-no-timing.t
t/99-manifest.t
t/perlcriticrc
t/perltidyrc
...
...
META.json
View file @
2353b0aa
...
...
@@ -4,7 +4,7 @@
"hubert depesz lubaczewski <depesz@depesz.com>"
],
"dynamic_config"
:
1
,
"generated_by"
:
"Module::Build version 0.4
003, CPAN::Meta::Converter version 2.1209
21"
,
"generated_by"
:
"Module::Build version 0.421"
,
"license"
:
[
"perl_5"
],
...
...
@@ -41,35 +41,35 @@
"provides"
:
{
"Pg::Explain"
:
{
"file"
:
"lib/Pg/Explain.pm"
,
"version"
:
"0.
69
"
"version"
:
"0.
70
"
},
"Pg::Explain::From"
:
{
"file"
:
"lib/Pg/Explain/From.pm"
,
"version"
:
"0.
69
"
"version"
:
"0.
70
"
},
"Pg::Explain::FromJSON"
:
{
"file"
:
"lib/Pg/Explain/FromJSON.pm"
,
"version"
:
"0.
69
"
"version"
:
"0.
70
"
},
"Pg::Explain::FromText"
:
{
"file"
:
"lib/Pg/Explain/FromText.pm"
,
"version"
:
"0.
69
"
"version"
:
"0.
70
"
},
"Pg::Explain::FromXML"
:
{
"file"
:
"lib/Pg/Explain/FromXML.pm"
,
"version"
:
"0.
69
"
"version"
:
"0.
70
"
},
"Pg::Explain::FromYAML"
:
{
"file"
:
"lib/Pg/Explain/FromYAML.pm"
,
"version"
:
"0.
69
"
"version"
:
"0.
70
"
},
"Pg::Explain::Node"
:
{
"file"
:
"lib/Pg/Explain/Node.pm"
,
"version"
:
"0.
69
"
"version"
:
"0.
70
"
},
"Pg::Explain::StringAnonymizer"
:
{
"file"
:
"lib/Pg/Explain/StringAnonymizer.pm"
,
"version"
:
"0.
69
"
"version"
:
"0.
70
"
}
},
"release_status"
:
"stable"
,
...
...
@@ -78,5 +78,5 @@
"http://dev.perl.org/licenses/"
]
},
"version"
:
"0.
69
"
"version"
:
"0.
70
"
}
META.yml
View file @
2353b0aa
...
...
@@ -3,51 +3,51 @@ abstract: 'Object approach at reading explain analyze output'
author
:
-
'
hubert
depesz
lubaczewski
<depesz@depesz.com>'
build_requires
:
Test::Deep:
0
Test::Exception:
0
Test::More:
0
autodie
:
0
Test::Deep:
'0'
Test::Exception:
'0'
Test::More:
'0'
autodie
:
'
0'
configure_requires
:
Module::Build:
0.38
Module::Build:
'
0.38
'
dynamic_config
:
1
generated_by
:
'
Module::Build
version
0.4
003
,
CPAN::Meta::Converter
version
2.1
20921
'
generated_by
:
'
Module::Build
version
0.4
21
,
CPAN::Meta::Converter
version
2.1
42690
'
license
:
perl
meta-spec
:
url
:
http://module-build.sourceforge.net/META-spec-v1.4.html
version
:
1.4
version
:
'
1.4
'
name
:
Pg-Explain
provides
:
Pg::Explain:
file
:
lib/Pg/Explain.pm
version
:
0.
69
version
:
'
0.
70'
Pg::Explain::From:
file
:
lib/Pg/Explain/From.pm
version
:
0.
69
version
:
'
0.
70'
Pg::Explain::FromJSON:
file
:
lib/Pg/Explain/FromJSON.pm
version
:
0.
69
version
:
'
0.
70'
Pg::Explain::FromText:
file
:
lib/Pg/Explain/FromText.pm
version
:
0.
69
version
:
'
0.
70'
Pg::Explain::FromXML:
file
:
lib/Pg/Explain/FromXML.pm
version
:
0.
69
version
:
'
0.
70'
Pg::Explain::FromYAML:
file
:
lib/Pg/Explain/FromYAML.pm
version
:
0.
69
version
:
'
0.
70'
Pg::Explain::Node:
file
:
lib/Pg/Explain/Node.pm
version
:
0.
69
version
:
'
0.
70'
Pg::Explain::StringAnonymizer:
file
:
lib/Pg/Explain/StringAnonymizer.pm
version
:
0.
69
version
:
'
0.
70'
requires
:
Clone
:
0
Digest::SHA1:
0
HOP::Lexer:
0
JSON
:
0
XML::Simple:
0
YAML
:
0
Clone
:
'
0'
Digest::SHA1:
'0'
HOP::Lexer:
'0'
JSON
:
'
0'
XML::Simple:
'0'
YAML
:
'
0'
resources
:
license
:
http://dev.perl.org/licenses/
version
:
0.
69
version
:
'
0.
70'
lib/Pg/Explain.pm
View file @
2353b0aa
...
...
@@ -11,11 +11,11 @@ Pg::Explain - Object approach at reading explain analyze output
=head1 VERSION
Version 0.
69
Version 0.
70
=cut
our
$VERSION
=
'
0.
69
';
our
$VERSION
=
'
0.
70
';
=head1 SYNOPSIS
...
...
lib/Pg/Explain/From.pm
View file @
2353b0aa
...
...
@@ -9,11 +9,11 @@ Pg::Explain::From - Base class for parsers of non-text explain formats.
=head1 VERSION
Version 0.
69
Version 0.
70
=cut
our
$VERSION
=
'
0.
69
';
our
$VERSION
=
'
0.
70
';
=head1 SYNOPSIS
...
...
lib/Pg/Explain/FromJSON.pm
View file @
2353b0aa
...
...
@@ -10,11 +10,11 @@ Pg::Explain::FromJSON - Parser for explains in JSON format
=head1 VERSION
Version 0.
69
Version 0.
70
=cut
our
$VERSION
=
'
0.
69
';
our
$VERSION
=
'
0.
70
';
=head1 SYNOPSIS
...
...
lib/Pg/Explain/FromText.pm
View file @
2353b0aa
...
...
@@ -9,11 +9,11 @@ Pg::Explain::FromText - Parser for text based explains
=head1 VERSION
Version 0.
69
Version 0.
70
=cut
our
$VERSION
=
'
0.
69
';
our
$VERSION
=
'
0.
70
';
=head1 SYNOPSIS
...
...
@@ -60,20 +60,21 @@ sub parse_source {
$line
=~
s/"\z//
;
if
(
my
@catch
=
$line
=~
m{
\A
(\s* -> \s* | \s* )
(\S.*?)
(
?<prefix>
\s* -> \s* | \s* )
(
?<type>
\S.*?)
\s+
\( cost=(
\d+\.\d+)\.\.(\d+\.\d+) \s+ rows=(\d+) \s+ width=(
\d+) \)
\( cost=(
?<estimated_startup_cost>\d+\.\d+)\.\.(?<estimated_total_cost>\d+\.\d+) \s+ rows=(?<estimated_rows>\d+) \s+ width=(?<estimated_row_width>
\d+) \)
(?:
\s+
\(
(?:
actual \s time=(
\d+\.\d+)\.\.(
\d+\.\d+) \s rows=(\d+) \s loops=(\d+)
actual \s time=(
?<actual_time_first>\d+\.\d+)\.\.(?<actual_time_last>
\d+\.\d+) \s rows=(
?<actual_rows>
\d+) \s loops=(
?<actual_loops>
\d+)
|
( never \s+ executed )
actual \s rows=(?<actual_rows>\d+) \s loops=(?<actual_loops>\d+)
|
(?<never_executed> never \s+ executed )
)
\)
)?
...
...
@@ -82,37 +83,29 @@ sub parse_source {
}xms
)
{
my
$new_node
=
Pg::Explain::
Node
->
new
(
'
type
'
=>
$catch
[
1
],
'
estimated_startup_cost
'
=>
$catch
[
2
],
'
estimated_total_cost
'
=>
$catch
[
3
],
'
estimated_rows
'
=>
$catch
[
4
],
'
estimated_row_width
'
=>
$catch
[
5
],
'
actual_time_first
'
=>
$catch
[
6
],
'
actual_time_last
'
=>
$catch
[
7
],
'
actual_rows
'
=>
$catch
[
8
],
'
actual_loops
'
=>
$catch
[
9
],
);
if
(
defined
$catch
[
10
]
&&
$catch
[
10
]
=~
m{never \s+ executed }xms
)
{
my
$new_node
=
Pg::Explain::
Node
->
new
(
%+
);
if
(
defined
$+
{
'
never_executed
'
}
)
{
$new_node
->
actual_loops
(
0
);
$new_node
->
never_executed
(
1
);
}
my
$element
=
{
'
node
'
=>
$new_node
,
'
subelement-type
'
=>
'
subnode
',
};
my
$prefix_length
=
length
$+
{
'
prefix
'
};
if
(
0
==
scalar
keys
%element_at_depth
)
{
$element_at_depth
{
length
$catch
[
0
]
}
=
$element
;
$element_at_depth
{
$prefix_length
}
=
$element
;
$top_node
=
$new_node
;
next
LINE
;
}
my
@existing_depths
=
sort
{
$a
<=>
$b
}
keys
%element_at_depth
;
for
my
$key
(
grep
{
$_
>=
length
(
$catch
[
0
]
)
}
@existing_depths
)
{
for
my
$key
(
grep
{
$_
>=
$prefix_length
}
@existing_depths
)
{
delete
$element_at_depth
{
$key
};
}
my
$maximal_depth
=
(
sort
{
$b
<=>
$a
}
keys
%element_at_depth
)[
0
];
my
$previous_element
=
$element_at_depth
{
$maximal_depth
};
$element_at_depth
{
length
$catch
[
0
]
}
=
$element
;
$element_at_depth
{
$prefix_length
}
=
$element
;
if
(
$previous_element
->
{
'
subelement-type
'
}
eq
'
subnode
'
)
{
$previous_element
->
{
'
node
'
}
->
add_sub_node
(
$new_node
);
...
...
lib/Pg/Explain/FromXML.pm
View file @
2353b0aa
...
...
@@ -10,11 +10,11 @@ Pg::Explain::FromXML - Parser for explains in XML format
=head1 VERSION
Version 0.
69
Version 0.
70
=cut
our
$VERSION
=
'
0.
69
';
our
$VERSION
=
'
0.
70
';
=head1 SYNOPSIS
...
...
lib/Pg/Explain/FromYAML.pm
View file @
2353b0aa
...
...
@@ -10,11 +10,11 @@ Pg::Explain::FromYAML - Parser for explains in YAML format
=head1 VERSION
Version 0.
69
Version 0.
70
=cut
our
$VERSION
=
'
0.
69
';
our
$VERSION
=
'
0.
70
';
=head1 SYNOPSIS
...
...
lib/Pg/Explain/Node.pm
View file @
2353b0aa
...
...
@@ -12,11 +12,11 @@ Pg::Explain::Node - Class representing single node from query plan
=head1 VERSION
Version 0.
69
Version 0.
70
=cut
our
$VERSION
=
'
0.
69
';
our
$VERSION
=
'
0.
70
';
=head1 SYNOPSIS
...
...
@@ -544,12 +544,15 @@ sub as_text {
$heading_line
.=
sprintf
'
(cost=%.3f..%.3f rows=%s width=%d)
',
$self
->
estimated_startup_cost
,
$self
->
estimated_total_cost
,
$self
->
estimated_rows
,
$self
->
estimated_row_width
;
if
(
$self
->
is_analyzed
)
{
my
$inner
;
if
(
0
==
$self
->
{
'
actual_loops
'
}
)
{
if
(
$self
->
never_executed
)
{
$inner
=
'
never executed
';
}
els
e
{
els
if
(
defined
$self
->
actual_time_last
)
{
$inner
=
sprintf
'
actual time=%.3f..%.3f rows=%s loops=%d
',
$self
->
actual_time_first
,
$self
->
actual_time_last
,
$self
->
actual_rows
,
$self
->
actual_loops
;
}
else
{
$inner
=
sprintf
'
actual rows=%s loops=%d
',
$self
->
actual_rows
,
$self
->
actual_loops
;
}
$heading_line
.=
"
(
$inner
)
";
}
...
...
lib/Pg/Explain/StringAnonymizer.pm
View file @
2353b0aa
...
...
@@ -11,11 +11,11 @@ Pg::Explain::StringAnonymizer - Class to anonymize sets of strings
=head1 VERSION
Version 0.
69
Version 0.
70
=cut
our
$VERSION
=
'
0.
69
';
our
$VERSION
=
'
0.
70
';
=head1 SYNOPSIS
...
...
t/26-explain-with-no-timing.t
0 → 100644
View file @
2353b0aa
#!perl
use
Test::
More
;
use
Test::
Deep
;
use
Test::
Exception
;
use
Data::
Dumper
;
use
autodie
;
use
Pg::
Explain
;
my
@plans
=
(
q{
Aggregate (cost=22.21..22.22 rows=1 width=0) (actual rows=1 loops=1)
-> Hash Join (cost=5.75..21.93 rows=111 width=0) (actual rows=113 loops=1)
Hash Cond: (pg_class.oid = pg_index.indrelid)
-> Index Only Scan using pg_class_oid_index on pg_class (cost=0.15..12.86 rows=314 width=4) (actual rows=319 loops=1)
Heap Fetches: 130
-> Hash (cost=4.22..4.22 rows=111 width=4) (actual rows=113 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 12kB
-> Seq Scan on pg_index (cost=0.00..4.22 rows=111 width=4) (actual rows=113 loops=1)
Filter: indisunique
Rows Removed by Filter: 11
}
,
q{
Result (cost=1.06..17.20 rows=314 width=201) (actual rows=0 loops=1)
One-Time Filter: ($0 > 1000)
InitPlan 1 (returns $0)
-> Aggregate (cost=1.05..1.06 rows=1 width=0) (actual rows=1 loops=1)
-> Seq Scan on pg_language (cost=0.00..1.04 rows=4 width=0) (actual rows=319 loops=1)
-> Seq Scan on pg_class (cost=0.00..16.14 rows=314 width=201) (never executed)
}
);
plan
'
tests
'
=>
4
*
scalar
@plans
;
for
my
$plan_source
(
@plans
)
{
my
$explain
=
Pg::
Explain
->
new
(
'
source
'
=>
$plan_source
);
isa_ok
(
$explain
,
'
Pg::Explain
'
);
isa_ok
(
$explain
->
top_node
,
'
Pg::Explain::Node
'
);
my
$textual
=
$explain
->
as_text
();
ok
(
$textual
=~
m{\(actual rows=319 loops=1\)}
,
"
Got actual data without timing
"
);
if
(
$plan_source
=~
m{never executed}
)
{
ok
(
$textual
=~
m{never executed}
,
"
Plan is never executed
"
);
}
else
{
ok
(
1
,
'
placeholder test, to keep calculation of number of tests simple
');
}
}
exit
;
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment