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 5     5   3598 use v5.26;
  5         20  
5 5     5   28 use strict;
  5         21  
  5         121  
6 5     5   38 use warnings;
  5         13  
  5         148  
7 5     5   25 use warnings qw( FATAL utf8 );
  5         10  
  5         203  
8 5     5   38 use utf8;
  5         9  
  5         53  
9 5     5   131 use open qw( :std :utf8 );
  5         28  
  5         46  
10 5     5   1259 use Unicode::Normalize qw( NFC );
  5         20  
  5         546  
11 5     5   56 use Unicode::Collate;
  5         34  
  5         240  
12 5     5   35 use Encode qw( decode );
  5         17  
  5         497  
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 5     5   1466 use autodie;
  5         13  
  5         50  
24 5     5   30003 use Carp qw( carp croak confess cluck );
  5         12  
  5         448  
25 5     5   44 use English qw( -no_match_vars );
  5         17  
  5         37  
26 5     5   1954 use Data::Dumper qw( Dumper );
  5         18  
  5         1154  
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 5     5   47 use parent qw( Pg::SQL::PrettyPrinter::Node );
  5         11  
  5         33  
44 5     5   388 use List::Util qw( any );
  5         19  
  5         1312  
45              
46             sub new {
47 76     76 1 2452 my $class = shift;
48 76         343 my $self = $class->SUPER::new( @_ );
49 76         149 bless $self, $class;
50              
51 76         169 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 );
  1064         2029  
52 76         149 my %type_is_ok = map { $_ => 1 } @types_ok;
  1064         2030  
53 76 50       312 if ( !$type_is_ok{ $self->{ 'kind' } } ) {
54 0         0 croak( "Unsupported A_Expr kind: " . $self->{ 'kind' } );
55             }
56              
57 76         430 $self->objectify( 'name', 'rexpr', 'lexpr' );
58 5     5   42 use Data::Dumper;
  5         16  
  5         10683  
