Properly recognize various Aggregate types

Reported by Rhodium Toad on IRC - non-text plans that have node
"Aggregate" lose information about what type of aggregate it is (Hash,
Sorted).

In process re-run perltidy on whole code.
parent 864fa6a9
Revision history for Pg-Explain
0.92 2020/02/18
- Recognize various Aggregate nodes from non-text explains.
Per bug report from RhodiumToad.
0.91 2020/01/07
- Make all times be wall-clock times. That is - take into account
number of parallel workers
......
......@@ -209,6 +209,23 @@ t/46-parallel-queries-correct-times-nontext.d/json.plan
t/46-parallel-queries-correct-times-nontext.d/xml.plan
t/46-parallel-queries-correct-times-nontext.d/yaml.plan
t/46-parallel-queries-correct-times-nontext.t
t/47-aggregate-names.d/1.json
t/47-aggregate-names.d/1.txt
t/47-aggregate-names.d/1.xml
t/47-aggregate-names.d/1.yaml
t/47-aggregate-names.d/2.json
t/47-aggregate-names.d/2.txt
t/47-aggregate-names.d/2.xml
t/47-aggregate-names.d/2.yaml
t/47-aggregate-names.d/3.json
t/47-aggregate-names.d/3.txt
t/47-aggregate-names.d/3.xml
t/47-aggregate-names.d/3.yaml
t/47-aggregate-names.d/4.json
t/47-aggregate-names.d/4.txt
t/47-aggregate-names.d/4.xml
t/47-aggregate-names.d/4.yaml
t/47-aggregate-names.t
t/99-manifest.t
t/perlcriticrc
t/perltidyrc
......
......@@ -4,7 +4,7 @@
"hubert depesz lubaczewski <[email protected]>"
],
"dynamic_config" : 1,
"generated_by" : "Module::Build version 0.4224",
"generated_by" : "Module::Build version 0.4231",
"license" : [
"perl_5"
],
......@@ -42,39 +42,39 @@
"provides" : {
"Pg::Explain" : {
"file" : "lib/Pg/Explain.pm",
"version" : "0.91"
"version" : "0.92"
},
"Pg::Explain::Analyzer" : {
"file" : "lib/Pg/Explain/Analyzer.pm",
"version" : "0.91"
"version" : "0.92"
},
"Pg::Explain::From" : {
"file" : "lib/Pg/Explain/From.pm",
"version" : "0.91"
"version" : "0.92"
},
"Pg::Explain::FromJSON" : {
"file" : "lib/Pg/Explain/FromJSON.pm",
"version" : "0.91"
"version" : "0.92"
},
"Pg::Explain::FromText" : {
"file" : "lib/Pg/Explain/FromText.pm",
"version" : "0.91"
"version" : "0.92"
},
"Pg::Explain::FromXML" : {
"file" : "lib/Pg/Explain/FromXML.pm",
"version" : "0.91"
"version" : "0.92"
},
"Pg::Explain::FromYAML" : {
"file" : "lib/Pg/Explain/FromYAML.pm",
"version" : "0.91"
"version" : "0.92"
},
"Pg::Explain::Node" : {
"file" : "lib/Pg/Explain/Node.pm",
"version" : "0.91"
"version" : "0.92"
},
"Pg::Explain::StringAnonymizer" : {
"file" : "lib/Pg/Explain/StringAnonymizer.pm",
"version" : "0.91"
"version" : "0.92"
}
},
"release_status" : "stable",
......@@ -83,6 +83,6 @@
"http://dev.perl.org/licenses/"
]
},
"version" : "0.91",
"x_serialization_backend" : "JSON::PP version 2.97001"
"version" : "0.92",
"x_serialization_backend" : "JSON::PP version 4.02"
}
......@@ -10,7 +10,7 @@ build_requires:
configure_requires:
Module::Build: '0.38'
dynamic_config: 1
generated_by: 'Module::Build version 0.4224, CPAN::Meta::Converter version 2.150010'
generated_by: 'Module::Build version 0.4231, CPAN::Meta::Converter version 2.150010'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
......@@ -19,31 +19,31 @@ name: Pg-Explain
provides:
Pg::Explain:
file: lib/Pg/Explain.pm
version: '0.91'
version: '0.92'
Pg::Explain::Analyzer:
file: lib/Pg/Explain/Analyzer.pm
version: '0.91'
version: '0.92'
Pg::Explain::From:
file: lib/Pg/Explain/From.pm
version: '0.91'
version: '0.92'
Pg::Explain::FromJSON:
file: lib/Pg/Explain/FromJSON.pm
version: '0.91'
version: '0.92'
Pg::Explain::FromText:
file: lib/Pg/Explain/FromText.pm
version: '0.91'
version: '0.92'
Pg::Explain::FromXML:
file: lib/Pg/Explain/FromXML.pm
version: '0.91'
version: '0.92'
Pg::Explain::FromYAML:
file: lib/Pg/Explain/FromYAML.pm
version: '0.91'
version: '0.92'
Pg::Explain::Node:
file: lib/Pg/Explain/Node.pm
version: '0.91'
version: '0.92'
Pg::Explain::StringAnonymizer:
file: lib/Pg/Explain/StringAnonymizer.pm
version: '0.91'
version: '0.92'
requires:
Clone: '0'
Digest::SHA: '0'
......@@ -54,5 +54,5 @@ requires:
perl: '5.010'
resources:
license: http://dev.perl.org/licenses/
version: '0.91'
version: '0.92'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
......@@ -17,11 +17,11 @@ Pg::Explain - Object approach at reading explain analyze output
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......
......@@ -11,11 +11,11 @@ Pg::Explain::Analyzer - Some helper methods to analyze explains
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......@@ -43,7 +43,7 @@ Takes one argument - Pg::Explain object.
sub new {
my $class = shift;
my $self = bless {}, $class;
croak( 'You have to provide explain object.' ) if 0 == scalar @_;
croak( 'You have to provide explain object.' ) if 0 == scalar @_;
croak( 'Too many arguments to Pg::Explain::Analyzer->new().' ) if 1 < scalar @_;
$self->explain( shift );
croak( 'Given explain is not an object.' ) unless ref( $self->explain );
......@@ -101,7 +101,7 @@ sub all_node_paths {
my $current_path = [ @{ $prefix }, $node_type ];
my $current_path_str = join ' :: ', @{ $current_path };
push @return, $current_path unless $seen{ $current_path_str }++;
push @nodes, map { [ $current_path, $_ ] } $node->all_subnodes;
push @nodes, map { [ $current_path, $_ ] } $node->all_subnodes;
}
return \@return;
}
......
......@@ -9,11 +9,11 @@ Pg::Explain::From - Base class for parsers of non-text explain formats.
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......@@ -90,7 +90,14 @@ sub make_node_from {
$use_type .= ' on ' . $struct->{ 'Relation Name' };
$use_type .= ' ' . $struct->{ 'Alias' } if ( $struct->{ 'Alias' } ) && ( $struct->{ 'Alias' } ne $struct->{ 'Relation Name' } );
}
}
elsif ( $use_type eq 'Aggregate' ) {
my $strategy = $struct->{ 'Strategy' } || 'Plain';
$use_type = 'HashAggregate' if $strategy eq 'Hashed';
$use_type = 'GroupAggregate' if $strategy eq 'Sorted';
}
my $new_node = Pg::Explain::Node->new(
'type' => $use_type,
'estimated_startup_cost' => $struct->{ 'Startup Cost' },
......@@ -148,6 +155,11 @@ sub make_node_from {
);
}
if ( $struct->{ 'Group Key' } ) {
my $key = join( ', ', @{ $struct->{ 'Group Key' } } );
$new_node->add_extra_info( 'Group Key: ' . $key );
}
$new_node->add_extra_info( 'Workers Planned: ' . $struct->{ 'Workers Planned' } ) if $struct->{ 'Workers Planned' };
if ( $struct->{ 'Workers Launched' } ) {
$new_node->workers_launched( $struct->{ 'Workers Launched' } );
......@@ -225,7 +237,7 @@ sub make_node_from {
=head1 AUTHOR
hubert depesz lubaczewski, C<< <depesz at depesz.com> >>
hubert depesz lubaczewski, C << <depesz at depesz.com> >>
=head1 BUGS
......
......@@ -10,11 +10,11 @@ Pg::Explain::FromJSON - Parser for explains in JSON format
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......
......@@ -10,11 +10,11 @@ Pg::Explain::FromText - Parser for text based explains
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......
......@@ -10,11 +10,11 @@ Pg::Explain::FromXML - Parser for explains in XML format
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......@@ -51,6 +51,15 @@ sub normalize_node_struct {
}
$struct->{ 'Plans' } = $subplans;
if ( $struct->{ 'Group Key' } ) {
my $items = $struct->{ 'Group Key' }->{ 'Item' };
if ( 'ARRAY' eq ref $items ) {
$struct->{ 'Group Key' } = $items;
}
else {
$struct->{ 'Group Key' } = [ $items ];
}
}
return $struct;
}
......
......@@ -10,11 +10,11 @@ Pg::Explain::FromYAML - Parser for explains in YAML format
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......
......@@ -12,11 +12,11 @@ Pg::Explain::Node - Class representing single node from query plan
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......
......@@ -11,11 +11,11 @@ Pg::Explain::StringAnonymizer - Class to anonymize sets of strings
=head1 VERSION
Version 0.91
Version 0.92
=cut
our $VERSION = '0.91';
our $VERSION = '0.92';
=head1 SYNOPSIS
......
......@@ -28,9 +28,9 @@ is( $explain->top_node->sub_nodes->[ 0 ]->type,
is( $explain->top_node->workers, 1, 'Correct workers for topnode' );
is( $explain->top_node->initplans->[ 0 ]->workers, 1, 'Correct workers for Finalize Aggregate' );
is( $explain->top_node->initplans->[ 0 ]->sub_nodes->[ 0 ]->workers, 1, 'Correct workers for nested Gather' );
is( $explain->top_node->initplans->[ 0 ]->sub_nodes->[ 0 ]->sub_nodes->[ 0 ]->workers, 3, 'Correct workers for Partial Aggregate' );
is( $explain->top_node->initplans->[ 0 ]->sub_nodes->[ 0 ]->sub_nodes->[ 0 ]->sub_nodes->[ 0 ]->workers, 3, 'Correct workers for Parallel Seq Scan' );
is( $explain->top_node->sub_nodes->[ 0 ]->workers, 3, 'Correct workers for top Parallel Seq Scan' );
is( $explain->top_node->initplans->[ 0 ]->sub_nodes->[ 0 ]->sub_nodes->[ 0 ]->workers, 3, 'Correct workers for Partial Aggregate' );
is( $explain->top_node->initplans->[ 0 ]->sub_nodes->[ 0 ]->sub_nodes->[ 0 ]->sub_nodes->[ 0 ]->workers, 3, 'Correct workers for Parallel Seq Scan' );
is( $explain->top_node->sub_nodes->[ 0 ]->workers, 3, 'Correct workers for top Parallel Seq Scan' );
exit;
......
QUERY PLAN
-------------------------------------------
[ +
{ +
"Plan": { +
"Node Type": "Aggregate", +
"Strategy": "Plain", +
"Partial Mode": "Simple", +
"Parallel Aware": false, +
"Startup Cost": 17.85, +
"Total Cost": 17.86, +
"Plan Rows": 1, +
"Plan Width": 8, +
"Plans": [ +
{ +
"Node Type": "Seq Scan", +
"Parent Relationship": "Outer",+
"Parallel Aware": false, +
"Relation Name": "pg_class", +
"Alias": "pg_class", +
"Startup Cost": 0.00, +
"Total Cost": 16.88, +
"Plan Rows": 388, +
"Plan Width": 0 +
} +
] +
} +
} +
]
(1 row)
QUERY PLAN
-----------------------------------------------------------------
Aggregate (cost=17.85..17.86 rows=1 width=8)
-> Seq Scan on pg_class (cost=0.00..16.88 rows=388 width=0)
(2 rows)
QUERY PLAN
------------------------------------------------------------
<explain xmlns="http://www.postgresql.org/2009/explain"> +
<Query> +
<Plan> +
<Node-Type>Aggregate</Node-Type> +
<Strategy>Plain</Strategy> +
<Partial-Mode>Simple</Partial-Mode> +
<Parallel-Aware>false</Parallel-Aware> +
<Startup-Cost>17.85</Startup-Cost> +
<Total-Cost>17.86</Total-Cost> +
<Plan-Rows>1</Plan-Rows> +
<Plan-Width>8</Plan-Width> +
<Plans> +
<Plan> +
<Node-Type>Seq Scan</Node-Type> +
<Parent-Relationship>Outer</Parent-Relationship>+
<Parallel-Aware>false</Parallel-Aware> +
<Relation-Name>pg_class</Relation-Name> +
<Alias>pg_class</Alias> +
<Startup-Cost>0.00</Startup-Cost> +
<Total-Cost>16.88</Total-Cost> +
<Plan-Rows>388</Plan-Rows> +
<Plan-Width>0</Plan-Width> +
</Plan> +
</Plans> +
</Plan> +
</Query> +
</explain>
(1 row)
QUERY PLAN
--------------------------------------
- Plan: +
Node Type: "Aggregate" +
Strategy: "Plain" +
Partial Mode: "Simple" +
Parallel Aware: false +
Startup Cost: 17.85 +
Total Cost: 17.86 +
Plan Rows: 1 +
Plan Width: 8 +
Plans: +
- Node Type: "Seq Scan" +
Parent Relationship: "Outer"+
Parallel Aware: false +
Relation Name: "pg_class" +
Alias: "pg_class" +
Startup Cost: 0.00 +
Total Cost: 16.88 +
Plan Rows: 388 +
Plan Width: 0
(1 row)
QUERY PLAN
-------------------------------------------
[ +
{ +
"Plan": { +
"Node Type": "Aggregate", +
"Strategy": "Hashed", +
"Partial Mode": "Simple", +
"Parallel Aware": false, +
"Startup Cost": 18.82, +
"Total Cost": 18.86, +
"Plan Rows": 4, +
"Plan Width": 9, +
"Group Key": ["relkind"], +
"Plans": [ +
{ +
"Node Type": "Seq Scan", +
"Parent Relationship": "Outer",+
"Parallel Aware": false, +
"Relation Name": "pg_class", +
"Alias": "pg_class", +
"Startup Cost": 0.00, +
"Total Cost": 16.88, +
"Plan Rows": 388, +
"Plan Width": 1 +
} +
] +
} +
} +
]
(1 row)
QUERY PLAN
-----------------------------------------------------------------
HashAggregate (cost=18.82..18.86 rows=4 width=9)
Group Key: relkind
-> Seq Scan on pg_class (cost=0.00..16.88 rows=388 width=1)
(3 rows)
QUERY PLAN
------------------------------------------------------------
<explain xmlns="http://www.postgresql.org/2009/explain"> +
<Query> +
<Plan> +
<Node-Type>Aggregate</Node-Type> +
<Strategy>Hashed</Strategy> +
<Partial-Mode>Simple</Partial-Mode> +
<Parallel-Aware>false</Parallel-Aware> +
<Startup-Cost>18.82</Startup-Cost> +
<Total-Cost>18.86</Total-Cost> +
<Plan-Rows>4</Plan-Rows> +
<Plan-Width>9</Plan-Width> +
<Group-Key> +
<Item>relkind</Item> +
</Group-Key> +
<Plans> +
<Plan> +
<Node-Type>Seq Scan</Node-Type> +
<Parent-Relationship>Outer</Parent-Relationship>+
<Parallel-Aware>false</Parallel-Aware> +
<Relation-Name>pg_class</Relation-Name> +
<Alias>pg_class</Alias> +
<Startup-Cost>0.00</Startup-Cost> +
<Total-Cost>16.88</Total-Cost> +
<Plan-Rows>388</Plan-Rows> +
<Plan-Width>1</Plan-Width> +
</Plan> +
</Plans> +
</Plan> +
</Query> +
</explain>
(1 row)
QUERY PLAN
--------------------------------------
- Plan: +
Node Type: "Aggregate" +
Strategy: "Hashed" +
Partial Mode: "Simple" +
Parallel Aware: false +
Startup Cost: 18.82 +
Total Cost: 18.86 +
Plan Rows: 4 +
Plan Width: 9 +
Group Key: +
- "relkind" +
Plans: +
- Node Type: "Seq Scan" +
Parent Relationship: "Outer"+
Parallel Aware: false +
Relation Name: "pg_class" +
Alias: "pg_class" +
Startup Cost: 0.00 +
Total Cost: 16.88 +
Plan Rows: 388 +
Plan Width: 1
(1 row)
QUERY PLAN
-----------------------------------------------
[ +
{ +
"Plan": { +
"Node Type": "Aggregate", +
"Strategy": "Sorted", +
"Partial Mode": "Simple", +
"Parallel Aware": false, +
"Startup Cost": 33.56, +
"Total Cost": 40.39, +
"Plan Rows": 4, +
"Plan Width": 9, +
"Group Key": ["pg_class.relkind"], +
"Plans": [ +
{ +
"Node Type": "Sort", +
"Parent Relationship": "Outer", +
"Parallel Aware": false, +
"Startup Cost": 33.56, +
"Total Cost": 34.53, +
"Plan Rows": 388, +
"Plan Width": 1, +
"Sort Key": ["pg_class.relkind"], +
"Plans": [ +
{ +
"Node Type": "Seq Scan", +
"Parent Relationship": "Outer",+
"Parallel Aware": false, +
"Relation Name": "pg_class", +
"Alias": "pg_class", +
"Startup Cost": 0.00, +
"Total Cost": 16.88, +
"Plan Rows": 388, +
"Plan Width": 1 +
} +
] +
} +
] +
} +
} +
]
(1 row)
QUERY PLAN
-----------------------------------------------------------------------
GroupAggregate (cost=33.56..40.39 rows=4 width=9)
Group Key: pg_class.relkind
-> Sort (cost=33.56..34.53 rows=388 width=1)
Sort Key: pg_class.relkind
-> Seq Scan on pg_class (cost=0.00..16.88 rows=388 width=1)
(5 rows)
QUERY PLAN
----------------------------------------------------------------
<explain xmlns="http://www.postgresql.org/2009/explain"> +
<Query> +
<Plan> +
<Node-Type>Aggregate</Node-Type> +
<Strategy>Sorted</Strategy> +
<Partial-Mode>Simple</Partial-Mode> +
<Parallel-Aware>false</Parallel-Aware> +
<Startup-Cost>33.56</Startup-Cost> +
<Total-Cost>40.39</Total-Cost> +
<Plan-Rows>4</Plan-Rows> +
<Plan-Width>9</Plan-Width> +
<Group-Key> +
<Item>pg_class.relkind</Item> +
</Group-Key> +
<Plans> +
<Plan> +
<Node-Type>Sort</Node-Type> +
<Parent-Relationship>Outer</Parent-Relationship> +
<Parallel-Aware>false</Parallel-Aware> +
<Startup-Cost>33.56</Startup-Cost> +
<Total-Cost>34.53</Total-Cost> +
<Plan-Rows>388</Plan-Rows> +
<Plan-Width>1</Plan-Width> +
<Sort-Key> +
<Item>pg_class.relkind</Item> +
</Sort-Key> +
<Plans> +
<Plan> +
<Node-Type>Seq Scan</Node-Type> +
<Parent-Relationship>Outer</Parent-Relationship>+
<Parallel-Aware>false</Parallel-Aware> +
<Relation-Name>pg_class</Relation-Name> +
<Alias>pg_class</Alias> +
<Startup-Cost>0.00</Startup-Cost> +
<Total-Cost>16.88</Total-Cost> +
<Plan-Rows>388</Plan-Rows> +
<Plan-Width>1</Plan-Width> +
</Plan> +
</Plans> +
</Plan> +
</Plans> +
</Plan> +
</Query> +
</explain>
(1 row)
QUERY PLAN
------------------------------------------
- Plan: +
Node Type: "Aggregate" +
Strategy: "Sorted" +
Partial Mode: "Simple" +
Parallel Aware: false +
Startup Cost: 33.56 +
Total Cost: 40.39 +
Plan Rows: 4 +
Plan Width: 9 +
Group Key: +
- "pg_class.relkind" +
Plans: +
- Node Type: "Sort" +
Parent Relationship: "Outer" +
Parallel Aware: false +
Startup Cost: 33.56 +
Total Cost: 34.53 +
Plan Rows: 388 +
Plan Width: 1 +
Sort Key: +
- "pg_class.relkind" +
Plans: +
- Node Type: "Seq Scan" +
Parent Relationship: "Outer"+
Parallel Aware: false +
Relation Name: "pg_class" +
Alias: "pg_class" +
Startup Cost: 0.00 +
Total Cost: 16.88 +
Plan Rows: 388 +
Plan Width: 1
(1 row)
[
{
"Plan": {
"Node Type": "Aggregate",
"Strategy": "Hashed",
"Partial Mode": "Simple",
"Parallel Aware": false,
"Startup Cost": 19.79,
"Total Cost": 19.95,
"Plan Rows": 16,
"Plan Width": 13,
"Group Key": ["relnamespace", "relkind"],
"Plans": [