File Coverage

blib/lib/Pg/Explain/FromXML.pm
Criterion Covered Total %
statement 93 97 95.8
branch 37 44 84.0
condition 5 6 83.3
subroutine 16 16 100.0
pod 2 2 100.0
total 153 165 92.7


line stmt bran cond sub pod time code
1             package Pg::Explain::FromXML;
2              
3             # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/
4 72     72   818 use v5.18;
  72         216  
5 72     72   346 use strict;
  72         121  
  72         1270  
6 72     72   282 use warnings;
  72         114  
  72         3002  
7 72     72   370 use warnings qw( FATAL utf8 );
  72         129  
  72         2272  
8 72     72   382 use utf8;
  72         175  
  72         415  
9 72     72   2114 use open qw( :std :utf8 );
  72         145  
  72         462  
10 72     72   8650 use Unicode::Normalize qw( NFC );
  72         189  
  72         3900  
11 72     72   468 use Unicode::Collate;
  72         131  
  72         2243  
12 72     72   413 use Encode qw( decode );
  72         119  
  72         4110  
13              
14             if ( grep /\P{ASCII}/ => @ARGV ) {
15             @ARGV = map { decode( 'UTF-8', $_ ) } @ARGV;
16             }
17              
18             # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/
19              
20 72     72   14380 use base qw( Pg::Explain::From );
  72         147  
  72         7590  
21 72     72   52206 use XML::Simple;
  72         563391  
  72         552  
22 72     72   6127 use Carp;
  72         148  
  72         3351  
23 72     72   400 use Pg::Explain::JIT;
  72         137  
  72         1364  
24 72     72   360 use Pg::Explain::Buffers;
  72         152  
  72         57284  
