File Coverage

blib/lib/Pg/Explain/JIT.pm
Criterion Covered Total %
statement 111 111 100.0
branch 32 38 84.2
condition n/a
subroutine 19 19 100.0
pod 7 7 100.0
total 169 175 96.5


line stmt bran cond sub pod time code
1             package Pg::Explain::JIT;
2              
3             # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/
4 79     79   1261 use v5.18;
  79         350  
5 79     79   477 use strict;
  79         151  
  79         2172  
6 79     79   365 use warnings;
  79         144  
  79         5223  
7 79     79   454 use warnings qw( FATAL utf8 );
  79         150  
  79         4153  
8 79     79   480 use utf8;
  79         143  
  79         587  
9 79     79   3133 use open qw( :std :utf8 );
  79         148  
  79         580  
10 79     79   12638 use Unicode::Normalize qw( NFC );
  79         155  
  79         6008  
11 79     79   518 use Unicode::Collate;
  79         164  
  79         2965  
12 79     79   458 use Encode qw( decode );
  79         137  
  79         12564  
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 79     79   619 use Carp;
  79         205  
  79         161724  
21              
22             =head1 NAME
23              
24             Pg::Explain::JIT - Stores information about JIT from PostgreSQL's explain analyze.
25              
26             =head1 VERSION
27              
28             Version 2.9
29              
30             =cut
31              
32             our $VERSION = '2.9';
33              
34             =head1 SYNOPSIS
35              
36             This module provides wrapper around various information about JIT that can be parsed from plans returned by explain analyze in PostgreSQL.
37              
38             Object of this class is created by Pg::Explain when parsing plan, and is later available as $explain->jit.
39              
40             =head1 ACCESSORS
41              
42             =head2 functions( [val] )
43              
44             Returns/sets number of functions / operators that were JIT compiled.
45              
46             =head2 options( [val] )
47              
48             Returns/sets whole hashref of options that were used by JIT compiler.
49              
50             =head2 option( name, [val] )
51              
52             Returns/sets value of single option that was used by JIT compiler.
53              
54             =head2 timings( [val] )
55              
56             Returns/sets whole hashref of how long it took to process various stages of JIT compiling.
57              
58             =head2 timing( name, [val] )
59              
60             Returns/sets time of single stage of JIT compiling.
61              
62             =cut
63              
64 37 100   37 1 59 sub functions { my $self = shift; $self->{ 'functions' } = $_[ 0 ] if 0 < scalar @_; return $self->{ 'functions' }; }
  37         115  
  37         129  
65 152 50   152 1 248 sub options { my $self = shift; $self->{ 'options' } = $_[ 0 ] if 0 < scalar @_; return $self->{ 'options' }; }
  152         302  
  152         668  
66 148 100   148 1 212 sub timings { my $self = shift; $self->{ 'timings' } = $_[ 0 ] if 0 < scalar @_; return $self->{ 'timings' }; }
  148         284  
  148         1139  
67              
68 76 100   76 1 164 sub option { my $self = shift; my $name = shift; $self->options->{ $name } = $_[ 0 ] if 0 < scalar @_; return $self->options->{ $name }; }
  76         157  
  76         234  
  76         143  
69 45 50   45 1 69 sub timing { my $self = shift; my $name = shift; $self->timings->{ $name } = $_[ 0 ] if 0 < scalar @_; return $self->timings->{ $name }; }
  45         98  
  45         134  
  45         80  
