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   14038 use v5.26;
  6         32  
5 6     6   38 use strict;
  6         17  
  6         228  
6 6     6   26 use warnings;
  6         18  
  6         574  
7 6     6   36 use warnings qw( FATAL utf8 );
  6         13  
  6         350  
8 6     6   41 use utf8;
  6         11  
  6         44  
9 6     6   306 use open qw( :std :utf8 );
  6         14  
  6         49  
10 6     6   1063 use Unicode::Normalize qw( NFC );
  6         12  
  6         516  
11 6     6   44 use Unicode::Collate;
  6         64  
  6         263  
12 6     6   39 use Encode qw( decode );
  6         11  
  6         1127  
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   44 use autodie;
  6         17  
  6         73  
24 6     6   44790 use Carp qw( carp croak confess cluck );
  6         50  
  6         2265  
25 6     6   49 use English qw( -no_match_vars );
  6         15  
  6         79  
26 6     6   3052 use Data::Dumper qw( Dumper );
  6         16  
  6         1844  
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   50 use parent qw( Pg::SQL::PrettyPrinter::Node );
  6         13  
  6         63  
44 6     6   585 use List::Util qw( any );
  6         12  
  6         2608  
45              
46             sub new {
47 84     84 1 2619 my $class = shift;
48 84         325 my $self = $class->SUPER::new( @_ );
49 84         163 bless $self, $class;
50              
51 84         224 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         2075  
52 84         217 my %type_is_ok = map { $_ => 1 } @types_ok;
  1176         2223  
53 84 50       431 if ( !$type_is_ok{ $self->{ 'kind' } } ) {
54 0         0 croak( "Unsupported A_Expr kind: " . $self->{ 'kind' } );
55             }
56              
57 84         386 $self->objectify( 'name', 'rexpr', 'lexpr' );
58 6     6   44 use Data::Dumper;
  6         14  
  6         16446  
59 84         589 return $self;
60             }
61              
62             sub operator {
63 198     198 1 287 my $self = shift;
64 198 100       289 if ( 1 == scalar @{ $self->{ 'name' } } ) {
  198         503  
65 196         488 return $self->{ 'name' }->[ 0 ]->string;
66             }
67 2         4 my @elements = map { $_->as_ident() } @{ $self->{ 'name' } };
  4         9  
  2         3  
68 2         6 $elements[ -1 ] = $self->{ 'name' }->[ -1 ]->string;
69 2         7 return sprintf( "OPERATOR( %s )", join( '.', @elements ) );
70             }
71              
72             sub as_text {
73 96     96 1 176 my $self = shift;
74              
75 96 100       972 if ( $self->{ 'kind' } eq 'AEXPR_IN' ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
76 2 50       5 my $op = $self->operator eq '<>' ? 'NOT IN' : 'IN';
77             return sprintf(
78             '%s %s %s',
79             $self->{ 'lexpr' }->as_text,
80             $op,
81 2         7 $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         4 $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         5 $self->{ 'rexpr' }->as_text
98             );
99             }
100 359     359   1151 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         14 $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         8 $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         10 $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         5 $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         4 $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         3 my $right_side;
148 1 50 33     24 if ( ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::FuncCall' ) )
      33        
149             && ( $self->{ 'rexpr' }->func_name eq 'pg_catalog.similar_to_escape' )
150 1         6 && ( 1 == scalar @{ $self->{ 'rexpr' }->{ 'args' } } ) )
151             {
152 1         7 $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         8 $self->{ 'lexpr' }->as_text,
160             $right_side,
161             );
162             }
163              
164 79         344 my @parts = ();
165 79 100       220 if ( exists $self->{ 'lexpr' } ) {
166 78         167 my $this_expr = '';
167 78         126 my $add_parens = 0;
168 78 100       584 if ( $self->{ 'lexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
169 7 100       11 $add_parens = 1 if $self->{ 'lexpr' }->operator ne $self->operator;
170             }
171 78 100       182 $this_expr .= '( ' if $add_parens;
172 78         286 $this_expr .= $self->{ 'lexpr' }->as_text;
173 78 100       212 $this_expr .= ' )' if $add_parens;
174 78         199 push @parts, $this_expr;
175             }
176              
177 79         208 push @parts, $self->operator;
178              
179 79 50       201 if ( exists $self->{ 'rexpr' } ) {
180 79         135 my $this_expr = '';
181 79         110 my $add_parens = 0;
182 79 100       403 if ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
183 2 50       7 $add_parens = 1 if $self->{ 'rexpr' }->operator ne $self->operator;
184             }
185 79 100       302 $this_expr .= '( ' if $add_parens;
186 79         221 $this_expr .= $self->{ 'rexpr' }->as_text;
187 79 100       196 $this_expr .= ' )' if $add_parens;
188 79         162 push @parts, $this_expr;
189             }
190              
191 79         309 return join( ' ', @parts );
192             }
193              
194             sub pretty_print {
195 80     80 1 145 my $self = shift;
196 80 100       720 if ( $self->{ 'kind' } eq 'AEXPR_IN' ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
197 2 50       5 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         5 $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         5 $self->{ 'rexpr' }->pretty_print
219             );
220             }
221 295     295   926 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         13 $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         6 $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         6 $self->{ 'rexpr' }->pretty_print,
242             );
243             }
244             elsif ( $self->{ 'kind' } eq 'AEXPR_LIKE' ) {
245 1 50       6 my $op = $self->operator eq '!~~' ? 'NOT LIKE' : 'LIKE';
246             return sprintf(
247             '%s %s %s',
248             $self->{ 'lexpr' }->pretty_print,
249             $op,
250 1         8 $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         8 $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         9 $self->{ 'rexpr' }->pretty_print,
265             );
266             }
267             elsif ( $self->{ 'kind' } eq 'AEXPR_SIMILAR' ) {
268 1         3 my $right_side;
269 1 50 33     21 if ( ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::FuncCall' ) )
      33        
270             && ( $self->{ 'rexpr' }->func_name eq 'pg_catalog.similar_to_escape' )
271 1         7 && ( 1 == scalar @{ $self->{ 'rexpr' }->{ 'args' } } ) )
272             {
273 1         7 $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         8 $self->{ 'lexpr' }->pretty_print,
281             $right_side,
282             );
283             }
284              
285 63         232 my @parts = ();
286 63 100       168 if ( exists $self->{ 'lexpr' } ) {
287 62         118 my $this_expr = '';
288 62         105 my $add_parens = 0;
289 62 100       312 if ( $self->{ 'lexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
290 7 100       13 $add_parens = 1 if $self->{ 'lexpr' }->operator ne $self->operator;
291             }
292 62 100       143 $this_expr .= '( ' if $add_parens;
293 62         239 $this_expr .= $self->{ 'lexpr' }->pretty_print;
294 62 100       177 $this_expr .= ' )' if $add_parens;
295 62         135 push @parts, $this_expr;
296             }
297              
298 63         165 push @parts, $self->operator;
299              
300 63 50       168 if ( exists $self->{ 'rexpr' } ) {
301 63         108 my $this_expr = '';
302 63         98 my $add_parens = 0;
303 63 100       314 if ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
304 2 50       6 $add_parens = 1 if $self->{ 'rexpr' }->operator ne $self->operator;
305             }
306 63 100       171 $this_expr .= '( ' if $add_parens;
307 63         214 $this_expr .= $self->{ 'rexpr' }->pretty_print;
308 63 100       149 $this_expr .= ' )' if $add_parens;
309 63         139 push @parts, $this_expr;
310             }
311              
312 63         280 return join( ' ', @parts );
313             }
314              
315             1;