59 76         441 return $self;
60             }
61              
62             sub operator {
63 173     173 1 280 my $self = shift;
64 173 100       269 if ( 1 == scalar @{ $self->{ 'name' } } ) {
  173         402  
65 171         521 return $self->{ 'name' }->[ 0 ]->{ 'str' };
66             }
67 2         4 my @elements = map { $_->as_ident() } @{ $self->{ 'name' } };
  4         8  
  2         8  
68 2         12 $elements[ -1 ] = $self->{ 'name' }->[ -1 ]->{ 'str' };
69 2         10 return sprintf( "OPERATOR( %s )", join( '.', @elements ) );
70             }
71              
72             sub as_text {
73 79     79 1 160 my $self = shift;
74              
75 79 100       571 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         13 $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         18 $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         23 $self->{ 'rexpr' }->as_text
98             );
99             }
100 291     291   778 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         16 $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         12 $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         8 $self->{ 'rexpr' }->as_text,
121             );
122             }
123             elsif ( $self->{ 'kind' } eq 'AEXPR_LIKE' ) {
124 1 50       5 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         5 $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         6 $self->{ 'rexpr' }->as_text,
144             );
145             }
146             elsif ( $self->{ 'kind' } eq 'AEXPR_SIMILAR' ) {
147 1         3 my $right_side;
148 1 50 33     12 if ( ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::FuncCall' ) )
      33        
149             && ( $self->{ 'rexpr' }->func_name eq 'pg_catalog.similar_to_escape' )
150 1         5 && ( 1 == scalar @{ $self->{ 'rexpr' }->{ 'args' } } ) )
151             {
152 1         5 $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         6 $self->{ 'lexpr' }->as_text,
160             $right_side,
161             );
162             }
163              
164 62         217 my @parts = ();
165 62 100       155 if ( exists $self->{ 'lexpr' } ) {
166 61         106 my $this_expr = '';
167 61         94 my $add_parens = 0;
168 61 100       335 if ( $self->{ 'lexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
169 7 100       19 $add_parens = 1 if $self->{ 'lexpr' }->operator ne $self->operator;
170             }
171 61 100       133 $this_expr .= '( ' if $add_parens;
172 61         194 $this_expr .= $self->{ 'lexpr' }->as_text;
173 61 100       169 $this_expr .= ' )' if $add_parens;
174 61         138 push @parts, $this_expr;
175             }
176              
177 62         168 push @parts, $self->operator;
178              
179 62 50       167 if ( exists $self->{ 'rexpr' } ) {
180 62         116 my $this_expr = '';
181 62         94 my $add_parens = 0;
182 62 100       244 if ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
183 2 50       14 $add_parens = 1 if $self->{ 'rexpr' }->operator ne $self->operator;
184             }
185 62 100       124 $this_expr .= '( ' if $add_parens;
186 62         205 $this_expr .= $self->{ 'rexpr' }->as_text;
187 62 100       184 $this_expr .= ' )' if $add_parens;
188 62         120 push @parts, $this_expr;
189             }
190              
191 62         213 return join( ' ', @parts );
192             }
193              
194             sub pretty_print {
195 72     72 1 123 my $self = shift;
196 72 100       517 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         9 $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         8 $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         17 $self->{ 'rexpr' }->pretty_print
219             );
220             }
221 263     263   687 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         17 $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         10 $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         14 $self->{ 'rexpr' }->pretty_print,
242             );
243             }
244             elsif ( $self->{ 'kind' } eq 'AEXPR_LIKE' ) {
245 1 50       4 my $op = $self->operator eq '!~~' ? 'NOT LIKE' : 'LIKE';
246             return sprintf(
247             '%s %s %s',
248             $self->{ 'lexpr' }->pretty_print,
249             $op,
250 1         7 $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         16 $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         14 $self->{ 'rexpr' }->pretty_print,
265             );
266             }
267             elsif ( $self->{ 'kind' } eq 'AEXPR_SIMILAR' ) {
268 1         2 my $right_side;
269 1 50 33     10 if ( ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::FuncCall' ) )
      33        
270             && ( $self->{ 'rexpr' }->func_name eq 'pg_catalog.similar_to_escape' )
271 1         5 && ( 1 == scalar @{ $self->{ 'rexpr' }->{ 'args' } } ) )
272             {
273 1         5 $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         7 $self->{ 'lexpr' }->pretty_print,
281             $right_side,
282             );
283             }
284              
285 55         167 my @parts = ();
286 55 100       141 if ( exists $self->{ 'lexpr' } ) {
287 54         94 my $this_expr = '';
288 54         84 my $add_parens = 0;
289 54 100       229 if ( $self->{ 'lexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
290 7 100       19 $add_parens = 1 if $self->{ 'lexpr' }->operator ne $self->operator;
291             }
292 54 100       119 $this_expr .= '( ' if $add_parens;
293 54         189 $this_expr .= $self->{ 'lexpr' }->pretty_print;
294 54 100       153 $this_expr .= ' )' if $add_parens;
295 54         121 push @parts, $this_expr;
296             }
297              
298 55         127 push @parts, $self->operator;
299              
300 55 50       133 if ( exists $self->{ 'rexpr' } ) {
301 55         92 my $this_expr = '';
302 55         77 my $add_parens = 0;
303 55 100       201 if ( $self->{ 'rexpr' }->isa( 'Pg::SQL::PrettyPrinter::Node::A_Expr' ) ) {
304 2 50       5 $add_parens = 1 if $self->{ 'rexpr' }->operator ne $self->operator;
305             }
306 55 100       105 $this_expr .= '( ' if $add_parens;
307 55         137 $this_expr .= $self->{ 'rexpr' }->pretty_print;
308 55 100       152 $this_expr .= ' )' if $add_parens;
309 55         121 push @parts, $this_expr;
310             }
311              
312 55         205 return join( ' ', @parts );
313             }
314              
315             1;