File Coverage

blib/lib/Pg/SQL/PrettyPrinter/Node/A_Expr.pm
Criterion Covered Total %
statement 146 149 97.9
branch 85 96 88.5
condition 4 12 33.3
subroutine 22 22 100.0
pod 4 4 100.0
total 261 283 92.2


line stmt bran cond sub pod time code
1             package Pg::SQL::PrettyPrinter::Node::A_Expr;
2              
3             # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/
4 6     6   5721 use v5.26;
  6         19  
5 6     6   25 use strict;
  6         10  
  6         128  
6 6     6   19 use warnings;
  6         8  
  6         307  
7 6     6   22 use warnings qw( FATAL utf8 );
  6         9  
  6         238  
8 6     6   22 use utf8;
  6         7  
  6         35  
9 6     6   209 use open qw( :std :utf8 );
  6         9  
  6         30  
10 6     6   710 use Unicode::Normalize qw( NFC );
  6         15  
  6         286  
11 6     6   26 use Unicode::Collate;
  6         27  
  6         221  
12 6     6   26 use Encode qw( decode );
  6         13  
  6         793  
13              
14             if ( grep /\P{ASCII}/ => @ARGV ) {
15             @ARGV = map { decode( 'UTF-8', $_ ) } @ARGV;
16             }
17              
18             # If there is __DATA__,then uncomment next line:
19             # binmode( DATA, ':encoding(UTF-8)' );
20             # UTF8 boilerplace, per http://stackoverflow.com/questions/6162484/why-does-modern-perl-avoid-utf-8-by-default/
21              
22             # Useful common code
23 6     6   33 use autodie;
  6         8  
  6         71  
24 6     6   27135 use Carp qw( carp croak confess cluck );
  6         27  
  6         534  
25 6     6   36 use English qw( -no_match_vars );
  6         46  
  6         42  
26 6     6   2340 use Data::Dumper qw( Dumper );
  6         12  
  6         1409  
27              
28             # give a full stack dump on any untrapped exceptions
29             local $SIG{ __DIE__ } = sub {
30             confess "Uncaught exception: @_" unless $^S;
31             };
32              
33             # now promote run-time warnings into stackdumped exceptions
34             # *unless* we're in an try block, in which
35             # case just generate a clucking stackdump instead
36             local $SIG{ __WARN__ } = sub {
37             if ( $^S ) { cluck "Trapped warning: @_" }
38             else { confess "Deadly warning: @_" }
39             };
40              
41             # Useful common code
42              
43 6     6   41 use parent qw( Pg::SQL::PrettyPrinter::Node );
  6         10  
  6         44  
44 6     6   478 use List::Util qw( any );
  6         11  
  6         1422  