25              
26             =head1 NAME
27              
28             Pg::Explain::FromXML - Parser for explains in XML format
29              
30             =head1 VERSION
31              
32             Version 2.2
33              
34             =cut
35              
36             our $VERSION = '2.2';
37              
38             =head1 SYNOPSIS
39              
40             It's internal class to wrap some work. It should be used by Pg::Explain, and not directly.
41              
42             =head1 FUNCTIONS
43              
44             =head2 normalize_node_struct
45              
46             XML structure is different than JSON/YAML (after parsing), so we need to normalize it.
47             =cut
48              
49             sub normalize_node_struct {
50 177     177 1 267 my $self = shift;
51 177         247 my $struct = shift;
52              
53 177         342 my @keys = keys %{ $struct };
  177         843  
54 177         376 for my $key ( @keys ) {
55 2661         3153 my $new_key = $key;
56 2661         3348 $new_key =~ s{^I-O-(Read|Write)-Time$}{I/O $1 Time};
57 2661         5188 $new_key =~ s/-/ /g;
58 2661 100       7758 $struct->{ $new_key } = delete $struct->{ $key } if $key ne $new_key;
59             }
60              
61 177         322 my $subplans = [];
62 177 50 66     659 if ( ( $struct->{ 'Plans' } )
63             && ( $struct->{ 'Plans' }->{ 'Plan' } ) )
64             {
65 68 100       256 if ( 'HASH' eq ref $struct->{ 'Plans' }->{ 'Plan' } ) {
66 31         62 push @{ $subplans }, $struct->{ 'Plans' }->{ 'Plan' };
  31         85  
67             }
68             else {
69 37         68 $subplans = $struct->{ 'Plans' }->{ 'Plan' };
70             }
71             }
72 177         378 $struct->{ 'Plans' } = $subplans;
73              
74 177 100       424 if ( $struct->{ 'Group Key' } ) {
75 3         10 my $items = $struct->{ 'Group Key' }->{ 'Item' };
76 3 100       9 if ( 'ARRAY' eq ref $items ) {
77 1         94 $struct->{ 'Group Key' } = $items;
78             }
79             else {
80 2         6 $struct->{ 'Group Key' } = [ $items ];
81             }
82             }
83              
84 177 100       354 if ( $struct->{ 'Conflict Arbiter Indexes' } ) {
85 1         5 $struct->{ 'Conflict Arbiter Indexes' } = [ $struct->{ 'Conflict Arbiter Indexes' }->{ 'Item' } ];
86             }
87 177         513 return $struct;
88             }
89              
90             =head2 parse_source
91              
92             Function which parses actual plan, and constructs Pg::Explain::Node objects
93             which represent it.
94              
95             Returns Top node of query plan.
96              
97             =cut
98              
99             sub parse_source {
100 67     67 1 138 my $self = shift;
101 67         150 my $source = shift;
102              
103 67 50       1451 unless ( $source =~ s{\A .*? ^ \s* () \s* $}{$1}xms ) {
104 0         0 carp( 'Source does not match first s///' );
105 0         0 return;
106             }
107 67 50       5289 unless ( $source =~ s{^ \s* \s* $ .* \z}{}xms ) {
108 0         0 carp( 'Source does not match second s///' );
109 0         0 return;
110             }
111              
112 67         366 my $struct = XMLin( $source );
113              
114             # Need this to work around a bit different format from auto-explain module
115 67 100       2958554 $struct = $struct->{ 'Query' } if defined $struct->{ 'Query' };
116              
117 67         727 my $top_node = $self->make_node_from( $struct->{ 'Plan' } );
118              
119 67 100       279 if ( $struct->{ 'Planning' } ) {
    100          
120 4         16 $self->explain->planning_time( $struct->{ 'Planning' }->{ 'Planning-Time' } );
121 4         18 my $buffers = Pg::Explain::Buffers->new( $self->normalize_node_struct( $struct->{ 'Planning' } ) );
122 4 100       13 $self->explain->planning_buffers( $buffers ) if $buffers;
123             }
124             elsif ( $struct->{ 'Planning-Time' } ) {
125 48         146 $self->explain->planning_time( $struct->{ 'Planning-Time' } );
126             }
127 67 100       321 $self->explain->execution_time( $struct->{ 'Execution-Time' } ) if $struct->{ 'Execution-Time' };
128 67 100       197 $self->explain->total_runtime( $struct->{ 'Total-Runtime' } ) if $struct->{ 'Total-Runtime' };
129 67 100       186 if ( $struct->{ 'Triggers' } ) {
130 59         95 for my $t ( @{ $struct->{ 'Triggers' }->{ 'Trigger' } } ) {
  59         228  
131 2         3 my $ts = {};
132 2 50       6 $ts->{ 'calls' } = $t->{ 'Calls' } if defined $t->{ 'Calls' };
133 2 50       7 $ts->{ 'time' } = $t->{ 'Time' } if defined $t->{ 'Time' };
134 2 50       7 $ts->{ 'relation' } = $t->{ 'Relation' } if defined $t->{ 'Relation' };
135 2 50       5 $ts->{ 'name' } = $t->{ 'Trigger-Name' } if defined $t->{ 'Trigger-Name' };
136 2         5 $self->explain->add_trigger_time( $ts );
137             }
138             }
139 67 100       269 $self->explain->jit( Pg::Explain::JIT->new( 'struct' => $struct->{ 'JIT' } ) ) if $struct->{ 'JIT' };
140              
141 67 100       190 $self->explain->query( $struct->{ 'Query-Text' } ) if $struct->{ 'Query-Text' };
142              
143 67 100 100     229 $self->explain->settings( $struct->{ 'Settings' } ) if ( $struct->{ 'Settings' } ) && ( 0 < scalar keys %{ $struct->{ 'Settings' } } );
  3         15  
144              
145 67         695 return $top_node;
146             }
147              
148             =head1 AUTHOR
149              
150             hubert depesz lubaczewski, C<< >>
151              
152             =head1 BUGS
153              
154             Please report any bugs or feature requests to C.
155              
156             =head1 SUPPORT
157              
158             You can find documentation for this module with the perldoc command.
159              
160             perldoc Pg::Explain
161              
162             =head1 COPYRIGHT & LICENSE
163              
164             Copyright 2008-2021 hubert depesz lubaczewski, all rights reserved.
165              
166             This program is free software; you can redistribute it and/or modify it
167             under the same terms as Perl itself.
168              
169             =cut
170              
171             1; # End of Pg::Explain::FromXML