File Coverage

blib/lib/Pg/Explain/FromJSON.pm
Criterion Covered Total %
statement 78 79 98.7
branch 31 36 86.1
condition 6 9 66.6
subroutine 15 15 100.0
pod 1 1 100.0
total 131 140 93.5


line stmt bran cond sub pod time code
1             package Pg::Explain::FromJSON;
2              
3             # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/
4 72     72   816 use v5.18;
  72         211  
5 72     72   348 use strict;
  72         132  
  72         1260  
6 72     72   279 use warnings;
  72         115  
  72         1833  
7 72     72   335 use warnings qw( FATAL utf8 );
  72         151  
  72         2238  
8 72     72   356 use utf8;
  72         136  
  72         376  
9 72     72   1934 use open qw( :std :utf8 );
  72         148  
  72         410  
10 72     72   9043 use Unicode::Normalize qw( NFC );
  72         149  
  72         3604  
11 72     72   443 use Unicode::Collate;
  72         144  
  72         2138  
12 72     72   373 use Encode qw( decode );
  72         148  
  72         3837  
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   14280 use base qw( Pg::Explain::From );
  72         135  
  72         9250  
21 72     72   30461 use JSON::MaybeXS;
  72         355934  
  72         3906  
22 72     72   484 use Carp;
  72         130  
  72         3153  
23 72     72   367 use Pg::Explain::JIT;
  72         134  
  72         1504  
24 72     72   367 use Pg::Explain::Buffers;
  72         126  
  72         43514  
25              
26             =head1 NAME
27              
28             Pg::Explain::FromJSON - Parser for explains in JSON 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 parse_source
45              
46             Function which parses actual plan, and constructs Pg::Explain::Node objects
47             which represent it.
48              
49             Returns Top node of query plan.
50              
51             =cut
52              
53             sub parse_source {
54 74     74 1 122 my $self = shift;
55 74         150 my $source = shift;
56              
57             # We need to remove things before and/or after explain
58             # To do this, first - split explain into lines...
59 74         3168 my @source_lines = split( /[\r\n]+/, $source );
60              
61 74 100       328 if ( 1 < scalar @source_lines ) {
62              
63             # If there are many lines, there could be line prefix...
64 73         142 my $prefix = undef;
65              
66             # Now, find first line of explain, and cache it's prefix (some spaces ...)
67 73         180 for my $l ( @source_lines ) {
68 3954 100       5942 next unless $l =~ m{\A (\s*) \[ \s* \z }x;
69 69         195 $prefix = $1;
70             }
71              
72 73 100       245 if ( defined $prefix ) {
73              
74             # Now, extract lines with explain using known prefix
75 69         182 my @use_lines = grep { /\A$prefix\[\s*\z/ ... /\A$prefix\]\s*\z/ } @source_lines;
  3872         8651  
76 69         907 $source = join( "\n", @use_lines );
77             }
78             }
79              
80             # And now parse the json...
81 74         3214 my $struct = decode_json( $source );
82 74 100 66     607 if ( ( 'ARRAY' eq ref $struct )
    50 33        
83             && ( defined $struct->[ 0 ]->{ 'Plan' } ) )
84             {
85             # This structure is used by normal "explain" command
86 70         198 $struct = $struct->[ 0 ];
87             }
88             elsif (( 'HASH' eq ref $struct )
89             && ( defined $struct->{ 'Plan' } ) )
90             {
91             # This structure is used by auto-explain command
92             # empty command block, so I can have simple else condition
93             }
94             else {
95 0         0 croak( 'Unknown JSON parsed' );
96             }
97              
98 74         383 my $top_node = $self->make_node_from( $struct->{ 'Plan' } );
99              
100 74 100       335 if ( $struct->{ 'Planning' } ) {
    100          
101 4         17 $self->explain->planning_time( $struct->{ 'Planning' }->{ 'Planning Time' } );
102 4         22 my $buffers = Pg::Explain::Buffers->new( $struct->{ 'Planning' } );
103 4 100       13 $self->explain->planning_buffers( $buffers ) if $buffers;
104             }
105             elsif ( $struct->{ 'Planning Time' } ) {
106 52         141 $self->explain->planning_time( $struct->{ 'Planning Time' } );
107             }
108 74 100       250 $self->explain->execution_time( $struct->{ 'Execution Time' } ) if $struct->{ 'Execution Time' };
109 74 100       247 $self->explain->total_runtime( $struct->{ 'Total Runtime' } ) if $struct->{ 'Total Runtime' };
110 74 100       242 if ( $struct->{ 'Triggers' } ) {
111 65         104 for my $t ( @{ $struct->{ 'Triggers' } } ) {
  65         189  
112 2         3 my $ts = {};
113 2 50       8 $ts->{ 'calls' } = $t->{ 'Calls' } if defined $t->{ 'Calls' };
114 2 50       7 $ts->{ 'time' } = $t->{ 'Time' } if defined $t->{ 'Time' };
115 2 50       7 $ts->{ 'relation' } = $t->{ 'Relation' } if defined $t->{ 'Relation' };
116 2 50       6 $ts->{ 'name' } = $t->{ 'Trigger Name' } if defined $t->{ 'Trigger Name' };
117 2         4 $self->explain->add_trigger_time( $ts );
118             }
119             }
120 74 100       211 $self->explain->jit( Pg::Explain::JIT->new( 'struct' => $struct->{ 'JIT' } ) ) if $struct->{ 'JIT' };
121              
122 74 100       183 $self->explain->query( $struct->{ 'Query Text' } ) if $struct->{ 'Query Text' };
123              
124 74 100 100     243 $self->explain->settings( $struct->{ 'Settings' } ) if ( $struct->{ 'Settings' } ) && ( 0 < scalar keys %{ $struct->{ 'Settings' } } );
  3         13  
125              
126 74         866 return $top_node;
127             }
128              
129             =head1 AUTHOR
130              
131             hubert depesz lubaczewski, C<< >>
132              
133             =head1 BUGS
134              
135             Please report any bugs or feature requests to C.
136              
137             =head1 SUPPORT
138              
139             You can find documentation for this module with the perldoc command.
140              
141             perldoc Pg::Explain
142              
143             =head1 COPYRIGHT & LICENSE
144              
145             Copyright 2008-2021 hubert depesz lubaczewski, all rights reserved.
146              
147             This program is free software; you can redistribute it and/or modify it
148             under the same terms as Perl itself.
149              
150             =cut
151              
152             1; # End of Pg::Explain::FromJSON