45              
46             sub new {
47 84     84 1 1851 my $class = shift;
48 84         231 my $self = $class->SUPER::new( @_ );
49 84         129 bless $self, $class;
50              
51 84         158 my @types_ok = map { 'AEXPR_' . $_ } qw( BETWEEN IN OP OP_ALL OP_ANY NOT_BETWEEN BETWEEN_SYM NOT_BETWEEN_SYM DISTINCT NOT_DISTINCT LIKE ILIKE NULLIF SIMILAR );
  1176         1653  
52 84         142 my %type_is_ok = map { $_ => 1 } @types_ok;
  1176         1728  
53 84 50       300 if ( !$type_is_ok{ $self->{ 'kind' } } ) {
54 0         0 croak( "Unsupported A_Expr kind: " . $self->{ 'kind' } );
55             }
56              
57 84         267 $self->objectify( 'name', 'rexpr', 'lexpr' );
58 6     6   38 use Data::Dumper;
  6         13  
  6         11567  
59 84         464 return $self;
60             }
61              
62             sub operator {
63 198     198 1 236 my $self = shift;
64 198 100       216 if ( 1 == scalar @{ $self->{ 'name' } } ) {
  198         347  
65 196         398 return $self->{ 'name' }->[ 0 ]->string;
66             }
67 2         4 my @elements = map { $_->as_ident() } @{ $self->{ 'name' } };
  4         10  
  2         4  
68 2         6 $elements[ -1 ] = $self->{ 'name' }->[ -1 ]->string;
69 2         6 return sprintf( "OPERATOR( %s )", join( '.', @elements ) );
70             }
71              
72             sub as_text {
73 96     96 1 127 my $self = shift;
74              
75 96 100       643 if ( $self->{ 'kind' } eq 'AEXPR_IN' ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
76 2 50       6 my $op = $self->operator eq '<>' ? 'NOT IN' : 'IN';
77             return sprintf(
78             '%s %s %s',
79             $self->{ 'lexpr' }->as_text,
80             $op,
81 2         5 $self->{ 'rexpr' }->as_text
82             );
83             }
84             elsif ( $self->{ 'kind' } eq 'AEXPR_OP_ANY' ) {
85             return sprintf(
86             '%s %s ANY( %s )',
87             $self->{ 'lexpr' }->as_text,
88             $self->operator,
89 1         5 $self->{ 'rexpr' }->as_text
90             );
91             }
92             elsif ( $self->{ 'kind' } eq 'AEXPR_OP_ALL' ) {
93             return sprintf(
94             '%s %s ALL( %s )',
95             $self->{ 'lexpr' }->as_text,
96             $self->operator,
97 1         6 $self->{ 'rexpr' }->as_text
98             );
99             }
100 359     359   792 elsif ( any { $_ eq $self->{ 'kind' } } qw( AEXPR_BETWEEN AEXPR_NOT_BETWEEN AEXPR_BETWEEN_SYM AEXPR_NOT_BETWEEN_SYM ) ) {
101             return sprintf(
102             '%s %s %s AND %s',
103             $self->{ 'lexpr' }->as_text,
104             $self->operator,
105             $self->{ 'rexpr' }->{ 'items' }->[ 0 ]->as_text,
106 5         11 $self->{ 'rexpr' }->{ 'items' }->[ 1 ]->as_text,
107             );
108             }
109             elsif ( $self->{ 'kind' } eq 'AEXPR_DISTINCT' ) {
110             return sprintf(
111             '%s IS DISTINCT FROM %s',
112             $self->{ 'lexpr' }->as_text,
113 2         5 $self->{ 'rexpr' }->as_text,
114             );
115             }
116             elsif ( $self->{ 'kind' } eq 'AEXPR_NOT_DISTINCT' ) {
117             return sprintf(
118             '%s IS NOT DISTINCT FROM %s',
119             $self->{ 'lexpr' }->as_text,
120 2         5 $self->{ 'rexpr' }->as_text,
121             );
122             }
123             elsif ( $self->{ 'kind' } eq 'AEXPR_LIKE' ) {
124 1 50       4 my $op = $self->operator eq '!~~' ? 'NOT LIKE' : 'LIKE';
125             return sprintf(
126             '%s %s %s',
127             $self->{ 'lexpr' }->as_text,
128             $op,
129 1         4 $self->{ 'rexpr' }->as_text,
130             );
131             }
132             elsif ( $self->{ 'kind' } eq 'AEXPR_NULLIF' ) {
133             return sprintf(
134             'NULLIF( %s, %s )',
135             $self->{ 'lexpr' }->as_text,
136 1         2 $self->{ 'rexpr' }->as_text,
137             );
138             }
139             elsif ( $self->{ 'kind' } eq 'AEXPR_ILIKE' ) {
140             return sprintf(
141             '%s ILIKE %s',
142             $self->{ 'lexpr' }->as_text,
143 1         4 $self->{ 'rexpr' }->as_text,
144             );
145             }
146             elsif ( $self->{ 'kind' } eq 'AEXPR_SIMILAR' ) {
147 1         1 my $right_side;
148 1 50 33     15 if ( ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::FuncCall' ) )
      33        
149             && ( $self->{ 'rexpr' }->func_name eq 'pg_catalog.similar_to_escape' )
150 1         4 && ( 1 == scalar @{ $self->{ 'rexpr' }->{ 'args' } } ) )
151             {
152 1         3 $right_side = $self->{ 'rexpr' }->{ 'args' }->[ 0 ]->as_text;
153             }
154             else {
155 0         0 $right_side = $self->{ 'rexpr' }->as_text;
156             }
157             return sprintf(
158             '%s SIMILAR TO %s',
159 1         4 $self->{ 'lexpr' }->as_text,
160             $right_side,
161             );
162             }
163              
164 79         193 my @parts = ();
165 79 100       184 if ( exists $self->{ 'lexpr' } ) {
166 78         104 my $this_expr = '';
167 78         88 my $add_parens = 0;
168 78 100       359 if ( $self->{ 'lexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
169 7 100       10 $add_parens = 1 if $self->{ 'lexpr' }->operator ne $self->operator;
170             }
171 78 100       124 $this_expr .= '( ' if $add_parens;
172 78         178 $this_expr .= $self->{ 'lexpr' }->as_text;
173 78 100       151 $this_expr .= ' )' if $add_parens;
174 78         120 push @parts, $this_expr;
175             }
176              
177 79         134 push @parts, $self->operator;
178              
179 79 50       147 if ( exists $self->{ 'rexpr' } ) {
180 79         198 my $this_expr = '';
181 79         92 my $add_parens = 0;
182 79 100       237 if ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
183 2 50       6 $add_parens = 1 if $self->{ 'rexpr' }->operator ne $self->operator;
184             }
185 79 100       129 $this_expr .= '( ' if $add_parens;
186 79         187 $this_expr .= $self->{ 'rexpr' }->as_text;
187 79 100       136 $this_expr .= ' )' if $add_parens;
188 79         151 push @parts, $this_expr;
189             }
190              
191 79         201 return join( ' ', @parts );
192             }
193              
194             sub pretty_print {
195 80     80 1 108 my $self = shift;
196 80 100       575 if ( $self->{ 'kind' } eq 'AEXPR_IN' ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
197 2 50       6 my $op = $self->operator eq '<>' ? 'NOT IN' : 'IN';
198             return sprintf(
199             '%s %s %s',
200             $self->{ 'lexpr' }->pretty_print,
201             $op,
202 2         7 $self->{ 'rexpr' }->pretty_print
203             );
204             }
205             elsif ( $self->{ 'kind' } eq 'AEXPR_OP_ANY' ) {
206             return sprintf(
207             '%s %s ANY( %s )',
208             $self->{ 'lexpr' }->pretty_print,
209             $self->operator,
210 1         3 $self->{ 'rexpr' }->pretty_print
211             );
212             }
213             elsif ( $self->{ 'kind' } eq 'AEXPR_OP_ALL' ) {
214             return sprintf(
215             '%s %s ALL( %s )',
216             $self->{ 'lexpr' }->pretty_print,
217             $self->operator,
218 1         3 $self->{ 'rexpr' }->pretty_print
219             );
220             }
221 295     295   626 elsif ( any { $_ eq $self->{ 'kind' } } qw( AEXPR_BETWEEN AEXPR_NOT_BETWEEN AEXPR_BETWEEN_SYM AEXPR_NOT_BETWEEN_SYM ) ) {
222             return sprintf(
223             '%s %s %s AND %s',
224             $self->{ 'lexpr' }->pretty_print,
225             $self->operator,
226             $self->{ 'rexpr' }->{ 'items' }->[ 0 ]->pretty_print,
227 5         15 $self->{ 'rexpr' }->{ 'items' }->[ 1 ]->pretty_print,
228             );
229             }
230             elsif ( $self->{ 'kind' } eq 'AEXPR_DISTINCT' ) {
231             return sprintf(
232             '%s IS DISTINCT FROM %s',
233             $self->{ 'lexpr' }->pretty_print,
234 2         5 $self->{ 'rexpr' }->pretty_print,
235             );
236             }
237             elsif ( $self->{ 'kind' } eq 'AEXPR_NOT_DISTINCT' ) {
238             return sprintf(
239             '%s IS NOT DISTINCT FROM %s',
240             $self->{ 'lexpr' }->pretty_print,
241 2         5 $self->{ 'rexpr' }->pretty_print,
242             );
243             }
244             elsif ( $self->{ 'kind' } eq 'AEXPR_LIKE' ) {
245 1 50       3 my $op = $self->operator eq '!~~' ? 'NOT LIKE' : 'LIKE';
246             return sprintf(
247             '%s %s %s',
248             $self->{ 'lexpr' }->pretty_print,
249             $op,
250 1         3 $self->{ 'rexpr' }->pretty_print,
251             );
252             }
253             elsif ( $self->{ 'kind' } eq 'AEXPR_NULLIF' ) {
254             return sprintf(
255             'NULLIF( %s, %s )',
256             $self->{ 'lexpr' }->pretty_print,
257 1         6 $self->{ 'rexpr' }->pretty_print,
258             );
259             }
260             elsif ( $self->{ 'kind' } eq 'AEXPR_ILIKE' ) {
261             return sprintf(
262             '%s ILIKE %s',
263             $self->{ 'lexpr' }->pretty_print,
264 1         4 $self->{ 'rexpr' }->pretty_print,
265             );
266             }
267             elsif ( $self->{ 'kind' } eq 'AEXPR_SIMILAR' ) {
268 1         3 my $right_side;
269 1 50 33     9 if ( ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::FuncCall' ) )
      33        
270             && ( $self->{ 'rexpr' }->func_name eq 'pg_catalog.similar_to_escape' )
271 1         4 && ( 1 == scalar @{ $self->{ 'rexpr' }->{ 'args' } } ) )
272             {
273 1         4 $right_side = $self->{ 'rexpr' }->{ 'args' }->[ 0 ]->pretty_print;
274             }
275             else {
276 0         0 $right_side = $self->{ 'rexpr' }->pretty_print;
277             }
278             return sprintf(
279             '%s SIMILAR TO %s',
280 1         4 $self->{ 'lexpr' }->pretty_print,
281             $right_side,
282             );
283             }
284              
285 63         158 my @parts = ();
286 63 100       121 if ( exists $self->{ 'lexpr' } ) {
287 62         81 my $this_expr = '';
288 62         79 my $add_parens = 0;
289 62 100       212 if ( $self->{ 'lexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
290 7 100       12 $add_parens = 1 if $self->{ 'lexpr' }->operator ne $self->operator;
291             }
292 62 100       103 $this_expr .= '( ' if $add_parens;
293 62         154 $this_expr .= $self->{ 'lexpr' }->pretty_print;
294 62 100       117 $this_expr .= ' )' if $add_parens;
295 62         108 push @parts, $this_expr;
296             }
297              
298 63         106 push @parts, $self->operator;
299              
300 63 50       119 if ( exists $self->{ 'rexpr' } ) {
301 63         79 my $this_expr = '';
302 63         78 my $add_parens = 0;
303 63 100       190 if ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
304 2 50       4 $add_parens = 1 if $self->{ 'rexpr' }->operator ne $self->operator;
305             }
306 63 100       109 $this_expr .= '( ' if $add_parens;
307 63         147 $this_expr .= $self->{ 'rexpr' }->pretty_print;
308 63 100       107 $this_expr .= ' )' if $add_parens;
309 63         99 push @parts, $this_expr;
310             }
311              
312 63         175 return join( ' ', @parts );
313             }
314              
315             1;