70              
71             =head1 METHODS
72              
73             =head2 new
74              
75             Object constructor. Should get one of:
76              
77             =over
78              
79             =item * struct - hashref based on parsing of JSON/YAML/XML plans
80              
81             =item * lines - arrayref of strings containling lines describing JIT from text plans
82              
83             =back
84              
85             =cut
86              
87             sub new {
88 13     13 1 31 my $class = shift;
89 13         51 my %args = @_;
90 13         38 my $self = bless {}, $class;
91 13         42 $self->{ 'options' } = {};
92 13         35 $self->{ 'timings' } = {};
93 13 100       67 if ( $args{ 'struct' } ) {
94 4 50       18 croak "Pg::Explain::JIT constructor cannot be called with both struct and lines!" if $args{ 'lines' };
95 4         19 $self->_parse_struct( $args{ 'struct' } );
96             }
97             else {
98 9         36 $self->_parse_lines( $args{ 'lines' } );
99             }
100 13         98 return $self;
101             }
102              
103             =head2 as_text
104              
105             Returns text that represents the JIT info as in explain analyze output for 'text' format.
106              
107             =cut
108              
109             sub as_text {
110 6     6 1 9 my $self = shift;
111 6         14 my $output = "JIT:\n";
112 6 50       18 if ( $self->functions ) {
113 6         16 $output .= sprintf " Functions: %s\n", $self->functions;
114             }
115 6 50       13 if ( 0 < scalar keys %{ $self->options } ) {
  6         17  
116 6 100       12 my $str = join( ', ', map { "$_ " . ( $self->option( $_ ) ? "true" : "false" ) } keys %{ $self->options } );
  24         60  
  6         15  
117 6         39 $output .= sprintf " Options: %s\n", $str;
118             }
119              
120             # Shortcircuit as logic to build timings line will be slightly longer than options…
121 6 50       14 return $output if 0 == scalar keys %{ $self->timings };
  6         17  
122              
123 6         15 my @parts = ();
124 6         13 for my $key ( sort keys %{ $self->timings } ) {
  6         14  
125 30         62 my $val = $self->timings->{ $key };
126              
127             # Two potential cases, value is scalar, and it's simply time, and it's hash, in which case we have to build all subparts
128 30 100       73 if ( '' eq ref $val ) {
129 28         95 push @parts, "${key} ${val} ms";
130 28         50 next;
131             }
132              
133             # This is the more complex case:
134 2         4 my @subelements = map { sprintf "%s %s ms", $_, $val->{ $_ } } grep { $_ ne 'Total' } sort keys %{ $val };
  2         10  
  4         10  
  2         14  
135 2         9 push @parts, sprintf "%s %s ms (%s)", $key, $val->{ 'Total' }, join( ', ', @subelements );
136             }
137 6         28 $output .= sprintf " Timing: %s\n", join( ', ', @parts );
138              
139 6         28 return $output;
140             }
141              
142             =head1 INTERNAL METHODS
143              
144             =head2 _parse_struct
145              
146             Parses given struct, as returned from parsing JSON/YAML/XML formats.
147              
148             =cut
149              
150             sub _parse_struct {
151 4     4   18 my $self = shift;
152 4         9 my $struct = shift;
153 4         20 $self->functions( $struct->{ 'Functions' } );
154 4         9 while ( my ( $key, $val ) = each %{ $struct->{ 'Options' } } ) {
  20         71  
155 16 100       77 $val = undef if $val eq 'false';
156 16 100       214 $self->option( $key, $val ? 1 : 0 );
157             }
158 4         17 $self->timings( $struct->{ 'Timing' } );
159 4         10 return;
160             }
161              
162             =head2 _parse_lines
163              
164             Parses given lines, as parsed out of TEXT explain format.
165              
166             =cut
167              
168             sub _parse_lines {
169 9     9   18 my $self = shift;
170 9         14 my $lines = shift;
171 9         18 for my $line ( @{ $lines } ) {
  9         22  
172 36 100       246 if ( $line =~ m{ \A \s* Functions: \s+ (\d+) \s* \z }xms ) {
    100          
    100          
173 9         54 $self->functions( $1 );
174             }
175             elsif ( $line =~ m{ \A \s* Options: \s+ (\S.*\S) \s* \z }xms ) {
176 9         98 my @parts = split( /\s*,\s*/, $1 );
177 9         22 for my $e ( @parts ) {
178 36         249 $e =~ s/\s*(true|false)\z//;
179 36 100       137 $self->option( $e, $1 eq "true" ? 1 : 0 );
180             }
181             }
182             elsif ( $line =~ m{ \A \s* Timing: \s+ (\S.*\S) \s* \z }xms ) {
183 9         26 my $timings = $1;
184              
185             # Two types of timing information:
186             # 1: "Optimization 11.314 ms"
187             # 2: "Generation 0.327 ms (Deform 0.131 ms)"
188 9         89 while (
189             $timings =~ s{ \A
190             (? \S+ )
191             \s+
192             (? \d+\.\d+ )
193             \s+
194             ms
195             (?: \s+ \( (? [^\)]+ ) \) )?
196             (?: \z | , \s+ )
197             }{}x
198             )
199             {
200 45         421 my ( $t_name, $t_total, $t_subelements ) = ( $+{ 'name' }, $+{ 'total' }, $+{ 'subelements' } );
201 45 100       166 if ( !defined $t_subelements ) {
202              
203             # This is the simple format of timing information
204 42         114 $self->timing( $t_name, $t_total );
205 42         299 next;
206             }
207              
208             # In here, we have subelements, so extract them separately, and put the total timing into Total key
209 3         25 my @parts = $t_subelements =~ m{(\S+)\s+(\d+\.\d+)\s+ms(?:$|, )}g;
210 3         10 my $val = { @parts };
211 3         9 $val->{ "Total" } = $t_total;
212 3         10 $self->timing( $t_name, $val );
213             }
214             }
215             }
216             }
217              
218             =head1 AUTHOR
219              
220             hubert depesz lubaczewski, C<< >>
221              
222             =head1 BUGS
223              
224             Please report any bugs or feature requests to C.
225              
226             =head1 SUPPORT
227              
228             You can find documentation for this module with the perldoc command.
229              
230             perldoc Pg::Explain::JIT
231              
232             =head1 COPYRIGHT & LICENSE
233              
234             Copyright 2008-2023 hubert depesz lubaczewski, all rights reserved.
235              
236             This program is free software; you can redistribute it and/or modify it
237             under the same terms as Perl itself.
238              
239              
240             =cut
241              
242             1; # End of Pg::Explain::JIT